iagobruno / graphql-server-with-typescript

A simple GraphQL server using NodeJS and TypeScript.

Home Page:https://graphql-server-with-typescript.httpiago.now.sh/graphql

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

graphql-server-with-typescript

A simple GraphQL server using NodeJS and TypeScript.

Online playground Known Vulnerabilities

About

Currently, in 2019, to create a GraphQL API it's necessary to write a schema with the types of inputs and outputs of server, which is usually defined in a text file called schema.graphql. That is not a problem for applications that use pure JavaScript, but it can create a headache if you want to use TypeScript to ensure code type-safety, because TS-compiler can't infer typings inside text file and the developer need to manually sync the GraphQL schema with TypeScript types and this is NOT productive.

Solutions

Some time ago, I tried to create a server using a package called "type-graphql" which aims to automate the creation of the schema based on code writed using decorators. I reached my goal and I was pleased with the result, but now I found some problems looking at the code: decorators are not a standard yet, the code gets more verbose and difficult for other developers to maintain and the package in question still needs to be improved.

Anyway, with this project I want to try the reverse approach: automate the creation of code typings based on GraphQL schema and this time I will use another package called "graphql-codegen".

Installation

git clone https://github.com/iagobruno/graphql-server-with-typescript.git
cd graphql-server-with-typescript
yarn install

and start the server:

yarn run dev

Workflow

flowchart

Security

Some security barriers have been implemented on this server to prevent abuse and malicious queries:

Query deep limit

There is a limit to how deep a query can be and if it is greater than 5, an error is returned. See implementation. Here's an example of how to calculate depth:

query GetUserAndHisTweets { // 0
  user(id: "5") { // 1
    username
    photo
    tweets { // 2
      edges { // 3
        node { // 4
          content
          createdAt
        }
      }
    }
  }
}

Query complexity limit

Some properties and mutations are more expensive than others and it's a good practice assign a complexity cost to them so the server can sum and check if queries exceeded the cost limit before they are actually executed. See implementation and how to define the cost.

Authentication System

All mutations require a JWT token to determine which user is trying to perform the action. To acquire this token you need to call the auth mutation. See how to generate the token and how to verify if there is an authenticated user in the request.

Token access permissions

It's possible define which places a token is allowed access and prevent all generated tokens from having access to everything in the api. See how to define token scope and how to check if they have access.

Only allow access to certain types of users

Another good practice is to determine what type of user has access to a certain part of server, for example, only a moderator can approving posts and only an administrator can banning users. This file contains all available checks and here is an example usage.

Pagination

To traverse the connections, a cursor-based pagination was implemented on this server. Example:

query GetLatestTweets {
  tweets(first: 20, after: "cursor") {
    edges {
      cursor
      node {
        id
        content
        createdAt
      }
    }
    pageInfos {
      endCursor
      hasNextPage
      hasPreviousPage
    }
  }
}

You can read more about GraphQL connections here.

Custom scalars

The package graphql-scalars has been installed to provides some custom scalars to make explicit what the field is about and also validate the inputs.

To add a new scalar, just set it in Root.graphql:

 scalar DateTime
 scalar URL
+scalar PositiveInt
....

Register its resolver in the resolvers/index.ts file:

-import { DateTimeResolver, URLResolver } from 'graphql-scalars'
+import { DateTimeResolver, URLResolver, PositiveIntResolver } from 'graphql-scalars'

const customScalarsResolvers = {
   DateTime: DateTimeResolver,
   URL: URLResolver,
   // The key must have the same scalar name defined in the previous step.
+  PositiveInt: PositiveIntResolver,
}
...

And define its type (valid in TypeScript) in this configuration file so graphql-codegen can correctly create type of resolvers (if you don't do this step, the type of arguments and fields with custom scalar will be "any").

...
    config:
      scalars:
        DateTime: Date
        URL: string
+       PositiveInt: number
...

Inspirations

About

A simple GraphQL server using NodeJS and TypeScript.

https://graphql-server-with-typescript.httpiago.now.sh/graphql

License:MIT License


Languages

Language:TypeScript 100.0%