Setting Up a Node & TypeScript Project - Setup Express

Published on

There are quite a few libraries/frameworks in the NodeJS world for building web applications. We’re going to take a look at one of the more popular ones, Express, and setup up our project to use Expresss with TypeScript. There is a plethora of ways to set up Express, but this is how I like to set it up.

 

Prerequisites

 

Installing Our Dependencies

Lets get started by setting up our project with Express. The first thing we need to do is install our dependencies. We will install dotenv, express, cors, and body-parser along with their associated @types packages since we are using TypeScript. You can install any additional middleware you need at this time as well.

npm install --save dotenv express cors body-parser
npm install --save-dev @types/express @types/cors @types/body-parser

The dotenv package allows us to load environment variables into Nodes process.env. First we will create a .env file at the root of our project which will contain all of our environment properties. We will only be adding two properties right now: NODE_SERVER_HOSTNAME and NODE_SERVER_PORT.

# Creates the .env file
touch .env 

And add our properties to the .env file…

# Server Properties
NODE_SERVER_HOSTNAME=localhost
NODE_SERVER_PORT=3000

 

Creating Our File Structure

So everything is setup, now how do we structure our project? In the Node/Javascript world everyone has their own way of doing things and there doesn’t seem to be a standard way of doing anything. Since I come from the .NET / Java world, I tend to follow a fairly structured MVC type pattern. In our src directory we will create an app directory containing directories for each layer of our application: controllers, middlewares, models, services, repositories, etc. Each directory will contain a barrel file to keep imports clean. If you are not familiar with barrel files you can learn more about them here. Using barrel files is a little more maintenance but I prefer the shorter, cleaner paths and multi-value imports for our import statements.

Lets creates our directories…

mkdir src/app/controllers \
  src/app/middlewares \
  src/app/models \
  src/app/repositories \
  src/app/routes \
  src/app/services 

And our barrel files…

touch src/app/index.ts \
  src/app/controllers/index.ts \
  src/app/middlewares/index.ts \
  src/app/models/index.ts \
  src/app/repositories/index.ts \
  src/app/routes/index.ts \
  src/app/services/index.ts

 

Creating Our Server

We have our file structure setup, now lets create our http server. Create an application.ts file in our app directory. In this file we will use ES6’s class syntax to create an Application class.

touch src/app/application.ts
import express, { Express } from 'express';
import bodyParser from 'body-parser';
import cors from 'cors';

export class Application {
  private _server: Express;
  
  constructor() {
    this._server = express();
    this._server.set('host', process.env.HOST || 'localhost');
    this._server.set('port', process.env.PORT || 3000);
    this._server.use(bodyParser.json());
    this._server.use(bodyParser.urlencoded({ extended: true }));
    this._server.use(cors());
  }

  public startServer(): void {
    const host: string = this._server.get('host');
    const port: number = this._server.get('port');
    this._server.listen(port, host, () => {
      console.log(`Server started at http://${host}:${port}`);
    });
  }
}

The class is pretty simple. We intialize our Express server in our constructor and setup a few middleware. We also have a startServer() method that starts our HTTP server to process incomming HTTP requests.

We also need to export our Application class in our app/ directory’s barrel file, src/app/index.ts.

export * from './application';

And finally we will import our Application in our node entry point file (src/index.ts), configure our environemt, create an instance of our Application, and start the application server.

import { Application } from './app';
import * as dotenv from 'dotenv';

dotenv.config();

const application: Application = new Application();
application.startServer();

Now we can run our server by running npm start, which should print to the console Server started at http://localhost:3000. So the server is running but we don’t have any routes to access yet. So lets first create a controller and then a router to route our request to our controller.

 

Creating a REST Endpoint

We going to create an endpoint to get all Users in our application. The users will be just a User[] stored in our UsersController. Create a file in your controllers directory, touch src/app/controllers/users.controller.ts and update our barrel file, src/app/controllers/index.ts.

export * from './users.controller';

And our controller will be an ES6 class with a single method that gets an array of User.

import { Request, Response, NextFunction } from 'express';
import { User } from '../models';

export class UsersController {
  private readonly users: User[];

  constructor() {
    this.users = [
      new User('jmw5598', 'Jason', 'White'),
      new User('djt2020', 'Daniel', 'Townswell'),
      new User('dlw3512', 'Danielle', 'Whitmore'),
    ];
  }

  public async getAllUsers(request: Request, response: Response, next: NextFunction): Promise<any> {
    return response.status(200).send(this.users);
  }
}

We will need to create our model class to model our users. Create a user model, touch src/app/models/user.model.ts and update our barrel file, src/app/models/index.ts.

export * from './user.model';

And our user model class…

export class User {
  constructor(
    public username: string, 
    public firstname: string, 
    public lastname: string
  ) {}
}

Now we need to create an Express router to route requests to our controller and then register our router with our application. Create a file for our application routes, touch src/app/routes/application.router.ts. Then create a file for our user endpoint routes touch src/app/routes/users.router.ts. And finally update our barrel file src/app/routes/index.ts.

export * from './application.router';

Our root application router…

import express, { Router } from 'express';
import { usersRouter } from './users.router';

const router: Router = express.Router();
router.use('/users', usersRouter);

/*
  Here we can add addition endpoints to our root application router.

  For example, if we wanted accounts endpoint, we would
  create an accounts.router.ts similar to how we created
  our users.router.ts and map it to '/accounts'.

  router.use('/accounts', accountsRouter);
*/

export const applicationRouter: Router = router;

And our users router…

import express, { Request, Response, Router, NextFunction } from 'express';
import { UsersController } from '../controllers';
import { IRepository, UsersRepository } from '../repositories';
import { User } from '../data';

const router: Router = express.Router();
const controller: UsersController = new UsersController();

router.get('/', async (request: Request, response: Response, next: NextFunction) => {
  await controller.getAllUsers(request, response, next);
});

export const usersRouter: Router = router;

Now we can modify our application to use our application router. We will update our Application class to import our application router and register it as middleware in the constructor.

import express, { Express } from 'express';
import bodyParser from 'body-parser';
import cors from 'cors';
import { applicationRouter } from './routes';  // Import our application router

export class Application {
  private _server: Express;

  constructor() {
    this._server = express();
    this._server.set('host', process.env.HOST || 'localhost');
    this._server.set('port', process.env.PORT || 3000);
    this._server.use(bodyParser.json());
    this._server.use(bodyParser.urlencoded({ extended: true }));
    this._server.use(cors());
    this._server.use(applicationRouter);  // Register our application router
  }

  public startServer(): void {
    const host: string = this._server.get('host');
    const port: number = this._server.get('port');
    this._server.listen(port, host, () => {
      console.log(`Server started at http://${host}:${port}`);
    });
  }
}

Let start our server, npm start. Open up a browser or rest client and make a GET request to http://localhost:3000/users. You should get back a response similar to what we have below.

Curl output for /users endpoint

Next we will be setting up TypeORM with a SQLite database.

 

The completed github repository can be found here

 

 

comments powered by Disqus