Structuring an Express.js API.

Photo credit https://unsplash.com/photos/P_qvsF7Yodw

This tutorial is a follow-up tutorial for the tutorial I wrote on How to set up an Express.js API using Webpack and TypeScript.

The tutorial above covered how to set up the project, however, this one will cover how to structure the API in a way that allows for adding new versions of the API, good maintainability and clean code.

Prerequisites.

  • Node.js, Yarn or npm.
  • Knowledge of Express.js
  • This is a follow-up tutorial, so I will use code from the previous tutorial.

Here is the repo for the codebase for following along. The code for this tutorial is on the structure branch.

Steps.

  1. Clone this repo and install dependencies.
git clone https://github.com/john555/express-app-typescript.git
cd express-app-typescript
yarn install # or npm install

2. Start the app in dev mode using yarn start:dev.

3. Inside the src/ folder create a folder with the name api.

4. Inside the api/ folder create an index.ts file and a folder for controllers and routes. The directory structure for the src folder should look as shown below.

.
+- src
+- api
+- controllers/
+- routes/
-- index.ts
-- index.ts

5. Create an express app in src/api/index.ts as shown below.

import * as express from 'express';
const api = express();
// You may add api specific middlewares here
// TODO: move all controllers in the src/api/controllers folder
api.get('/', (req, res) => {
res.send({
message: 'Hello from the API',
});
});
export default api;

6. In src/index.ts, import the api above add it as a middleware scoped to all routes that start with ‘/api/v1’ as shown below.

...
import api from './api';
const app = express();
...
app.use('/api/v1', api);

Ensure the app is running. Open your browser and got to http://localhost:3000/api/v1/. You should see the following JSON response.

{"message":"Hello from the API"}

Now, Let’s place routes and controllers in separate folders.

7. Create a file named getMovies.ts in /src/api/controllers/ with the following code.

import { Request, Response } from 'express';
export function getMovies(req: Request, res: Response) {
res.send({
users:[
{id: 1, name: 'How to train your dragon' },
{id: 2, name: 'Queen of Katwe'},
]
});
}

8. Create a file named movieRouter.ts in /src/api/routes/ with the following contents.

import { Router } from 'express';
import { getMovies } from '../controllers/getMovies';
const movieRouter = Router();
movieRouter.get('/', getMovies);
export default movieRouter;

9. Now, let’s add the movie router above as middleware to our api in src/api/index.ts while replacing the existing route. Below is how the file should look like.

import * as express from 'express';
import movieRouter from './routes/movieRouter';
const api = express();
// You may add api specific middlewares here
// TODO: move all controllers in the src/api/controllers folder
// api.get('/', (req, res) => {
// res.send({
// message: 'Hello from the API',
// });
// });
api.use('/movies', movieRouter)
export default api;

10. Go to http://localhost:3000/api/v1/movies in your browser. You should see a response with movies.

11. Let’s restructure our routes such that we can import them like:

import { movieRouter } from './routes/';
  • Create a file named index.ts in src/api/routes/ with the following code.
export * from './movieRouter';
  • Inside src/api/routes/movieRouter.ts, convert the export for movieRouter to a named export.
...
export const movieRouter = Router();
...
  • Go to src/api/index.ts and change how we’re importing the movieRouter to:
...
import { movieRouter } from './routes/';
...

Go to http://localhost:3000/api/v1/movies in your browser. You should still see a response with movies.

That’s it!

Final thoughts.

Any middlewares like body-parser, loggers etc that would potentially be used in all versions of the API (or other kinda apps that you may want to add) should be added to the app instance in src/index.ts.