内容简介:This enables us to add a subscription to our channel post and start listening for any event published in the channel.
Ever wonder how Facebook notifies you when a friend posts something? Or how Google Maps updates your location in real time? The answer to these and many other modern mysteries is (among other things) GraphQL subscriptions.
In this article, we’ll provide a basic understanding of GraphQL subscriptions for events on JSON data in a Node.js server.
Before we get started, you should have:
-
Node.js installed
-
A basic understanding of GraphQL concepts such as schema, query, mutation, and resolve
Without further ado, let’s dive in.
What are GraphQL subscriptions?
GraphQL subscriptions enable you to subscribe to events under a source stream and receive notifications in real-time via a response stream when a selected event executes. Once a GraphQL subscription is executed, a persistent function is created on the server that maps an underlying source stream to a returned response stream.
GraphQL subscriptions differ from queries in the way the data is delivered to the client. The latter immediately returns a single response, while the former returns a result every time data is published on a topic to which you have subscribed.
This is facilitated by a publisher/subscriber mechanism that can handle event-driven systems efficiently and at a scale. In a publisher/subscriber model, all messages and data flow according to the queue principle (first in, first out) and then to the subscriber.
Note: for production, it is recommended to use the pub/sub implementation of Redis.
There are many packages available on npm
that can be used to implement the pub/sub model for GraphQL subscriptions. Below are some of the most commonly used packages.
-
graphql-yoga
is a fully-featured GraphQL server with focus on easy setup, performance, and a great developer experience -
graphql-subscriptions
lets you wire up GraphQL with a pub/sub system (such as Redis) to implement subscriptions in GraphQL -
apollo-server-express
is the express and connect integration of GraphQL server. Apollo server is a community-maintained, open-source GraphQL server that works with many Node.js HTTP server frameworks
We will use the graphql-yoga
module because it is built over the other two and provides all necessary dependency and server binding with Node.js under the hood. Don’t worry about those last two things; once you get a hang of the implementation, they will be a breeze.
What we will code
We’ll use the post data that is stored inside a JSON file, and we’ll perform the following operations.
-
getPosts
(read all posts) -
getPost
(read a specific post by ID) -
updatePost
(update a post) -
deletePost
(delete a post) -
createPost
(create a post)
Then, we’ll add the subscription to the last three operations.
Now it’s time to get our hands dirty with some code.
First, make a folder, name it whatever you like, and initialize it using Node.js.
mkdir graphql-sub cd graphql-sub npm init
Next, install the dependency required.
npm i --s graphql-yoga
Now we’ll create all our files.
touch index.js postData.json typeDefs.js resolver.js
-
index.js
is responsible for the GraphQLServer creation with pub/sub, which we will see in a minute -
postData.json
is the JSON file on which we will perform CRUD. Add the following code or an array of an object for a post who’s schema should be:-
id:ID!
-
title:String!
-
subtitle:String!
-
body:String!
-
published:Boolean!
-
author: String!
-
upvotes: Int!
-
downvotes: Int!
-
commentCount: Int!
-
-
typeDefs.js
will be used to create schemas for the above operations -
resolvers.js
will have the logic to resolve for all queries, mutation, and subscriptions defined undertypeDefs.js
Inside typeDefs.js
, add the following code.
//type definitions and schemas - (operation and data structure) const typeDefs = ` type Query { getPosts(query: String):[Post!]! getPost(query: String):Post! } type Post{ id:ID! title:String! subtitle:String! body:String! published:Boolean! author: String! upvotes: Int! downvotes: Int! commentCount: Int! } type Mutation{ updatePost( id:ID! title:String! subtitle:String! body:String! published:Boolean! author: String! upvotes: Int! downvotes: Int! commentCount: Int! ): Post! deletePost(id: ID!): Post! createPost( id:ID! title:String! subtitle:String! body:String! published:Boolean! author: String! upvotes: Int! downvotes: Int! commentCount: Int! ): Post! } type Subscription { post: SubscriptionPayload! } type SubscriptionPayload { mutation: String! data: Post! } `; module.exports = typeDefs;
Other than the normal schema definitions for queries and mutation, we have a type called Subscription
that is added on the post object via a custom type SubscriptionPayload
.
Therefore, each time a change is made to a post object, an event will be triggered for all who subscribe to events that return the name of the mutation performed — update, delete, and create and post data.
Now let’s code our resolvers.js
for the above typeDefs
.
const posts = require('./postData'); //Resolvers - This are the set of the function defined to get the desired output for the given API const resolvers = { Query:{ }, Mutation:{ }, Subscription:{ }, } module.exports = resolvers;
Coding objects
We first imported the postData
and then added our resolver
object, which contains our query
, mutation
, and subscription
object.
Let’s code each object one by one .
Query object
We will define two queries — getPost
and getPosts
— inside our query
object.
// return all posts getPosts() { return posts; }, // return post by args passed, for now it just check for body and // title for the post getPost(parent, args){ return posts.filter((post) => { const body = post.body.toLowerCase().includes(args.query.toLowerCase()) const title = post.title.toLowerCase().includes(args.query.toLowerCase()) return body || title; }); }
Mutation object
We will define three mutations — createPost
, updatePost
, and deletePost
— inside our mutation object.
createPost
Check whether the post for the ID already exists. If yes, we’ll throw an error to GraphQL server. Otherwise, we’ll create the post from args
and add it to our posts JSON data.
createPost(parent, args, { pubsub }) { const id = parseInt(args.id, 10); const postIndex = posts.findIndex((post)=> post.id === id); if(postIndex === -1) { posts.push({ ...args }); pubsub.publish('post', { post:{ mutation: 'CREATED', data: {...args} } }); return {...args}; }; throw new Error('Post with same id already exist!'); }
We published an event called CREATED
that will be triggered to all subscribers of the channel post
through the socket and return newly created post data.
updatePost
We will check whether the post for the ID already exists. If it does, we’ll update the post with the args
passed. Otherwise, it’ll throw an error.
updatePost(parent, args, { pubsub }){ const id = parseInt(args.id, 10); const postIndex = posts.findIndex((post)=> post.id === id); if (postIndex !== -1) { const post = posts[postIndex]; const updatedPost = { ...post, ...args }; posts.splice(postIndex, 1, updatedPost); pubsub.publish('post', { post:{ mutation: 'UPDATED', data: updatedPost } }); return updatedPost; } throw new Error('Post does not exist!'); }
As you can see, we again published a new event called UPDATED
that returns the updated post data.
deletePost
We will check whether the post for the ID already exists. If it does, we’ll delete it from the posts array or throw an error.
deletePost(parent, args, { pubsub }){ const id = parseInt(args.id, 10); const isPostExists = posts.findIndex((post)=> post.id === id); if(isPostExists === -1) { throw new Error('Post does not exist!'); } //splice will return the index of the removed items from the array object const [post] = posts.splice(isPostExists, 1); // return post; pubsub.publish('post', { post:{ mutation: 'DELETED', data: post } }) return post; },
Again, we published a new event called DELETED
with the delete post data.
Subscription object
This object uses a pubsub.asyncIterator
function to map the event underlying the source stream to a returned response stream.
The asyncIterator
takes the channel name through which the event across the app will be mapped out.
post:{ subscribe(parent, args, {pubsub}){ return pubsub.asyncIterator('post'); } }
Now the only file left is the index.js
. Add the following code to this file.
const { GraphQLServer, PubSub } = require('graphql-yoga'); const typeDefs = require('./typeDefs'); const resolvers = require('./resolvers'); const pubsub = new PubSub() const server = new GraphQLServer({ typeDefs, resolvers, context:{ pubsub } }) const options = { port: 3000 }; server.start(options, ({ port }) => { console.log( `Graphql Server started, listening on port ${port} for incoming requests.`, ) })
Here, we created a GraphQLServer, passed all our files, and started the server.
Finally, we’ll add a script to run our project in package.json
.
"scripts": { "start": "node index.js" },
Open the terminal and run npm start
. If everything is good, you’ll see the following message.
Graphql Server started, listening on port 3000 for incoming requests.
Now head over to the browser and type localhost:3000
. You’ll see a GraphQL Playground.
Just to check that everything is working as expected, let’s run a getPosts
query.
To start our subscription to the post changes, we’ll open up a new tab in GraphQL Playground and run the following.
subscription{ post{ mutation data{ id, title, subtitle, body, published author, upvotes, downvotes, commentCount, } } }
This enables us to add a subscription to our channel post and start listening for any event published in the channel.
To see it in action, just perform any of the mutations. For example:
mutation { updatePost( id: 8, downvotes:3, author: "deepak gupta", published: true, subtitle: "testinng subtitle", body: "testing body", commentCount: 12, upvotes: 4, title: "oh yeah :)" ) { id } }
As you can see, the post response stream gave back the data for the update event.
Recapping the GraphQL subscription process
To wrap up our tutorial, let’s quickly recap the subscription process. The subscription is defined below in typeDefs.js
.
type Subscription { post: SubscriptionPayload ! } type SubscriptionPayload { mutation: String !, data: Post ! }
Use the pub/sub method provided by graphql-yoga
to subscribe and publish. This can also facilitate mechanisms like EventEmitter
.
const { GraphQLServer, PubSub } = require('graphql-yoga'); const pubsub = new PubSub() const server = new GraphQLServer({ typeDefs, resolvers, context: { pubsub } })
Implement the resolver for subscription type to map the event using pubsub.asyncIterator
. Once we request a subscription from GraphQL Playground, it will add our socket to its listening socket list and send back events while we call pubsub.publish
.
post: { subscribe(parent, args, { pubsub }) { return pubsub.asyncIterator('post'); } }
Finally, call the pubsub.publish()
method from the channel added mutation.
pubsub.publish('post', { post: { mutation: 'UPDATED', data: updatedPost } });
If you’ve followed these steps to a T, you’ve successfully created a GraphQL subscription, a real-time method to sync up client and server.
To see the above app in action, head over to CodeSandbox .
Get yourself added to our 2500+ people subscriber family to learn and grow more and please hit the share button on this article to share with your co-workers, friends, and others.
Check out articles onJavascript, Angular , Node.js , Vue.js
For more articles stay tuned tooverflowjs.com
Thank you!
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
在线进制转换器
各进制数互转换器
html转js在线工具
html转js在线工具