Microservices with GraphQL and Typescript Tutorial

Introduction

This article is intended for those who are looking to start building microservices into their projects.  I’m using Typescript for this particular example, but you can use any language you would like since microservices is just a design pattern and could be applied to any language.

Github Repository

If you are just here for code, I’ve published a boilerplate that is complete with migrations, linting, and realtime debugging (if you’re using VSCode).  The repository can be found here.

Tech Stack

Why Typescript?  Well, it brings some much-needed sanity to Javascript, integrates beautifully with VSCode, and can prevent hard-to-find bugs due to its strict nature.  For those reasons, I have started using Typescript in place of Javascript in all of my projects.  An added bonus of Typescript is that it can import and be imported by vanilla Javascript, allowing you to start using Typescript in any existing Javascript project simply by adding a loader to your webpack configuration.

Why GraphQL?  Mostly because I love using GraphQL for microservices, I’ve found that it greatly reduces the amount of code required for both the client and API compared to a more traditional RESTful design.  That being said, you could totally go with a RESTful setup instead and it would be perfectly acceptable.

The repository I’ve linked to includes linting, migrations, and realtime debugging via VSCode remote containers.  I will not be covering those bits of functionality in this article since they are not directly part of the microservice they are just things that make developing a little nicer.

Let’s Get Into Some Code

Alright, so we understand the reasoning behind the stack for this project let’s start building.  The first bit of code we need to write is in our webpack.config.js file.  You’ll need to define a new rule for TS and TSX files like so:

{
  test: /\.tsx?$/,
  use: 'ts-loader',
  exclude: /node_modules/
}

The full webpack.config.js file can be found on the Github repository.

The next thing we will want to do is spin up our express server and define the GraphQL endpoint using ApolloServer, this can be done like so:

import cors from 'cors';
import { ApolloServer, gql, makeExecutableSchema } from 'apollo-server-express';
import rawSchema from './schema';
import rootResolver from './resolvers';
const express = require('express');
const cookieParser = require('cookie-parser');
const logger = require('morgan');

const app = express();

const corsOptions = {
  origin: process.env.CLIENT_URL,
  credentials: true,
  methods: ['GET', 'OPTIONS', 'POST', 'PUT', 'HEAD']
};

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({extended: false}));
app.use(cookieParser());
app.use(cors(corsOptions));

app.get('/', function(req: object, res: {json: Function}, next: Function) {
  res.json({
    api: process.env.APP_NAME,
    health: 'healthy'
  });
});

const schema = makeExecutableSchema({
  typeDefs: gql(rawSchema),
  resolvers: rootResolver,
});

const apolloServer = new ApolloServer({ schema });

apolloServer.applyMiddleware({
  path: '/graphql',
  app
});

To make sure things are working, start the webserver (docker-compose up if you’re using the provided repository) and navigate to localhost:4004 in your browser.  If things are working, you should see a healthy response since ‘/’ is a health check endpoint by default.

At this point, our work is pretty much done, all that’s left is writing our GraphQL schema and defining resolvers.  A quick example of what that might look like is as follows:

export default `
type Query {
  helloWorld: Int!
}

type Mutation {
  helloWorld(message: String!): Int!
}
`;

Now, defining resolvers can be done in a lot of different ways, but essentially you just need an object with keys for each resolver function.  I personally like to structure my resolvers like so:

graphql/
├── resolvers/
│   ├── mutations/
|   │   └── myMutation.ts
│   ├── queries/
|   │   └── myQuery.ts
|   └── index.ts

That way, I can easily load them all at once at compile time from index.ts by making use of webpack’s require.context like so:

import { IResolvers } from "graphql-tools";

const requireQueries = require.context('./queries', true, /\.js$/);
const requireMutations = require.context('./mutations', true, /\.js$/);

interface Resolvers {
  Query: IResolvers<any, any>
  Mutation: IResolvers<any, any>
}

const exportedFunctions: Resolvers = {
  Query: {},
  Mutation: {},
};

requireQueries.keys().forEach((file: string) => {
  const queryName = file.replace(/\.\//, '').replace(/\.js/, '');
  const queryFunc = requireQueries(file).default;
  exportedFunctions.Query[queryName] = queryFunc;
});

requireMutations.keys().forEach((file: string) => {
  const mutationName = file.replace(/\.\//, '').replace(/\.js/, '');
  const mutationFunc = requireMutations(file).default;
  exportedFunctions.Mutation[mutationName] = mutationFunc;
});

export default exportedFunctions;

From there, you’d just need to import the index.ts file and pass it to makeExecutableSchema, this can be seen in src/graphql/index.ts

SHARE ON

The Author: Zach

Microservices with GraphQL and Typescript Tutorial

You May Also Like

Leave a Reply

Your email address will not be published.