Building a NodeJS express backend with TypeScript

Building a NodeJS express backend with TypeScript

- 12 mins

NodeJS + Express + TypeScript

Initially, you’ll need to execute some steps before you can actually start coding your own site. A backend application manipulates the data and executes commands by endpoint call. This backend application will not be connected to a database. If you’d like to set up a DB connection in NodeJS I strongly recommend TypeORM.

Let’s start with initializing an npm project.

mkdir app-backend
cd app-backend
npm init

Just press ENTER a few times and you will be set to start. Now you should only have a package.json file in your folder. Moving on, let’s install the most necessary packages first.

npm i --save typescript @types/node express @types/express ts-node

ts-node is not required, it just makes life easier by not having to transpile and run your code manually but using it in order to automate that. Create a source directory and initialize a config file for our TypeScript project.

mkdir src
npx tsc --init

npx is packaged with npm for us to be able to run packages without globally installing them our system. This can be useful in a lot of cases. The output of this operation should be a tsconfig.json file in which we implement some changes:

{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    "outDir": "./build",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true
  }
}

We added a rootDir and an outDir to our project as well as changing the transpilation target to es6 instead of es5. Now we can start coding!

Create some files in the src directory such as server.ts router.ts and create the basic layers of a backend application excluding the repository since our app won’t use any DB connection that we implement. Your file structure should look like this:

user@machine: ~/app-backend$ tree src/
src/
├── controllers
├── router.ts
├── server.ts
└── services

2 directories, 2 files

Since our app is going to integrate Google Calendar we need to create a service first that creates our token for verification. Without this token, the application should not even start so it is a good strategy to start with this. Let’s start coding! First of all, create a GoogleCalendarAuthService.ts in the services folder.

import * as fs from "fs";
import { OAuth2Client } from "google-auth-library";
import { google } from "googleapis";
import * as util from "util";

const readFile = util.promisify(fs.readFile);

export class CalendarAuthService {

    public authorizedClient(): Promise<OAuth2Client> {
        return readFile("credentials.json").then((credentials) => {
            const { client_secret, client_id, redirect_uris } =
                JSON.parse(credentials.toString()).installed;
            const oAuth2Client = new google.auth.OAuth2(
                client_id, client_secret, redirect_uris[0]);
            return readFile("token.json").then((token) => {
                oAuth2Client.setCredentials(JSON.parse(token.toString()));
                return oAuth2Client;
            });
        }).catch((err) => {
            console.log("Authentication error:", err);
            throw err;
        });
    }

}

What the heck is this? You could ask, dear reader, but you will understand that in a minute. Just go first to this site, execute the required commands. After acquiring the credentials.json file from Google’s API site and running the index.js (set SCOPE there to calendar instead of calendar.readonly) to resolve the token.json file, you should upload these to your projects root folder. Now we should install some packages:

npm i --save googleapis google-auth-library util fs

Let’s dig into the code. We promisified the readFile method in order to be able to return back a client after the token was verified with the credentials as well. We return back an authorized client that can connect to Google’s calendar API, read and write events there. Let’s set up the server as well.

import express from "express";
import { OAuth2Client } from "google-auth-library";
import { CalendarAuthService } from "./services/CalendarAuthService";

export const app: express.Application = express();
const port: string | number = 3000;

const authService = new CalendarAuthService();

authService.authorizedClient().then((client: OAuth2Client) => {

    app.listen(port, () => {
        console.log(`Events App backend listening on port: ${port}`);
    });

}).catch((err) => {

    console.log("Authentication failed." + err);

});

After having acquired the client we can start our express server. Finally, we have an express application that can connect to Google’s Calendar API. We should now set up the service for acquiring the data from the calendar that we connected to, building the CalendarService.ts after having defined an Event model (Event.model.ts):

export class Event {
    id?: string | undefined;
    title?: string | undefined;
    date?: string | undefined;
    timeZone?: string | undefined;
    description: string | undefined;
    host: string | undefined;
    maxParticipants?: number | undefined;
    location: string | undefined;
}

This seems pretty straight-forward at first glance so we should create the service:

