Writing a GraphQL TypeScript Server for NodeJS
Tags: GraphQL, JavaScript, Nodejs, Docker, Typescript

GraphQL is becoming an increasingly viable alternative to REST in modern web development by providing significant productivity and performance advantages.

In this post we will explore some important parts of writing a NodeJS code-first vanilla GraphQL Server in TypeScript.

This is for

people who understand the basics of GraphQL and want to see some patterns, techniques, and different non-decorator approaches to building a GraphQL server in TypeScript.

This is not

A complete front-to-back tutorial.

You will see important code snippets and fill in the blanks with the rest of your stack.

Final Result

For the complete example server code, visit GitHub.

Supported Queries

A GraphQL server built with the architecture outlined below will support queries and mutations such as:

Background

Code-first: Decorators vs Objects

Most popular guides use experimental / to-be-deprecated TypeScript (ES6) Decorators which obscure the GraphQL resolution process by merging the ORM layer with the API and the API layer with the authorisation layer.

Instead of Decorators, we will use the primitives provided by the graphql npm package. Most importantly: GraphQLObjectType and GraphQLInputType . These primitives are powerful enough to build a highly expressive and flexible GraphQL API.

GraphQL as the Engine of Application State

HATEOAS (Hypertext as the Engine of Application State) is an important part of the rest standard.

In practice, HATEOAS means the server should publish client-resource authorisation and routing to minimise code duplication on the client. A resource response should contain authorisation and link metadata.

GraphQL makes this easier than REST since GraphQL APIs often served off a single URL removing the need for “links”. Furthermore, GraphQL’s introspective type system lets developers circumvent API docs.

Leveraging Query Resolution to Create Flexible Graphs

Understanding how GraphQL resolves queries by separating Type and Source lets us build flexible and well-separated JSON structures.

A GraphQL server resolves a query by traversing the GraphQLObjectType tree to build a JSON response. However, the Source returned from a resolver need not of similar shape as the resolving GraphQLObjectType .

For example, a GraphQLObjectType that resolves an Account would typically resolve all fields, relations, and metadata on the same GraphQLObjectType node. Although, having separated our ORM layer from our GraphQL layer (something a Decorator based approach would obscure), we can separate an Account into multiple GraphQLObjectTypes representing an Accounts different categories of data, such as AccountData (the fields on an accounts table in the database), AccountActions (G|HATEOAS / action authorisation for the Account resource), AccountRelations (or AccountConnections ), and additional objects for any additional categories of data associated with an Account .

This is in contrast to the flat equivalent:

Code

Schema

As always with GraphQL, we provide a root Query type for reading and root Mutation type for updating.

Due to NodeJS module resolution and the cyclic nature of graph data structures, we run into import races when Node resolves our program. To get around this, the graphql library lets us define fields as Thunks. Instead of defining a field object, we define a function that returns the field object. After all circular modules are resolved by node, the GraphQL server can resolve the fields and the schema.

AccountQuery

We define root level query fields for Accounts , which are provided to the RootQuery above.

This is what lets us write

AccountCollectionNode

An AccountCollectionNode represents a paginated list of Accounts .

It has an array of AccountNodes , a AccountCollectionActions object with actions the client is/isn't authorised to take on the collection, and a PageInfo object detailing the results page number, total pages, whether there are more pages, etc...

AccountNode

AccountNode is the root node for an Account . Its fields are nodes representing different categories of data of the parent Account such as AccountData with the id, name, etc..., AccountActions with client authorisation, and AccountRelations .

Note that the AccountNode and all its children; AccountData , AccountActions , and AccountRelations , all have the same source/parent, the ORM's AccountModel . Look at the AccountNode's fields to see where we tell GraphQL to resolve as AccountData , AccountActions , AccountRelations , but simply return the parent.

Understanding this is especially crucial for paginated relations since even if you prefer flat over nested schema, paginated Relations/Connections must always be nested.

Root Node

|

Paginated Connection Node - requires the RootNode's source for querying the related data

/ \

PageInfo Related Nodes

i.e.

AccountPolicy

Policy classes are not specific to GraphQL but are included here because of their importance. They are single responsibility objects that authorise actions. This means they can both be used to 403/404 a request, or to provide a G|HATEOAS response publishing authorised actions. They can be used in HTTP Requests, GraphQL requests, WebSocket requests, RPC requests, CRON contexts, Job contexts, Migration contexts, Seeder contexts, or anything else that could fit a RequestContext interface.

Policies exist in contrast to other popular techniques like Guards that apply method/route based authorisation to endpoints (such as resolvers, HTTP endpoints, etc...) and whose logic can't be shared with different parts of the codebase. Guards' power is they're simple and readable. Guards are useful when working at high resolution such as authorising access to sensitive fields like date_of_birth or email , where the authorisation is too granular to publish in practice.

A guard example adopted from TypeGraphQL.

The rest…

For the remaining server code see the GitHub repository for this post.

What we didn’t cover

Using DataLoader to solve the n+1 problem

to solve the n+1 problem Using a database

How to serve a GraphQL server over HTTP (view the Source Code for an example)

Hosting this example server

With Docker

With Docker Compose