在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
开源软件名称(OpenSource Name):shaoruu/modern-graphql-tutorial开源软件地址(OpenSource Url):https://github.com/shaoruu/modern-graphql-tutorial开源编程语言(OpenSource Language):JavaScript 88.0%开源软件介绍(OpenSource Introduction):Modern GraphQL TutorialAfter this tutorial, you will be able to:
Table of ContentsIntroductionBefore we start, let me give you a brief introduction to GraphQL. GraphQL is a query language for API's. It is a more efficient alternative to traditional RESTful API's since it is:
An example would be: Say you are creating an Instagram-like app. When a user opens up the app, you want to show him the posts on his news feed, so you make a network request to your backend and fetch all the post data. As for the traditional RESTful API, you fetch the post data including the comments and details about the comments on the posts. However, that would be overfetching since the user may not necessarily click into individual posts to check comments. Here, GraphQL comes into play by letting you specify what kind of data you want and how many comments you want for each post. This not only limits the amount of data needed to transfer through the internet, it also speeds up the fetching efficiency and speed. In this tutorial, I will teach you how to set up your own GraphQL API, write a simple GraphQL client, and start communication between backend and frontend. I will be using graphql-yoga for the backend service, and apollo for a simple client side. Getting StartedType the following steps into terminal to get started with this tutorial. git clone https://github.com/ian13456/modern-graphql-tutorial.git
cd modern-graphql-tutorial/backend
yarn I will walk you through each file later on and teach you what it's for. GraphQL PlaygroundOne awesome tool that comes with graphql-yoga is GraphQL Playground. GraphQL Playground allows us to test our schemas without having to set up a client side. Essentially, it serves as a GraphQL client to run queries and mutations on. After cloning the repository, run // backend/src/index.js
import { GraphQLServer, PubSub } from 'graphql-yoga'
import db from './db'
import Query from './resolvers/Query'
import Mutation from './resolvers/Mutation'
import Subscription from './resolvers/Subscription'
import User from './resolvers/User'
import Post from './resolvers/Post'
import Comment from './resolvers/Comment'
// This will be covered later.
const pubsub = new PubSub()
// Don't worry about anything; just know that this initializes the server.
const server = new GraphQLServer({
typeDefs: './src/schema.graphql',
resolvers: {
Query,
Mutation,
Subscription,
User,
Post,
Comment
},
context: {
db,
pubsub
}
})
// Serving server on port 5000
server.start({ port: process.env.PORT | 5000 }, () => {
console.log(`The server is up on port ${process.env.PORT | 5000}!`)
}) Basic SyntaxAs mentioned before, GraphQL is a query language with specific syntax. Here an example of a basic query: query {
users {
username
password
creditCardNumber
}
} As I said before, GraphQL gives you the ability to specify what data's are needed for fetching. Lets start from the beginning. The out-most layer On the second layer, we have the specific query to run called Lastly, within the SchemaA GraphQL Schema is what defines the basic types. The file is often named Basic Types:These are the basic scalar types in GraphQL. Scalar types basically mean that these values are scalars and do not contain any sub-fields below them.
To add on, GraphQL "Lists" is a type that defines an array of a certain type with the notation For other special types such as Queries, Mutations and SubscriptionsQueries, mutations and subscriptions are the three major types that you include in Before I dig into what these three types are, I want to teach you about what GraphQL resolvers are for first. Resolvers are methods that you write in any language that defines what each query, mutation, and subscription does. They basically tell the GraphQL endpoint how to handle different types of data fetches. In most cases, resolvers are where you alter the database after receiving data from GraphQL requests. In graphql-yoga, they are the files imported and included in the With the knowledge of QueriesThese are the types of network requests that simply ask for data from the server. Remember the example I showed you in the Basic Syntax section? Here I defined the exact same query type # backend/src/schema.graphql
type Query {
users(query: String): [User!]!
}
type User {
id: ID!
name: String!
email: String!
age: Int
posts: [Post!]!
comments: [Comment!]!
} There are three important keys that I want to point out in this short GraphQL code:
However, this only tells GraphQLServer what the query looks like though. So, below is where I use resolvers to define HOW the server should handle any // backend/src/resolvers/Query.js
users(parent, args, context, info) {
const { db } = context
// Check if the optional `query` of type String is passed in.
if (!args.query) {
return db.users
}
// Reaching here means `query` is defined -> filter the data passed back.
return db.users.filter((user) => {
return user.name.toLowerCase().includes(args.query.toLowerCase())
})
} Here's a possible GraphQL request for this query {
users(query: "a") {
id
name
age
}
}
# results:
{
"data": {
"users": [
{
"id": "1",
"name": "Andrew",
"age": 27
},
{
"id": "2",
"name": "Sarah",
"age": null
}
]
}
} MutationsNow that you have a basic understanding of what queries are, Mutations are fairly easy to understand. Mutations are where you mutate the data in the database. note that I used a temporary file # backend/src/schema.graphql
type Mutation {
createUser(data: CreateUserInput!): User!
}
input CreateUserInput {
name: String!
email: String!
age: Int
} There are only a few things worth noticing in this Mutation type specification:
Ok, with the // backend/src/resolvers/Mutation.js
createUser(parent, args, context, info) {
const { db } = context
// Check if email is already taken in fake database
const emailTaken = db.users.some((user) => user.email === args.data.email)
if (emailTaken) {
throw new Error('Email taken')
}
// Create new user object
const user = {
id: uuidv4(),
...args.data
}
// Save (append) it to fake database
db.users.push(user)
return user
} Worth noting that in a regular project, the lines of code where I alter the fake database should be replaced by processes that communicate with an actual database and change/save/delete data. Here's an example of a GraphQL mutation: mutation {
createUser(data: {
name: "Morgan"
age: 81
email: "[email protected]"
}) {
id
name
}
}
# results:
{
"data": {
"createUser": {
"id": "47afd124-0c06-4e2e-992f-0b837d4f0d5e",
"name": "Morgan"
}
}
} SubscriptionsSubscriptions are a little bit different from regular Queries and Mutations. This is a very interesting feature provided by GraphQL that handles the real time aspect of modern web. Think of GraphQL Subscriptions as YouTube subscriptions. Once you are subscribed to YouTube channel, you get notified with the latest news from the channel. Not only you, it's everyone who's subscribed gets notified. With GraphQL Subscriptions, you can set up quote unquote "channels" on your endpoint with specified types of "content" that these "channels" post. Once data is mutated, you can then "post" the data up to the channel, notifying whoever's subscribed. Here's an example in my code of subscriptions on the comment section of a specific post: # backend/src/schema.graphql
type Subscription {
comment(postId: ID!): CommentSubscriptionPayload!
}
type CommentSubscriptionPayload {
mutation: MutationType!
data: Comment!
}
enum MutationType {
CREATED
UPDATED
DELETED
} There are only two important key points I want to mention here:
The resolver of GraphQL subscriptions is a bit trickier. Since you are now not only querying or mutating data, but actually setting up a websocket link between the client and the server, you need pubsub system on the server-side. Looking back to index.js, we can now clearly see the initialization of a // backend/src/index.js
import { GraphQLServer, PubSub } from 'graphql-yoga'
// ...import...lots...of...files...
const pubsub = new PubSub()
const server = new GraphQLServer({
typeDefs: './src/schema.graphql',
resolvers: {
Query,
Mutation,
Subscription,
User,
Post,
Comment
},
context: {
db,
pubsub
}
}) Now you may wonder what the However, the PubSub system isn't as easily set up as you thought. In order to set up websocket channels for each GraphQL Subscription, you need to set up a required // backend/src/resolvers/Subscription.js
const Subscription = {
comment: {
subscribe(parent, { postId }, { db, pubsub }, info) {
const post = db.posts.find((post) => post.id === postId && post.published)
if (!post) {
throw new Error('Post not found')
}
return pubsub.asyncIterator(`comment ${postId}`)
}
}
}
export { Subscription as default } Here I directly destructure With the // backend/src/resolvers/Mutation.js
...
const comment = {
id: uuidv4(),
...args.data
}
// THIS IS WHERE I PUBLISH MY DATA TO THE CHANNEL
pubsub.publish(`comment ${args.data.post}`, {
comment: {
mutation: 'CREATED',
data: comment
}
})
... To sum up, here's a quick example of how GraphQL subscriptions work: # subscription:
subscription {
comment (postId: 10) {
mutation
data {
text
author {
name
}
}
}
}
# mutation that was run after subscription:
mutation {
createComment(data: {
text: "Hello sir! Nice Post!"
author: 1
post: 10
}) {
text
author {
name
}
}
}
# results on subscription side:
{
"data": {
"comment": {
"mutation": "CREATED",
"data": {
"text": "Hello sir! Nice Post!",
"author": {
"name": "Andrew"
}
}
}
}
} Apollo ClientIn this example, I created an application that contains a form and a list of posts. The form allows you to use Before I start explaining the magic behind this, I strongly recommend you read the react-apollo documentations. It is well documented, and you will definitely learn a lot from it. In order to run the live exmaple above, run the commands below: cd frontend
yarn
yarn start Setting up Apollo clientIn order to connect to the backend API, you need to set up a GraphQL client like so: // frontend/src/index.js
import {
ApolloClient,
InMemoryCache,
ApolloProvider,
HttpLink
} from '@apollo/client'
import { split } from 'apollo-link'
import { WebSocketLink } from 'apollo-link-ws'
import { getMainDefinition } from 'apollo-utilities'
// Create an http link:
const httpLink = new HttpLink({
uri: 'http://localhost:5000/'
})
// Create a WebSocket link:
const wsLink = new WebSocketLink({
uri: `ws://localhost:5000/`,
options: { reconnect: true }
})
// using the ability to split links, you can send data to each link
// depending on what kind of operation is being sent
const link = split(
// split based on operation type
({ query }) => {
const definition = getMainDefinition(query)
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
)
},
wsLink,
httpLink
)
const client = new ApolloClient({
link,
cache: new InMemoryCache().restore({})
}) The HTTP link ( Querying and SubscribingQuerying in react-apollo is pretty simple. All you have to do is pass a // frontend/src/graphql/queries.js
import { gql } from '@apollo/client'
export const POSTS_QUERY = gql`
query {
posts {
title
body
author {
name
}
published
}
}
`
// frontend/src/graphql/subscriptions.js
export const POSTS_SUBSCRIPTION = gql`
subscription {
post {
mutation
data {
title
body
author {
name
}
published
}
}
}
` // frontend/src/containers/App/App.js
...
const { loading, error, data, subscribeToMore } = useQuery(POSTS_QUERY)
...
useEffect(() => {
subscribeToMore({
document: POSTS_SUBSCRIPTION,
updateQuery: (prev, { subscriptionData }) => {
if (!subscriptionData.data) return prev
const newPost = subscriptionData.data.post.data
return {
...prev,
posts: [newPost, ...prev.posts]
}
}
})
全部评论
专题导读
上一篇:kkemple/graphql-auth: 发布时间:2022-06-22下一篇:hasura/awesome-vue-graphql: A curated collection of resources, clients and tools ...发布时间:2022-06-22热门推荐
热门话题
阅读排行榜
|
请发表评论