Backend Development 21 min read

Comprehensive Guide to GraphQL and Building a GraphQL API with ThinkJS

This article explains what GraphQL is, how it solves common REST API problems, details its request structure, schema, type system, fragments, aliases, interfaces, unions, and resolvers, and provides a step‑by‑step tutorial for creating a GraphQL server using ThinkJS, MongoDB, and Apollo Server, while also outlining its advantages and drawbacks.

360 Tech Engineering
360 Tech Engineering
360 Tech Engineering
Comprehensive Guide to GraphQL and Building a GraphQL API with ThinkJS

GraphQL is a data query and manipulation language pioneered by Facebook that acts as a middleware between client and database, allowing clients to describe exactly what data they need in a single request.

Compared with traditional RESTful APIs, GraphQL reduces the number of HTTP calls, avoids over‑fetching or under‑fetching data, and lets clients request nested resources such as movie details, actors, and reviews with one query.

A typical GraphQL request body looks like this:

query myQry($name: String!) {
  movie(name: "Manchester") {
    name
    desc
    ratings
  }
}

The request defines an operation type ( query , mutation or subscription ), an optional operation name, and a selection set that determines the shape of the response.

GraphQL’s type system is defined in a Schema (SDL). Basic scalar types include Int , Float , String , Boolean and ID . Types can be composed with type , interface , union , enum and input definitions, for example:

type Movie {
  name: String!
  desc: String!
  ratings: String!
  score: Float!
  actors: [Actor]
}

type Actor {
  name: String!
  dob: String!
  movies: [Movie]
}

Fragments and aliases help avoid duplication when querying multiple similar objects:

fragment movieInfo on Movie {
  name
  desc
  ratings
}

query {
  first: movie(id: 1) { ...movieInfo }
  second: movie(id: 2) { ...movieInfo }
}

Resolvers are functions attached to schema fields that fetch the actual data. For a ThinkJS‑based GraphQL server, resolvers might look like:

module.exports = {
  Query: {
    movie(_, args) {
      return MovieModel.find({ name: args.name }).sort({ _id: -1 }).exec();
    },
    actor(_, args) {
      return ActorModel.find({ name: args.name }).sort({ _id: -1 }).exec();
    }
  },
  Movie: {
    actors(parent) {
      return Promise.all(parent.actors.map(id => ActorModel.findOne({ _id: id }).exec()));
    }
  },
  Actor: {
    movies(parent) {
      return Promise.all(parent.movies.map(id => MovieModel.findOne({ _id: id }).exec()));
    }
  }
};

To set up the environment, install ThinkJS and create a project:

npm install -g think-cli
thinkjs new gqldemo
cd gqldemo
npm install && npm start

Configure MongoDB via src/config/extend.js and adapter.js :

const mongoose = require('think-mongoose');
module.exports = [mongoose(think.app)];
export.model = {
  type: 'mongoose',
  mongoose: {
    connectionString: 'mongodb://your-db/gql',
    options: {}
  }
};

Create Mongoose models for actor and movie in the model directory (see the article for full definitions).

Define a middleware that intercepts /graphql requests and uses Apollo Server Core’s runHttpQuery to execute the GraphQL operation:

const { runHttpQuery } = require('apollo-server-core');
module.exports = (options = {}) => async ctx => {
  return runHttpQuery([ctx], {
    method: ctx.request.method,
    options,
    query: ctx.request.method === 'POST' ? await ctx.post() : ctx.param()
  })
    .then(rsp => { ctx.set('Content-Type', 'application/json'); ctx.body = rsp; })
    .catch(err => {
      if (err.name !== 'HttpQueryError') throw err;
      if (err.headers) Object.keys(err.headers).forEach(h => ctx.set(h, err.headers[h]));
      ctx.status = err.statusCode;
      ctx.body = err.message;
    });
};

Combine the SDL schema and resolvers into an executable schema with graphql-tools :

const { makeExecutableSchema } = require('graphql-tools');
const typeDefs = require('./schema');
const resolvers = require('./resolvers');
const schema = makeExecutableSchema({ typeDefs, resolvers });
module.exports = { match: '/graphql', handle: graphqlHandler({ schema }) };

The article concludes with a brief list of GraphQL’s advantages (single‑request data fetching, strong typing, self‑documenting schema) and disadvantages (added server‑side complexity, potential migration challenges).

Backend DevelopmentAPI designMongoDBGraphQLApollo Serverthinkjs
360 Tech Engineering
Written by

360 Tech Engineering

Official tech channel of 360, building the most professional technology aggregation platform for the brand.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.