Building a NodeJS express backend with TypeScript
- 12 minsNodeJS + 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.