Building a RESTful API using Express and TypeScript

Elliot Forbes Elliot Forbes ⏰ 6 Minutes 📅 Mar 28, 2018

In this tutorial, we are going to be building a REST API using both express and TypeScript! This REST API will simply return a status depending on what API endpoint we hit using our HTTP Client.

Prerequisites

You will require at least the following:

  • TypeScript 2.8.1
  • The Yarn Package Manager
  • ExpressJS

Our Project Layout

So within our project, we are going to create a couple of incredibly simple endpoints that will simply return a few simple strings, depending on what HTTP verb is used to hit said route.

We’ll be using the tsc compiler to transpile our TypeScript code back to normal JavaScript and then we’ll be running this built code using nodemon.

Package.json

We’ll need to start by initializing our project using the npm init command. This will generate the initial version of our package.json and allow us to specify any dependencies and scripts our project may need. Once you’ve run through this basic initialization, let’s add a few scripts to our package.json that will enable us to compile and run our TypeScript application:

"scripts": {
    "watch-ts": "tsc -w",
    "watch-node": "nodemon dist/server.js",
    "watch": "concurrently -k -p \"[{name}]\" -n \"TypeScript, Node\" -c \"yello.bold, cyan.bold\" \"yarn run watch-ts\" \"yarn run watch-node\""
}

The key one to note is the watch script which will concurrently watch our typescript application for changes and subsequently run the built server.js file using nodemon.

{
  "name": "express-api",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "watch-ts": "tsc -w",
    "watch-node": "nodemon dist/server.js",
    "watch": "concurrently -k -p \"[{name}]\" -n \"TypeScript, Node\" -c \"yello.bold, cyan.bold\" \"yarn run watch-ts\" \"yarn run watch-node\""
  },
  "dependencies": {
    "@types/express": "^4.11.1",
    "concurrently": "^3.5.1",
    "express": "^4.16.3"
  }
}

Should we try to run our watch script by calling npm run watch, you should notice it fails. This is because we haven’t yet specified our tsconfig.json and as such our tsc -W fails to execute.

Our tsconfig.json

Create a new tsconfig.json file within your root directory and add the following configuration:

{
  "compilerOptions": {
    "module": "commonjs",
    "esModuleInterop": false,
    "target": "es6",
    "noImplicitAny": true,
    "moduleResolution": "node",
    "sourceMap": true,
    "outDir": "dist",
    "baseUrl": ".",
    "paths": {
      "*": ["node_modules/", "src/types/*"]
    }
  },
  "include": ["src/**/*"]
}

The key things to note in this tsconfig.json file are that we are using commonjs to resolve our node modules. We are also targetting es6 as the ECMAScript version we wish to transpile our TypeScript code down to. outDir specifies the directory that our built JavaScript files will be pushed into and then paths specifies where to resolve the node_modules and the types that our project will require.

Finally, within our include array, we specify all of the locations in which we will be building up our project. For the purpose of this tutorial, we’ll be adding all of our code to a src directory and we want any .ts files underneath that directory to be included when our project is being rebuilt.

Initial Dependencies

Before we can start coding up our REST API, we’ll need to install any dependencies we may be using. To do this, call the following npm install command:

$ npm install express @types/express --save-dev

Now that we have these included within our project, we can start programming!

Our app.ts File

Let’s begin programming by creating our app.ts file. Within this we are going to import the express node module and then set up our express server. This includes setting up a simple endpoint within our application that will simple return Hi whenever that route is hit.

import express = require("express");

// Our Express APP config
const app = express();
app.set("port", process.env.PORT || 3000);

// API Endpoints
app.get("/", (req, res) => {
  res.send("Hi");
});

// export our app
export default app;

Our server.ts File

Our /src/server.ts file will act as the entry point for our REST API. This will kick off our application and start listening for incoming requests. We’ll first want to import the express application that we’ve defined within our app.ts file and then start up our server by calling app.listen, just as you normally would with a traditional ExpressJS server.

import app from "./app";

const server = app.listen(app.get("port"), () => {
  console.log(
    "App is running on http://localhost:%d in %s mode",
    app.get("port"),
    app.get("env")
  );
});

export default server;

With this now added to our project, we can check that everything is working as expected by calling npm run watch and checking to see if our application builds and starts as we would expect it to. Once you have kicked off this watch script, you should be able to navigate to http://localhost:3000/ within your browser or with your REST client of choice, and it should deliver Hi back to you. Awesome, we now have the base upon which we will be building the rest of our RESTful API!

Our Status Controller

Now that we have our base API, we want to expand upon it a bit and create a few new API endpoints that we can interact with. We will want to create a new status controller that will return different status strings depending on what API endpoint is called. We’ll call this ./src/controllers/status.ts within our application and we will add the following:

import { Request, Response } from "express";

export let hi = (req: Request, res: Response) => {
  res.send("hello");
};

export let hello = (req: Request, res: Response) => {
  res.send("how's it going?");
};

export let awesome = (req: Request, res: Response) => {
  res.send("EVERYTHING IS AWESOME");
};

Running our Build Server

In order to run our REST API locally, you can run the watch script like so:

$ yarn run watch

This will continuously watch for changes and serve our application on port 3000 like so:

0:23:48 - File change detected. Starting incremental compilation...
[TypeScript]
[TypeScript]
[TypeScript] 20:23:48 - Compilation complete. Watching for file changes.
[TypeScript]
[TypeScript]
[ Node] [nodemon] restarting due to changes...
[ Node] [nodemon] starting `node dist/server.js`
[ Node] App is running on http://localhost:3000 in development mode

Writing our Mocha Test Suite

Now that we’ve written our simple TypeScript based RESTful API, we need to be able to programatically check that it works! What good is a production bit of software if we don’t have a test suite for it!

In order to test this REST API, we’ll be using mocha and chai. You can install these dependencies like so:

npm install chai mocha ts-node @types/chai @types/mocha chai-http @types/chai-http typescript --save-dev

Next, create a file called ./src/app.spec.ts and add the following:

import app from "./app";
import * as chai from "chai";
import chaiHttp = require("chai-http");

import "mocha";

chai.use(chaiHttp);
const expect = chai.expect;

describe("Hello API Request", () => {
  it("should return hello on call", async () => {
    return chai
      .request(app)
      .get("/")
      .then(res => {
        chai.expect(res.text).to.eql("hello");
      });
  });
});

You will also have to update the package.json to include a new script that will run our mocha tests for us:

"test": "mocha -r ts-node/register src/**/*.spec.ts"

Now that we’ve create a simple test and updated our package.json, try running our incredible new test suite by calling npm run test:

➜  express-api npm run test

> express-api@1.0.0 test /Users/elliot/Documents/Projects/tutorials/typescript/express-api
> mocha -r ts-node/register src/**/*.spec.ts



  Hello API Request
    ✓ should return hello on call


  1 passing (68ms)

And Voila! We now have a working test suite that we can expand out and ensure that everything we write now works!

Conclusion

Hopefully, you have found this tutorial useful. We managed to set up an entire RESTful API written in TypeScript that automatically rebuilds whenever we make any changes to our code.