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… .