Peter Banjo
Soliloquy

Soliloquy

Starting with a health route

Starting with a health route

Peter Banjo's photo
Peter Banjo
ยทApr 21, 2022ยท

3 min read

Listen to this article

Why a health route?

I want to begin with the end in mind. When our API is deployed in a cloud environment, the automated, cloud deployment will need a way to know that our API has started correctly. That is the purpose of the GET /health route.

I have created a workspace on Codesandbox.io with the completed code. And I'll walk you through the code.

If you want to skip ahead, you can take a look at the "Getting Started" section in the README.md.

Acceptance criteria โœ…

Well written tests are like documentation that is always in sync with operational code. So I like to use a style called Acceptance criteria from Behaviour-Driven Design (BDD) when describing my tests. This takes the format of -

  • Given: the initial context at the beginning of the scenario, in one or more clauses;
  • When: the event that triggers the scenario;
  • Then: the expected outcome, in one or more clauses.

Red ๐Ÿ”ด

The entry point of our application is index.ts. A typical NodeJs server is started with a command like node ./build/index.js. I follow the pattern of placing test files next to the code they test. This makes them easier to find and shows which files are missing tests.

index.test.ts contains -

// helper method to make a HTTP request
const asyncGet = (
  options: http.RequestOptions
): Promise<http.IncomingMessage> =>
  new Promise((resolve, reject) => {
    http.get(options, (res) => resolve(res));
  });

describe(`Given start`, () => {
  let server: FastifyInstance;
  let port: number;

  // SETUP
  beforeAll(async () => {
    // generate a random port number for the server
    port = await getPort({ port: getPort.makeRange(3000, 3100) });
    // configure the server from outside using params
    // this makes it much easier to test
    server = await start({ port });
  });

  // CLEAN-UP
  afterAll(async () => {
    await server.close();
  });

  // ACT
  test(`Server is listening`, async () => {
    // make a request to the server port
    // does it work?
    const res = await asyncGet({
      hostname: 'localhost',
      port,
      path: '/health',
    });

    // check the response code
    expect(res.statusCode).toEqual(200);
  });
});

Green ๐ŸŸข

The minimum viable code to get the tests to pass is a simple route to respond to GET '/health'

import fastify from 'fastify';

// for testing
export async function start(options: { port: number }) {
  const server = fastify({ logger: true });

  // basic health response can be anything
  server.get('/health', async () => 'OK');
  const { port } = options;

  await server.listen(port);

  return server;
}

// so we can node ./build/index.js to start the server
(async () => start({ port: Number(process.env.PORT) }))();

Refactor

There isn't anything to do here since the code is so simple. But in the next post, ๐Ÿ“ฎ we'll add more routes that will require some refactoring.

ย 
Share this