GraphQL is a new way to build aN API using a strongly typed query language. GraphQL was launched by Facebook in 2015, and is now gaining traction and being adopted by Twitter and other large companies like Github. I wrote a brief article on NodeJS building GraphQL API service to briefly describe how to build the API. In this article, we’ll show you how to set up the GraphQL Server in Node.js using Apollo Server.
A high-level overview of GraphQL on the server
Once you’re familiar with all the moving parts, getting started with GraphQL is actually pretty straightforward. The GraphQL service is defined by a schema that works roughly as follows:
Types: type
A type is a strongly typed representation of a data model, and this is an example of a post type defined using Apollo’s GraphQL-Tools, which will be used in this tutorial to define the schema.
import User from "./user_type";
const Post = `
type Post {
id: Int!
title: String!
body: String!
author_id: Int!
author: User
}
`;
export default () => [Post, User];
Copy the code
The Queries: Queries
A query is a way to define which queries can be run against the schema. This is an example of a query in the RootQuery of a schema;
const RootQuery = ` type RootQuery { posts: [Post] post(id:Int!) : Post users: [User] user(id:Int!) : User } `;Copy the code
Mutations that change
Changes are similar to POST requests (although they’re really just a synchronous version of a query), and they allow data to be sent to the server for insertion, update, or deletion. The following is an example of defining changes for a new blog post, which accepts the input type PostInput and returns the newly created post as a POST type.
const RootMutation = ` type RootMutation { createPost(input: PostInput!) : Post } `;Copy the code
Subscriptions: the subscription
Subscriptions allow live events to be published via the GraphQL subscriber. An example of a subscription is defined below:
const RootSubscription = `
type RootSubscription {
postAdded(title: String): Post
}
`;
Copy the code
You can now publish events to subscribed events by running this event in the createPost mutation parser.
Pubsub. publish(' postAdded ', {postAdded: post});Copy the code
Resolvers: Resolvers
The parser is where operations are performed in response to a query, mutation, or subscription, and where you can go into the database layer to perform CRUD operations and return the appropriate response. Here is an example:
resolvers: {
RootQuery: {
posts: () => posts,
post: async (_, { id }) =>
await Post.query()
},
RootMutations: {
createPost: async (_, { input }) =>
await Post.query.insert(input)
},
RootSubscriptions: {
postAdded: {
subscribe: () =>
pubsub.asyncIterator('postAdded')
},
Post: {
author: async post =>
await User.query().where("id", "=", post.author_id)
}
}
Copy the code
Schema: model
A Schema is an API that connects all the living parts together to build a service.
Start entering the project
If you want to see the code, find a repository here.
Install dependencies
Start by creating a project called graphQL-Hello-API
mkdir graphql-hello-api
Copy the code
Then go to the directory and execute the following command:
yarn init
Copy the code
Add necessary dependencies:
yarn add apollo-server graphql
Copy the code
Create a Hello!
Create a folder named SRC. To better illustrate the process, different examples will be named with different file names. First create a file: index001.js.
We first define a query type:
const typeDefs = gql`
type Query {
hello: String
}
`;
Copy the code
Next define the parser (or root in the GraphQL tutorial) to parse the given query:
const resolvers = { Query: { hello: () => { return "Hello World!" ; ,}}};Copy the code
Finally, instantiate ApolloServer and start the service.
const server = new ApolloServer({ typeDefs, resolvers });
server.listen(3005).then(({ url }) => {
console.log(`🚀 GraphQL Server ready at ${url}`);
});
Copy the code
All the code for index001.js is as follows:
const { ApolloServer, gql } = require("apollo-server"); const typeDefs = gql` type Query { hello: String } `; const resolvers = { Query: { hello: () => { return "Hello World!" ; ,}}}; const server = new ApolloServer({ typeDefs, resolvers }); Server.listen (3005).then(({url}) => {console.log(' 🚀 GraphQL server ready at ${url} '); });Copy the code
To start GraphQL Server, go to the SRC folder and run the following command to open the browser and type http://localhost:3005/ :
node index001.js
Copy the code
You will see the following interface:
Enter the defined query {hello} as shown in the figure above. The result is as follows:
The basic types of GraphQL queries can consist of strings, integers, floating-point numbers, Booleans, and ids and their lists []. Let’s start adding some logic.
Here, typeDefs is defined as follows using different types. This!!!! Represents a result that cannot be null. Next we create index002.js and define three queries, strings, floats, and [].
const typeDefs = gql`
type Query {
today: String
random: Float!
fibonacci: [Int]
}
`;
Copy the code
Set the parser accordingly, as follows:
const resolvers = { Query: { today: () => { return new Date().toDateString(); }, random: () => { return Math.random(); }, fibonacci: () => { return fibonacci(10); ,}}};Copy the code
Now you can look at the complete code for index.js:
const { ApolloServer, gql } = require("apollo-server"); const fibonacci = (length) => { let nums = [0, 1]; for (let i = 2; i <= length; i++) { nums[i] = nums[i - 1] + nums[i - 2]; } return nums; }; const typeDefs = gql` type Query { today: String random: Float! fibonacci: [Int] } `; const resolvers = { Query: { today: () => { return new Date().toDateString(); }, random: () => { return Math.random(); }, fibonacci: () => { return fibonacci(10); ,}}}; const server = new ApolloServer({ typeDefs, resolvers }); Server.listen (3005).then(({url}) => {console.log(' 🚀 GraphQL server ready at ${url} '); });Copy the code
The running results are as follows:
Passing parameters
Now to show how to use the query to pass some parameters to the server and create index003.js, in this example we will define the query to get a Fibonacci array of the specified length and define the parameter Length. The code is as follows:
const typeDefs = gql` type Query { fibonacci(length:Int!) : [Int] } `;Copy the code
Next comes the parser, and note that when using Apollo Server, its API is slightly different from the GraphQL API. The argument is passed through the second argument in the form Fibonacci: (_, {length}), ignoring the first argument with _.
const resolvers = { Query: { fibonacci: (_, { length }) => { return fibonacci(length); ,}}};Copy the code
Here’s the complete code:
const { ApolloServer, gql } = require("apollo-server"); const fibonacci = (length) => { let nums = [0, 1]; for (let i = 2; i <= length; i++) { nums[i] = nums[i - 1] + nums[i - 2]; } return nums; }; const typeDefs = gql` type Query { fibonacci(length: Int!) : [Int] } `; const resolvers = { Query: { fibonacci: (_, { length }) => { return fibonacci(length); ,}}}; const server = new ApolloServer({ typeDefs, resolvers }); Server.listen (3005).then(({url}) => {console.log(' 🚀 GraphQL server ready at ${url} '); });Copy the code
Enter a query in the left window:
{
fibonacci(length:10)
}
Copy the code
The running results are as follows:
Object type
Sometimes you need to return a more complex object constructed of a primitive type. This can be done by declaring a class (JavaScript ES6) type for it. Create a new file, index004.js, and the complete code is as follows:
const { ApolloServer, gql } = require("apollo-server"); /** * const typeDefs = GQL 'type RandomDie {numSides: Int! rollOnce: Int! roll(numRolls: Int!) : [Int] } type Query { getDie(numSides: Int): RandomDie } `; class RandomDie { constructor(numSides) { this.numSides = numSides; } rollOnce() { return 1 + Math.floor(Math.random() * this.numSides); } roll({ numRolls }) { const output = []; for (let i = 0; i < numRolls; i++) { output.push(this.rollOnce()); } return output; } } const resolvers = { Query: { getDie: (_, { numSides }) => { return new RandomDie(numSides || 6); ,}}}; const server = new ApolloServer({ typeDefs, resolvers }); Server.listen (3005).then(({url}) => {console.log(' 🚀 GraphQL server ready at ${url} '); });Copy the code
Call getDie as the base query while typing the query, as follows:
{
getDie(numSides:6){
numSides,
rollOnce,
roll(numRolls:10)
}
}
Copy the code
The running results are as follows:
The use of mutation
Mutations replace Query with mutation and create index005.js for server-side data changes.
const { ApolloServer, gql } = require("apollo-server"); const fakeDb = {}; const typeDefs = gql` type Mutation { setTitle(title: String): String } type Query { getTitle: String } `; const resolvers = { Mutation: { setTitle: (_, { title }) => { fakeDb.title = title; return title; }, }, Query: { getTitle: () => { return fakeDb.title; ,}}}; const server = new ApolloServer({ typeDefs, resolvers }); Server.listen (3005).then(({url}) => {console.log(' 🚀 GraphQL server ready at ${url} '); });Copy the code
Enter the query:
mutation{
setTitle(title:"Hello DevPoint!")
}
Copy the code
The result is as follows:
Input type
Create index006.js as an interface to define the structure of the input type, and implement an example of maintaining the basic information of a website. The overall code is as follows:
const { ApolloServer, gql } = require("apollo-server"); const { nanoid } = require("nanoid"); const typeDefs = gql` input SiteInput { title: String author: String url: String } type SiteDetail { id: ID! title: String author: String url: String } type Query { getSite(id: ID!) : SiteDetail } type Mutation { createSite(input: SiteInput): SiteDetail updateSite(id: ID! , input: SiteInput): SiteDetail } `; class SiteDetail { constructor(id, { author, title, url }) { this.id = id; this.title = title; this.author = author; this.url = url; } } const fakeDb = {}; const resolvers = { Mutation: { createSite: (_, { input }) => { var id = nanoid(); fakeDb[id] = input; return new SiteDetail(id, input); }, updateSite: (_, { id, input }) => { if (! FakeDb [id]) {throw new Error(" message does not exist "+ id); } fakeDb[id] = input; return new SiteDetail(id, input); }, }, Query: { getSite: (_, { id }) => { if (! FakeDb [id]) {throw new Error(" message does not exist "+ id); } return new SiteDetail(id, fakeDb[id]); ,}}}; const server = new ApolloServer({ typeDefs, resolvers }); Server.listen (3005).then(({url}) => {console.log(' 🚀 GraphQL server ready at ${url} '); });Copy the code
Execute node index006.js to create a site information object and query the ID of the newly created object:
mutation{
createSite(input:{
title:"DevPoint",
author:"QuintionTang",
url:"https://www.devpoint.com"}
){
id
}
}
Copy the code
The running results are as follows:
Next, query information based on the returned ID:
query{
getSite(id:"w3pFxgiCyHgZ8vF6ip1D2"){
id,
author,
title,
author
}
}
Copy the code
The running results are as follows:
Perform the update operation and query the latest data:
mutation{
updateSite(id:"w3pFxgiCyHgZ8vF6ip1D2",input:{
title:"DevPoint WebSite"
}){
id,
title,
author
}
}
Copy the code
The running results are as follows:
validation
Before, a friend asked whether there is a unified verification place.
Does GraphQL have a unified entry to verify the validity of parameters?
The answer is yes, you can use the GraphQL context to authenticate between the HTTP server and the GraphQL server. Verify requests and user permissions through the custom context building function. In the next installment, I will write a special article about identity and request validity verification in GraphQL.
const server = new ApolloServer({ typeDefs, resolvers, context: ({req}) => {// Request validation here const author = "QuintionTang"; return { author }; }});Copy the code
All of the above code is submitted to Github, github.com/QuintionTan… .