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:
- Nodejs
- Typescript
- Express.js
- Graphql-request
- Hasura Graphql Engine
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.
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
- 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
- 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',
],
},
},
};
- Update package.json > scripts with codegen generate script:
"generate": "graphql-codegen --config codegen.js"
- 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:
- 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.
- 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 😄