Creating a gateway service with node js, typescript and fastify.

In this article we are going to create a gateway service that will serve all future micro-services that we will learn to create.

Image for post
Image for post

What is a gateway?

An API gateway service is an infrastructure layer that sits at the front of all your back-end micro-services. Its specific purpose is to serve requests from the client by routing it to the correct back-end micro-service. In other words it is a node network, a key stopping point for data on its way to or from other networks. Thanks to gateways, we are able to communicate and send data back and forth without disclosing the location of other services.

Why use gateway services?

  • Prevents exposing internal concerns to external clients
  • Adds an additional layer of security to your micro-services
  • Enables support for mixing communication protocols
  • Decreased micro-service complexity

What architecture are we using when creating the gateway?

Image for post
Image for post
types of architectures

We are going to make use of the micro-services architecture.This term was introduced early 2005 by Peter Rodgers.

Let us get started…

Why typescript?

Typescript simplifies JavaScript code, making it consistent, easier to read, debug, and clean especially if you are working on a project as a team. It is also easily maintained. Basically typescript is JavaScript with superpowers.

So, what are we going to do in this article?

  • Create a server
  • Document our gateway with swagger.
  • Setup docker in the application
  • Setup Jenkins in the application

Setting up fastify project with typescript

Create a new fastify project by creating a new project directory. In the project directory initialize your fastify application.

$ mkdir gateway
$ cd ./gateway
$ npm init

Now let us create install all required packages for our gateway service

Create a tsconfig.json in the root folder of our application. The file allows you to specify the root level files and the compiler options that requires to compile a Typescript project. The presence of this file in a directory specifies that the said directory is the Typescript project root.

Next create a .env file with some variables as a function configuration, bear in mind that it is not recommended as a way to store secrets such as database credentials or API keys.

PORT=2400
NODE_ENV=development
PROJECT_NAME=gateway

Now let us create a folder structure for our application. The config folder contains all configurations that will be used within the application. The public folder will be used to hold all assets that will be accessible by the public, but in this case it is not necessary (just a force of habit for me). The src folder is where we will write all our code in.

Image for post
Image for post

Let us create an interface that we will use in our gateway routes schema. It will also help a lot during documentation leaving clean, readable and maintainable code. Create a routeschema.ts file inside the @types folder

Create a server.ts file ( we obviously use .ts extension since we are coding in typescript ). Create a fastify app instance in the file and pass the relevant typings for our HTTP version used <Server, IncomingMessage, ServerResponse>. By passing types we get correctly typed access to the underlying HTTP objects in routes.

If you are new to fastify, you can have a look at their documentation and how to setup a simple typescript server on their site https://www.fastify.io/.

Next let us add swagger into our project and the amazing this is fastify already has you sorted with an amazing package fastify-swagger.

The final swagger documentation ui will have the bellow outlook.

Image for post
Image for post

Now let us create some reusable utilities for our application.

To start with lets create type definitions for our requests. Create a Requests class with a requestBody method which accepts two arguments. itemInterface to handle all objects passed in a body request and a requiredValues to pass a list of compulsory fields

Now for our responses, we need to have types for our error response object which will need require status code, message and error. Our success response object will need just a single argument with array of data which will make use of the itemInterface discussed above.

Up to this point we are 90% there. what we need to do is actually wire the form data we receive in our body to respective micro-service.

What do we need to know to do so?

  • the micro-service project name
  • the micro-service IP or domain
  • the port the micro-service is running on

In the routes folder create a folder called auth. This will hold all the code related to the auth micro-service in the file, lets create an auth.ts file. It would be much simple to just write

let user;
try {
consst user =
await axios.post('http://localhost:3000/api/v1/user/auth', req.body);
}catch(ex){
return response.code(400).send(ex)
}
response.send(user);

This solution is not the best since the most likely scenario is that you have got a dozen of micro-services that you intended to access with your gateway service. You may want to further advance the gateway to make use of permissions. If you want to know how to use permissions in the gateway get in touch at (jsisaacdev@gmail.com).

In our utilities, let us create a request maker that will handle all this in the background:

type APIs = | 'auth' ;interface Reqmaker{
service: APIs,
action: string,
method: Method,
data?: any,
headers?: Object,
}
export default function({service, action, method, data = '', headers = {} }: Reqmaker){
return new Promise( (resolve, reject) => {
const url = ((services.find((el) => el.name === service )).url) + '/' + action;
axios({
url: url,
method: method,
headers: headers,
data: data
}).then((response) => {
resolve(response.data)
}).catch((error) => {
console.log('Error', error.message);
});
});
}

The request maker will decide which URL to use by service name provided

Back to our auth.ts: let us create a user interface and declare types for our form data. If you read the typescript fastify docs above, you now have knowledge on how to create a schema for our endpoint, here we are going to make it neater.

Make sure the tag used is the same as the tag name used in the server.ts swagger tags array. This helps in the grouping of services in the gateway swagger UI.

tags: [
{ name: 'Auth', description: 'Auth related endpoints' }
]

In the body, let us make use of requests utility. Pass the user interface as an argument and an array of required fields. In our responses lets rewrite the status codes using our responses utility. Now change the axios example above to:

let user;
try {
user = await requestmaker({
service: 'auth',
action: 'user/auth',
method: 'POST',
data: request.body
})
} catch (ex) {
return response.code(400).send(ex)
}
response.send(user);

Final code:

What on earth are those index.ts files that we have not yet gotten to use? well, you need a file to point to your routes: the index file in the auth folder

import userRoute from './user';
import authRoute from './auth';
export default (app: fastify.FastifyInstance) => {
app.register(userRoute, { prefix: '/api/v1/user' });
app.register(authRoute, { prefix: '/api/v1/user' });
};

The index in the routes folder register all your routes in the fastify app instance.

import * as fastify from 'fastify';
import authRoute from './v1/auth';
export default (app: fastify.FastifyInstance) => {
authRoute(app);
};

Can we now create a middleware for our application? The middleware will help authorize all requests in the application. Let us create an auth folder in our middleware. In the folder create an admin.ts file and use our request maker to authenticate tokens from the client. We will get to use this in later articles.

Now let us setup docker in our application

docker-compose-build.yml

Dockerfile-build

Github link: https://github.com/jayisaac0/gateway/tree/develop

Releases: https://github.com/jayisaac0/gateway/releases

Written by

I am an experienced back-end software engineer adept in bringing forth expertise in design, installation, testing and maintenance of software systems.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store