GraphQL is the alternative for REST made by Facebook. When facebook reached the limits of REST API, they had to make something breaking these limits. Through this article, you can learn what GraphQL is, how it works, and how to use it in your projects. GraphQL (Graph Query Language) is an API-level query language. Like SQL is used for requests to relational databases, GraphQL allows clients to request data from an API. For a good understanding of this article, you should have a basic understanding of TypeScript and NodeJS. Why is GraphQL so good? Receive only data you need. GraphQL allows you to manage what data API will return to you. This has a number of advantages. It helps network performance when the response contains a lot of data you don't need. Of course, REST can handle it via request parameters like , however much easier when it is available by default, does it? It is also useful if your API supports different types of clients which want to call less and fetch more. ?fields=name,birthdate Access to a complex graph of objects aggregated from different sources. GraphQL allows you to get a complex graph of objects by a single request. These objects might be from different sources. Clients and server API schemes are always in sync. Your clients and server have the same scheme, so it is easy to sync it. GraphQL anatomy To understand how GraphQL realizes all you’ve read above, let’s talk about what artifacts GraphQL includes. GraphQL includes schema, which describes the types of objects you can operate on. Take a look at the following schema: type App { id: ID! name: String! } This is the type App, which includes id and name required fields. It means you can operate this object. But how to operate it? There are a few access operators in GraphQL. The first one (required one) - . This is a getter for your object. The second one - , is a setter. And the last one is a , which allows you to subscribe for updates of your type. query mutation subscription type Query { apps: [App!]! app(id: ID!): App! } type Mutation { createApp(app: AppInput!): App! updateApp(appId: ID!, app: AppInput!): App! } input AppInput { name: String! publisherId: ID! } type Subscription { appMutated: AppMutationPayload! } type AppMutationPayload { mutation: MutationType! node: App! } enum MutationType { CREATED UPDATED DELETED } The code snippet above contains . Inputs allow you to define input parameters types. input AppInput Another important component of GraphQL - resolvers. Resolvers define how to handle queries, mutations, and subscribers. We will come back to it soon. Apollo Apollo Server is a Node.js implementation of graphQL server. There is good scalable architecture and a strong community that means it is a good choice for use. We won’t talk much about Apollo architecture, we’ll learn everything needed by code examples. Install dependencies "dependencies": { "apollo-datasource": "^0.8.0", "apollo-server": "^2.22.1", "graphql": "^15.5.0" }, "devDependencies": { "@types/graphql": "^14.5.0", "@types/node": "^14.14.37", "ts-node": "^9.1.1", "typescript": "^4.2.3" } - allows implementing GraphQL server - allows implementing data sources (like database aggregators or rest api integrations) - graphQL package. Required by Apollo. apollo-server apollo-datasource graphql Server Initialization import {ApolloServer, gql} from 'apollo-server' import {typeDefs} from './graphql/typedefs' import {resolvers} from './graphql/resolvers' import {dataSources} from './datasources' const server = new ApolloServer({ typeDefs: gql` ${typeDefs} `, resolvers, dataSources }) server.listen().then(({url}) => { console.log(`Server ready at ${url}`) }) Let’s understand the code above. We are initializing Apollo Server with several arguments. - this is a path for graphQL schema - as a said earlier, we will come back for that a bit later - data sources like DB aggregators and API integrations typeDefs resolvers dataSources DataSources import { AppService } from './app-service'; export const dataSources = () => ({ appService: new AppService() }); This is DI of dataSources. This format is required by Apollo. import { DataSource } from 'apollo-datasource'; export class AppService extends DataSource { constructor() { super(); } } dataSource might look like this. This is an aggregator, where you can implement business logic and accessors for data. Resolvers Finally, let’s talk about resolvers. As I said earlier, resolvers define how to handle queries, mutations, and subscribers. Take a look at that: type Mutation { createApp(app: AppInput!): App! updateApp(appId: ID!, app: AppInput!): App! } type Subscription { appMutated: AppMutationPayload! } type AppMutationPayload { mutation: MutationType! node: App! } And now take a look at the resolver: import { pubsub } from '../pubsub'; const APP_MUTATED = 'appMutated'; export default { Query: { apps(parent, args, {dataSources}) { return dataSources.appService.getApps(); }, app(parent, args, {dataSources}) { return dataSources.appService.getApp(args.id); } }, Mutation: { async createApp(parent, args, {dataSources}) { const { publisherId, ...rest } = args.app; let app = await dataSources.appService.createApp({...rest}, publisherId); pubsub.publish(APP_MUTATED, { appMutated: { mutation: 'CREATED', node: app } }); return app; }, async updateApp(parent, args, {dataSources}) { const { publisherId, ...rest } = args.app; let updatedApp = await dataSources.appService.updateApp(args.appId, {...rest}, publisherId); publishAppUpdated(updatedApp); return updatedApp; }, async setAppDevelopers(parent, args, {dataSources}) { let updatedApp = await dataSources.appService.setAppDevelopers(args.appId, args.developerIds) publishAppUpdated(updatedApp); return updatedApp; } }, Subscription: { appMutated: { subscribe: () => pubsub.asyncIterator(APP_MUTATED) } }, }; function publishAppUpdated(app) { pubsub.publish(APP_MUTATED, { appMutated: { mutation: 'UPDATED', node: app } }); return app; } This resolver totally defines all queries, mutations, and subscribers. And as you can see, we have access to data sources that were injected into ApolloServer. let app = await dataSources.appService.createApp({...rest} We deal with this, but what about subscribers? Subscription: { appMutated: { subscribe: () => pubsub.asyncIterator(APP_MUTATED) } } And what is the code in mutations? pubsub.publish(APP_MUTATED, { appMutated: { mutation: 'CREATED', node: app } }); This is pubSub. Maybe you know this pattern. Microservices usually communicate using it. This is required for subscribers in Apollo. And it makes sense because subscribers are WebSockets. Websockets keep connections, so it is easy to reach a high load. So, the Apollo team decided to handle it with this design solution. Anyway, there is an in-memory dummy, just for testing. Never use it in production. import { PubSub } from 'apollo-server'; export const pubsub = new PubSub(); How to test Run the app using the following command: ts-node src/index.ts Go to localhost:4000 via web browser and see the following: This is the test console Apollo. You can test queries, mutations, and subscriptions here. Congratulations! You’ve just learned how to build graphQL API using Apollo and Node.JS. Full code example you can find here: https://github.com/gandronchik/graphql-example