GraphQL Server

Published on October 18, 2022

GraphQL Server

As we’ve already seen here, GraphQL is a powerful query language that allows clients to request data from a server with a flexible and efficient approach. To implement a GraphQL API, you need a server that can interpret and execute GraphQL queries and mutations.

Getting Started

A popular GraphQL server is Apollo Server, which offers a developer-friendly and straightforward way to build GraphQL APIs. Additionally, Apollo Server provides seamless support for any GraphQL client, including Apollo’s own Apollo Client making it a comprehensive and all-in-one solution for GraphQL development.

To create a simple GraphQL server from scratch:

Create an empty npm project:

$ npm init -y

Apollo is the leading tool for creating graphQL servers. Install it together with graphql:

$ npm install aplollo-server graphql
  • In the root directory, create an index.js file:
index.js
const { ApolloServer, gql } = require("apollo-server")

let books = [...] //sample data

const typeDefs = gql`
   type Book {
    title: String!
    published: Int
    author: Author!
    stock: Int!
    city: String
    id: ID!
  }

  type Author {
    name: String!
    id: ID!
    age: Int
  }
 type Query {
    bookList: [Book]
    authorList: [Author]
    inventory: [Book]
    findBooksByAuthor(author: String): [Book]
  }
`

const resolvers = {
  Query: {
    bookList: () => books,
    authorList: () => authors,
    inventory: () => books.find(book => book.stock > 0)
    findBooksByAuthor: (root, args) => books.find(book => book.author === args.author),
  },
}

const server = new ApolloServer({ typeDefs, resolvers })

server.listen().then(({ url }) => {
  console.log("Server readt at: ", url)
})

ApolloServer is the heart of the logic. It receives two parameters:

  • typeDefs - represents the GraphQL schema of the application
  • resolvers - The resolvers parameter specifies the functions that will be used to resolve the fields in the schema. When a client queries for a field, the resolver for that field fetches the data from the appropriate data source and returns it to the client

Resolvers

Resolvers match every query/mutation defined in the GraphQL schema:

type Query {
  bookList: [Book]
  authorList: [Author]
  inventory: [Book]
  findBooksByAuthor(author: String): [Book]
}

type Mutation {
  addBook(title: String!, author: String, published: Int): Book
}

const resolvers = {
  Query: {
    bookList: () => books,
    authorList: () => authors,
    inventory: () => books.find(book => book.stock > 0),
    findBooksByAuthor: (root, args) => books.find(book => book.author === args.author),
  },
  Mutation: {
    addBook: (root, args) => {
      const book = { ...args, id: uuid() }
      // add the author to the authors' data
    authors = authors.concat({ name: args.author, age: null})
      books = books.concat(book)
      return books
    },
  },
}

In mutation resolvers, parameters passed in the GraphQL mutation are passed in the resolver as the args object, which is used to create an instance of the Book object. The new book object is added into the books array, and it also serves as the return value.

Resolver Parameters

A resolver accepts four positional arguments:

fieldName: (root, args, context, info) => {
  result
}
  1. root -This argument refers to the parent object that the field being resolved is a part of. For example, if the field being resolved is a field on the User type, the parent argument would be an instance of the User object.
  2. args - This represents the arguments passed in the GraphQL query. For instance, in our query, the findBooksByAuthor field is defined with a author parameter: findBooksByAuthor: (author: 'Jane Doe'), the args obj will be {author: 'Jane Doe'}. The resolver will use the args object to fiter the data accordingly and return all books by Jane Doe
  3. context - This is an object shared by all resolvers in a particular query, and represents the context each GraphQL request. It can be used to pass information needed by the resolver such as authentication credentials or the current user
  4. info - This contains information about a GraphQL request - including the field name, the path to the field from the root etc

Default Resolvers

Each field in a GraphQL schema must have a corresponding resolver fuction. In our case, hovewever, the only resolvers defined are for the type Query object and the addBook mutation.

For type Book object, no resolvers have been defined, so the GraphQL server will automatically create default resolvers for these fields.

Default resolvers use the first parameter object, root to return the appropriate value for each field.

It is still possible to update the functionality of the default resolver, example:

type Author {
    city: (root) => 'Lagos'
}

Each object will now have city as Lagos. The default resolver will handle the rest of the fields.

Default resolvers can be useful in cases where you want to quickly prototype a GraphQL API without having to write explicit resolvers for every field. However, it is generally recommended to define explicit resolvers for your fields, as this gives you more control over the data that is returned and can make your GraphQL API more flexible and scalable.

To start the server, run:

node index.js

Error Handling

To catch errors in GraphQL queries or mutations, we can do so from the resolvers by using try-catch</spanblocks:

const resolver = (parent, args, context, info) => {
  try {
    // code that may throw an error
  } catch (error) {
    // handle the error by returning an error object
    return {
      success: false,
      message: error.message,
      code: error.code,
    };
  }
}

The return value can be as elaborate as above, or it could be a simple null

Conclusion

A GraphQL server handles all the queries and mutations sent by clients,retrieves or updates the requested data, and returns the results to the client in the form of a GraphQL response, usually in JSON format. The server exposes a single endpoint to achieve all this, and clients send their requests to this endpoint. Next, see how to connect React and GraphQL