import { OAuth2Client } from "google-auth-library";
import { calendar_v3, google } from "googleapis";
import { Event } from "./Event.model";

export class CalendarService {

    private calendar: calendar_v3.Calendar;

    constructor(client: OAuth2Client) {
        this.calendar = google.calendar({ version: "v3", auth: client });
    }

    public getUpcomingEvents(): Promise<Event[]> {

        return this.calendar.events.list({
            calendarId: "primary",
            timeMin: (new Date()).toISOString(),
            // tslint:disable-next-line:object-literal-sort-keys
            singleEvents: true,
            orderBy: "startTime",
        }).then((res) => {
            const events = res.data.items;
            if (events && events.length > 0) {
                const myEvents: Event[] = [];
                events.map((event) => {
                    const myEvent: Event = new Event();
                    myEvent.id = event.id;
                    myEvent.description = event.description;
                    if(event.start) {
                        myEvent.date = event.start.dateTime;
                    }
                    myEvent.host = event.organizer.displayName;
                    myEvent.location = event.location;
                    myEvent.title = event.summary;
                    if (event.attendees) {
                        myEvent.maxParticipants = event.attendees.length + 1;
                    } else {
                        myEvent.maxParticipants = 1;
                    }
                    myEvents.push(myEvent);
                });
                return myEvents;
            } else {
                console.log("No upcoming events.");
                throw new Error();
            }
        }).catch((err) => {
            console.log("Error encountered during event listing: " + err);
            throw err;
        });
    }


}

We are giving the created client to the service to be able to instantiate a calendar. We are reading from the primary calendar of the user and returning errors when no events were found. Let’s build the controller layer:

import { CalendarService } from '../services/CalendarService';
import { Event } from '../services/Event.model';

export class CalendarController {

    constructor(private service: CalendarService) {}

    public getEvents(): Promise<Event[]> {
        return this.service.getUpcomingEvents();
    }

}

And finally create a router function that we will include in the server:

import { Router, Request, Response } from "express";
import { OAuth2Client } from "google-auth-library";
import { CalendarController } from "./controllers/CalendarController";
import { CalendarService } from "./services/CalendarService";

export function setUpRouter(client: OAuth2Client): Router {

    const router: Router = Router();
    const controller = new CalendarController(
        new CalendarService(client)
    );

    router.get('/events', (req: Request, res: Response) => {
        controller.getEvents().then((events) => {
            res.send(events).status(200);
        }).catch((err) => {
            res.send(err).status(503);
        })
    });

    return router;

}
import express from "express";
import { OAuth2Client } from "google-auth-library";
import { CalendarAuthService } from "./services/CalendarAuthService";
import { setUpRouter } from "./router";

export const app: express.Application = express();
const port: string | number = 3000;

const authService = new CalendarAuthService();

authService.authorizedClient().then((client: OAuth2Client) => {

    app.use(setUpRouter(client));

    app.listen(port, () => {
        console.log(`Events App backend listening on port: ${port}`);
    });

}).catch((err) => {

    console.log("Authentication failed." + err);

});

After all these steps your application structure should look like this ( I am excluding the node_modules directory on purpose ):

.
├── credentials.json
├── package.json
├── src
│   ├── controllers
│   │   └── CalendarController.ts
│   ├── router.ts
│   ├── server.ts
│   └── services
│       ├── CalendarAuthService.ts
│       ├── CalendarService.ts
│       └── Event.model.ts
├── token.json
└── tsconfig.json

3 directories, 10 files

Let’s set up the package.json file with the proper run script:

...
"scripts": {
    "start": "ts-node src/server.ts",
    ...
}
...

So now in the terminal, you can run npm start and your application should start listening on port 3000 and calling localhost:3000/events should return the events from your connected calendar.

@Regards, Alex

Alex Olar

Alex Olar

Christian, foodie, physicist, tech enthusiast

comments powered by Disqus
rss facebook twitter github gitlab youtube mail spotify lastfm instagram linkedin google google-plus pinterest medium vimeo stackoverflow reddit quora quora