JM
Jasper Moelker 5 years ago (edited)
“Ice cream with GraphQL subscriptions”
“Get notified whenever ice cream is dispen
JM
Jasper Moelker 5 years ago
Can we link to a working demo?
JM
Jasper Moelker 5 years ago
todo: move to gist or repo and link
- Experimenting with GraphQL subscriptions
- Getting started
- GraphQL schema and resolvers
- Run the server
- GraphQL playground
- Real-time ice cream
- Event filtering
Getting started
npm install apollo-server-express express graphql
const express = require('express');
const { createServer } = require('http');
const { ApolloServer, gql } = require('apollo-server-express');
GraphQL schema and resolvers
const typeDefs = gql`
type IceCream {
id: Int!
flavour: String!
description: String!
}
type Query {
iceCream(flavour: String!): IceCream
iceCreams: [IceCream]
}
`
// The excellent flavour descriptions courtesy of https://phrasegenerator.com/wine, with modifications.
const stub = [
{
id: 0,
flavour: 'vanilla',
description: 'A flippant pepper bouquet and alcoholic garlic essences are blended in'
},{
id: 1,
flavour: 'strawberry',
description: 'Blends indigestible parsnip flavors with a sandy cool ranch flavor'
},{
id: 2,
flavour: 'pear',
description: 'A soporiphic coconut finish and enticing Bar-B-Q midtones are intertwined'
}
]
const resolvers = {
Query: {
iceCreams: () => {
return Promise.resolve(stub)
},
iceCream: (_, { flavour }) => {
return Promise.resolve(stub.find(({ flavour:stubFlavour }) => flavour === stubFlavour))
}
}
};
Run the server
const server = new ApolloServer({
typeDefs,
resolvers
})
const app = express()
server.applyMiddleware({ app })
const httpServer = createServer(app)
const port = 3000
httpServer.listen(port, () => {
console.log(`Server ready at http://localhost:${port}${server.graphqlPath}`)
})
GraphQL playground
query {
iceCream(flavour: "vanilla") {
flavour
description
}
}
{
"data": {
"iceCream": {
"description": "A flippant pepper bouquet and alcoholic garlic essences are blended in",
"flavour": "vanilla"
}
}
}
query {
iceCreams {
flavour
description
}
}
Real-time ice cream
npm install graphql-subscriptions
const express = require('express');
const { createServer } = require('http');
const { ApolloServer, gql, withFilter } = require('apollo-server-express');
const { PubSub } = require('graphql-subscriptions');
const pubsub = new PubSub();
const typeDefs = gql`
type IceCream {
id: Int!
flavour: String!
description: String!
owner: String!
}
type Query {
iceCream(flavour: String!): IceCream
iceCreams: [IceCream]
}
type Subscription {
iceCreamDispensed(flavour: String): IceCream
}
`
// Event name to listen to
const ICE_CREAM_DISPENSED = 'ICE_CREAM_DISPENSED';
const resolvers = {
Subscription: {
iceCreamDispensed: {
subscribe: () => pubsub.asyncIterator([ ICE_CREAM_DISPENSED ]),
}
},
// ... remaining part of resolvers stays the same
Query: {
server.applyMiddleware({ app })
const httpServer = createServer(app)
server.installSubscriptionHandlers(httpServer)
const port = 3000
httpServer.listen(port, () => {
console.log(`Server ready at http://localhost:${port}${server.graphqlPath}`)
console.log(`Subscriptions ready at ws://localhost:${port}${server.subscriptionsPath}`)
})
subscription {
iceCreamDispensed {
flavour
description
}
}
const app = express()
app.post('/dispatch', (req, res) => {
const flavours = ['vanilla','pear','strawberry']
const flavour = flavours[Math.round(Math.random() * (flavours.length - 1))]
const ice = stub.find(item => item.flavour === flavour)
pubsub.publish(ICE_CREAM_DISPENSED, { iceCreamDispensed: ice } )
return res.status(202).send('accepted')
})
server.applyMiddleware({ app })
curl -X POST http://localhost:3000/dispatch
{
"data": {
"iceCreamDispensed": {
"flavour": "strawberry",
"description": "Blends indigestible parsnip flavors with a sandy cool ranch flavor"
}
}
}
Event filtering
const express = require('express');
const { createServer } = require('http');
const { ApolloServer, gql, withFilter } = require('apollo-server-express');
const { PubSub } = require('graphql-subscriptions');
const resolvers = {
Subscription: {
iceCreamDispensed: {
subscribe: withFilter(
() => pubsub.asyncIterator([ ICE_CREAM_DISPENSED ]),
({ iceCreamDispensed: { flavour:payloadFlavour } }, { flavour:requestFlavour }) => {
if (!requestFlavour) {
return true
}
return payloadFlavour === requestFlavour
}
)
},
},
Query: {
// ...same as before
subscription {
iceCreamDispensed (flavour: "vanilla") {
flavour
description
}
}
const express = require('express');
const { createServer } = require('http');
const { ApolloServer, gql, withFilter } = require('apollo-server-express');
const { PubSub } = require('graphql-subscriptions');
const pubsub = new PubSub();
const typeDefs = gql`
type IceCream {
id: Int!
flavour: String!
description: String!
}
type Query {
iceCream(flavour: String!): IceCream
iceCreams: [IceCream]
}
type Subscription {
iceCreamDispensed(flavour: String): IceCream
}
`
const stub = [
{
id: 0,
flavour: 'vanilla',
description: 'A flippant pepper bouquet and alcoholic garlic essences are blended in'
},{
id: 1,
flavour: 'strawberry',
description: 'Blends indigestible parsnip flavors with a sandy cool ranch flavor'
},{
id: 2,
flavour: 'pear',
description: 'A soporiphic coconut finish and enticing Bar-B-Q midtones are intertwined'
}
]
const ICE_CREAM_DISPENSED = 'ICE_CREAM_DISPENSED';
const resolvers = {
Subscription: {
iceCreamDispensed: {
subscribe:
withFilter(
() => pubsub.asyncIterator([ ICE_CREAM_DISPENSED ]),
({ iceCreamDispensed: { flavour:payloadFlavour } }, { flavour:requestFlavour }) => {
if (!requestFlavour) {
return true
}
return payloadFlavour === requestFlavour
}
)
}
},
Query: {
iceCreams: () => {
return Promise.resolve(stub)
},
iceCream: (_, { flavour }) => {
return Promise.resolve(stub.find(({ flavour:stubFlavour }) => flavour === stubFlavour))
}
}
};
const server = new ApolloServer({
typeDefs,
resolvers
})
const app = express()
app.post('/dispatch', (req, res) => {
const flavours = ['vanilla','pear','strawberry']
const flavour = flavours[Math.round(Math.random() * (flavours.length - 1))]
const ice = stub.find(item => item.flavour === flavour)
pubsub.publish(ICE_CREAM_DISPENSED, { iceCreamDispensed: ice } )
return res.status(202).send('accepted')
})
server.applyMiddleware({ app })
const httpServer = createServer(app)
server.installSubscriptionHandlers(httpServer)
const port = 3000
httpServer.listen(port, () => {
console.log(`Server ready at http://localhost:${port}${server.graphqlPath}`)
console.log(`Subscriptions ready at ws://localhost:${port}${server.subscriptionsPath}`)
})