Santhosh
Santhosh
Code with passion
Jul 23, 2022 4 min read

Generate Nodejs Typescript client to access Graphql API

While working with Graphql in microservice environment it’s a common use case to access the graphql endpoints from a nodejs service. In this blog we’ll look at how to do this in a typesafe manner leveraging whole benefits of using graphql with typescript.

Lets setup a DX for backend graphql client usage that supports typescript. Tech stack:

We are using Hasura here just to speed up the process, you can replace it with whatever graphql server you have.

Use this Github example project with same setup as explained in this post as reference.

1. Setup a express typescript app

Setup a simple typescript express app. create a folder, npm init and npm install the following dependencies

npm i express 
npm i -d typescript ts-node nodemon @types/node @types/express

Create tsconfig file in root.

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es6",
    "rootDir": "./",
    "esModuleInterop": true
  }
}

Create a index.ts entry file:

import express from 'express';

const app = express();
const port = 3000;

app.listen(port, () => {
  console.log(`Application is running on port ${port}.`);
});

Add a start script in package.json. (Using nodemon to restart server on changes)

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "nodemon index.ts",
}

run npm start and verify the success console message

2. Setup Hasura Graphql Engine and create tables

Follow the Hasura official getting started doc to create app and create following tables projects -> issues.

schema-diagram

Alternatively you can clone the repo here for the example project representing this post and run Hasura docker locally. Above schema is already setup in this and ready to run with:

npm run hasura

assuming docker-compose is already installed in your system

3. Setup Graphql-Request client & Typescript codegen

  1. Npm install the dependencies.

We’re using the following plugins:

  • codegen/cli - core cli to generate files.
  • typescript & typescript-operations - Typescript core files and operations
  • typescript-graphql-request - sdk generator for graphql-request client library
npm i graphql-request graphql-tag
npm i -D @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-graphql-request @graphql-codegen/typescript-operations
  1. create codegen.js file:
const dotenv = require('dotenv')

dotenv.config()
module.exports = {
    schema: [
        {
            'http://localhost:8080/v1/graphql': {
                headers: {
                    'x-hasura-admin-secret':  process.env.ADMIN_SECRET,
                },
            },
        },
    ],
    documents: ['./**/*.gql.ts'],
    overwrite: true,
    generates: {
        './src/generated/sdk.ts': {
            plugins: [
                'typescript',
                'typescript-operations',
                'typescript-graphql-request',
            ],
        },
    },
};
  1. Update package.json > scripts with codegen generate script:
    "generate": "graphql-codegen --config codegen.js"
  1. Let’s Write some graphql queries and generate ts files

create a queries.gql.ts with following gql query to create and retrieve projects in hasura.

import gql from 'graphql-tag';

gql`mutation createProjects($projects: [projects_insert_input!]!) {
  insert_projects(objects: $projects) {
    returning {
      id
      name
    }
  }
}`

gql`query getProjects {
  projects{
    id
    name
  }
}`

We can test the queries in the Hasura api console like below:

hasura-consoel-image

  1. Run codegen and generate typescript files.
npm run generate

Cli should give success message like below and the generated files would be there in ./src/generated folder as per our codegen.js config. cli

  1. Create api routes to create and retrieve projects
import express from 'express';
import { GraphQLClient } from 'graphql-request'
import { getSdk } from './generated/sdk' // THIS FILE IS THE GENERATED FILE
import dotenv from 'dotenv'

dotenv.config()
const app = express();
const port = 3000;
const endpoint = 'http://localhost:8080/v1/graphql'

const graphQLClient = new GraphQLClient(endpoint, {
    headers: {
        'x-hasura-admin-secret':  process.env.ADMIN_SECRET,
    },
})
const sdk = getSdk(graphQLClient)


app.get('/api/projects', async(req, res) => {
    const data = await sdk.getProjects()
    res.send(data)
})

app.post('/api/projects', async(req, res) => {
    const projects = req.body;
    const createdProjects = await sdk.createProjects(projects);
    res.send(createdProjects)
})


app.listen(port, () => {
  console.log(`Application is running on port ${port}.`);
});

create a .env file in root to use ADMIN_SECRET as env if you’ve a admin secret configured in Hasura engine.

ADMIN_SECRET=myadminsecretkey

run npm start and tryout the apis. Try accessing the properties in response projects with intellisense suggestions. Enjoy the Typesafety!

Codegen will help you whenever there’s a breaking change in graphql schema we can use prestart or some git hooks like husky to generate types using graphql codegen to ensure we check this time to time.

No more run time error because of a rename in a api field 😄