Add JWT REST API Authentication to Your Node.js/TypeScript Backend with TypeORM and SQLite3 Database

Add JWT REST API Authentication to Your Node.js/TypeScript Backend with TypeORM and SQLite3 Database

Angular 9 and Ionic 5 Chat App

In this tutorial, we'll learn how to create a REST API server for JWT authentication using Node.js (Nest.js) and TypeScript for our Angular 9/Ionic 5 chat application.

You'll learn what's an ORM and how to use TypeORM with TypeScript to access and work with a database You'll see what's JWT and how to use them to implement authentication for your REST APIs backend in TypeScript and Node using Nest.js and TypeORM for database.

These are all the tutorial parts:

This is the second part of our tutorial series for building a chat mobile application with Node.js, TypeScript, Ionic 5, and Angular 9. You'll learn:

  • How to set up TypeORM and create a database, TypeORM is the most mature ORM in TypeScript.
  • How to use SQLite database with Node and TypeScript. TypeORM supports all major databases like MySQL, PostgreSQL, MSSQL, Oracle, and MongoDB but for simplicity we'll use SQLite3.
  • How to create a TypeORM entity for working with the users SQLite database.
  • How to create a Nest.js service for working with the users database.
  • How to enable CORS in Node and TypeScript.

Note: Chatkit is the hosted chat service provided by Pusher which is now retired. You can either use your own hosted chat server with an open source solution like https://chatsdk.co/ which is based on Firebase or use PubNub Chat, an alternative paid service for Chatkit.

What's a TypeScript ORM

According to Wikipedia:

Object-relational mapping (ORM, O/RM, and O/R mapping tool) in computer science is a programming technique for converting data between incompatible type systems using object-oriented programming languages. This creates, in effect, a "virtual object database" that can be used from within the programming language.

An ORM is not specific to TypeScript but rather a general concept in software and web development. In this tutorial, we'll be using TypeORM, the most mature ORM for TypeScript.

TypeORM is an ORM for TypeScript and JavaScript (ES7, ES6, ES5). Supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle, SAP Hana, WebSQL databases. Works in NodeJS, Browser, Ionic, Cordova and Electron platforms.

Why Using SQLite

SQLite is a C-language library that implements a small, fast, self-contained, high-reliability, full-featured, SQL database engine. SQLite is the most used database engine in the world. SQLite is built into all mobile phones and most computers and comes bundled inside countless other applications that people use every day. More

The SQLite file format is stable, cross-platform, and backwards compatible and the developers pledge to keep it that way through at least the year 2050. SQLite database files are commonly used as containers to transfer rich content between systems and as a long-term archival format for data. There are over 1 trillion (1e12) SQLite databases in active use.

SQLite source code is in the public-domain and is free to everyone to use for any purpose.

For the sake of simplicity, we'll use an SQLite database, but TypeORM supports all major databases like MySQL, PostgreSQL, MSSQL, Oracle, and MongoDB.

Since an ORM abstracts away any direct operation with the underlying database system, you can later switch to use a fully fledged system like MySQL for production without changing anything in your code. But for now, let's keep it simple and use SQLite.

Setting up TypeORM and Creating a SQLite3 Database

For storing and registering users we need a database.

Nest.js supports TypeORM which is considered the most mature Object Relational Mapper (ORM) available in TypeScript. It's available from the @nestjs/typeorm package.

Let's start by installing the required dependencies:

$ npm install --save @nestjs/typeorm typeorm sqlite3

After finishing up with installing the dependencies, you need to import the TypeOrmModule into the root ApplicationModule module. Open the src/app.module.ts file and add the following changes:

// server/src/app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';

import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [
   TypeOrmModule.forRoot({
      type: 'sqlite',
      database: 'my.db',
      entities: [__dirname + '/**/*.entity{.ts,.js}'],
      synchronize: true,
   }),
],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

We import TypeOrmModule and we call the forRoot() method which takes the same configuration object as the standard createConnection() method of TypeORM.

In the configuration object, we specify:

  • The sqlite string for type so we can use SQLite as the database,
  • The my.db string for the database file (SQLite uses files to store the database),
  • The entities array which refers to all files that end with .entity.ts or .entity.js extensions. These files are created by developers and contain the ORM entities.
  • The synchronize option which takes true or false and allows you to automatically sync your database tables with the entities each time you run the app. In development, you can set it to true but it's not preferable in production.

Note: Now, you can inject the Connection and EntityManager services anywhere you want to access them.

Creating a TypeORM Entity for Working with Users

Next, let's create a User entity which corresponds to a user in the database. Create a src/models/user.entity.ts file and add the following class:

// server/src/models/user.entity.ts
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column()
  email: string;

  @Column()
  password: string;
}

You need to import the User entity and add it in the imports array of the module using the forFeature method:

// server/src/app.module.ts
import { User } from './models/user.entity';
...

@Module({
imports: [
...
TypeOrmModule.forFeature([User]),

Creating a Nest.js Service for Working with Users Database

Next, let's create a UserService that encapsulates all database operations that we need to perform against the User model.

Head back to your terminal and run the following command to generate a service:

$ nest g s user

This command will create the src/user/user.service.ts file that contains the actual service code and the src/user/user.service.spec.ts file that contains the unit tests for the service. And also update the src/app.module.ts file by including UserService in the providers array.

Next, let's add the create and findByEmail TypeScript methods in the src/user/user.service.ts file which will be used respectively to persist a user and find a user by its email in the database:

// server/src/user/user.service.ts
import { Injectable } from '@nestjs/common';
import { User } from '../models/user.entity';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';

@Injectable()
export class UserService {
    constructor(
        @InjectRepository(User)
        private userRepository: Repository<User>,
    ) { }

    async  findByEmail(email: string): Promise<User> {
        return await this.userRepository.findOne({
            where: {
                email: email,
            }
        });
    }

    async  create(user: User): Promise<User> {
        return await this.userRepository.save(user);
    }
}

First we import User, Repository and InjectRepository, next, inject the User repository via the service's constructor and finally we define our TypeScript methods.

The findByEmail method simply calls the findOne method of the injected repository to search for a user by the passed email in the database.

The create method calls the save method of the injected repository to save a user in the database.

Adding JWT Authentication with TypeScript and Node

Authentication is important for most web applications. You can follow different ways and approaches to implement user authentication. In this tutorial, we'll implement authentication with JSON Web Tokens (JWTs).

What's JWT

According to Wikipedia:

JSON Web Token (JWT) is an Internet standard for creating data with optional signature and/or optional encryption whose payload holds JSON that asserts some number of claims. The tokens are signed either using a private secret or a public/private key. For example, a server could generate a token that has the claim "logged in as admin" and provide that to a client. The client could then use that token to prove that it is logged in as admin.

Implementing JWT with Nest.js JWT Utilities Module

First, you need to install the JWT utilities module for Nest.js using :

$ npm install --save @nestjs/jwt

Next, open the /src/app.module.ts file and include the module in the imports array:

// server/src/app.module.ts
import { JwtModule } from  '@nestjs/jwt';
// [...]

JwtModule.register({
    secretOrPrivateKey:  'secret123'
})

We also provided a private secret key that will be used to sign the JWT payload.

To interact with our chat server, you also need valid JWT tokens that will be obtained by the client by using a token provider and will be sent with every request that the client makes to the chat server.

Chatkit provides a test token provider that can be used to quickly start testing the chat features but it should be only used for testing. For production, you need to create your own token provider which can be done in two ways:

  • Either, by using the provided server SDKs.
  • Or without the help of the server SDKs using a JWT library or your own custom JWT implementation.

In this tutorial, we'll use the Node.js SDK for Chatkit to add a token provider in our Node (Nest.js) project so head back to your terminal and run the following command from the root of your project to install it:

$ npm install @pusher/chatkit-server --save

Next, let's create the AuthService class that will encapsulate the code for implementing JWT authentication in our application.

Using Nest.js CLI run the following command to generate a service:

$ nest g s auth

This command will add the /src/auth/auth.service.ts file that contains the service and the /src/auth/auth.service.spec.ts file that contains the tests for the service and will update the main app module contained in the /src/app.module.ts file to include the generated service.

If you open the main module file at this stage, you can see that the JwtauthService was imported and included in the providers array:

// server/src/app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { AuthService } from './auth/auth.service';
// [...]

@Module({
  imports: [/* [...] */],
  controllers: [AppController],
  providers: [AppService, UserService,AuthService],
})
export class AppModule {}

Now, after creating the service, you need to import the Chatkit server SDK, JwtService , UserService , the User entity and the AuthenticationResponse. Open the src/auth/auth.service.ts file and add the following import:

// server/src/auth/auth.service.ts
import Chatkit from '@pusher/chatkit-server';
import { JwtService } from  '@nestjs/jwt';
import { UserService } from  '../user/user.service';
import { User } from  '../models/user.entity';
import  Chatkit, { AuthenticationResponse } from  '@pusher/chatkit-server';

Next, you need to add the following code:

// server/src/auth/auth.service.ts
@Injectable()
export class AuthService {
  chatkit: Chatkit;
  constructor(
    private readonly userService: UserService,
    private readonly jwtService: JwtService
  ) {
    this.chatkit = new Chatkit({
      instanceLocator: YOUR_INSTANCE_LOCATOR,
      key: YOUR_SECRET_KEY
    })    
  }

We add a member variable to the service that holds the Chatkit instance. Next we inject UserService and JwtService via the constructor and inside it, we create the Chatkit instance.

Replace YOUR_INSTANCE_LOCATOR and YOUR_SECRET_KEY with the credentials from the dashboard. When a user connects to Chatkit, a request will be sent to a /token endpoint (that will be created later in this tutorial) to authenticate the user. Your server has to send a response that contains a token using the Chatkit.authenticate method if the request is valid.

Now, you need to define and implement the following methods:

  • getToken: It's used to create and return a valid JWT token. This method will simply use the authenticate method of the Chatkit instance to generate a valid token.
  • validateUser: It's used to validate the user. This method will use the findByEmail method of UserService to check if the user exists in the database.
  • createUser: It's used to create a user in the local database and then in the Chatkit instance.

Let's start with the createUser method which takes a parameter of the User type:

// server/src/auth/auth.service.ts
private async createUser(userData: User): Promise<User>{
    return this.userService.create(userData).then(user =>{
      const userId = `${user.name}${user.id}`;
      const roomId = "YOUR_ROOM_ID";
      const avatarURL = "https://image.flaticon.com/icons/png/128/149/149071.png";

      return this.chatkit.createUser({id: userId, 
         name: user.name,
         avatarURL: avatarURL
      }).then(()=>{

        return this.chatkit.addUsersToRoom({ roomId: roomId,
          userIds: [userId]}).then(()=>{
            return user;
        });

      })

    });
}

Replace YOUR_ROOM_ID with the room id from the dashboard.

This method calls the create method of UserService to persist the user in the database then when the Promise successfully resolves with a user object that has a unique identifier in the database we use the id and name to create a corresponding user in the Chatkit instance by calling the createUser method of the instance and finally we add the user to the room by calling the addUsersToRoom method.

The createUser method of the Chatkit instance requires a unique user identifier and a user name. We construct the user id by concatenating the name with the database id of the user. This way we make sure the Chatkit user id is unique. We also provide a user avatar for testing using the https://image.flaticon.com/icons/png/128/149/149071.png URL.

Note: In a production application, you need to provide your users with a way to upload their avatars then associate them with the Chatkit user. You also need to hash passwords before storing them in the database using a tool like bcrypt.

Let's now define the getToken method. It takes a user id and returns an AuthenticationResponse:

// server/src/auth/auth.service.ts
public getToken(userId:  string): AuthenticationResponse {
    return this.chatkit.authenticate({ userId: userId });
}  

The getToken method is simply a wrapper around the authenticate method of the Chatkit instance which returns a valid JWT token that can be used by the client to access Chatkit APIs. The authenticate method takes a userId that we specify when we create the user in the Chatkit instance (a concatenation of the word name and the database identifier of the user).

Another method that we need to define is the validateUser method which takes a parameter of the User type:

// server/src/auth/auth.service.ts
private async validateUser(userData: User): Promise<User> {
    return await this.userService.findByEmail(userData.email);
}

This method calls the findByEmail method of UserService which checks if the user with the email exists in the database. If it exists the user object is returned otherwise a null object is returned.

After defining these methods, we'll use them to define two public methods in the same service which are:

  • register for registering users,
  • login for login users.

This is the implementation of the two methods:

// server/src/auth/auth.service.ts
public async login(user: User): Promise<any | {status: number}>{
    return this.validateUser(user).then((userInfo)=>{
      if(!userInfo){
        return { status: 404 };
      }
      let userId = `${userInfo.name}${userInfo.id}`;
      const accessToken = this.jwtService.sign(userId);
      return {
         expires_in: 3600,
         access_token: accessToken,
         user_id: userId,
         status: 200
      };

    });
}

public async register(user: User): Promise<any>{
    return this.createUser(user)
}

In the login method, we first use the validateUser method to make sure the user exists in the database then we call the sign method of JwtService to create an access token from the user id and name payload. Finally, we return an object containing the expires_in, access_token, user_id and status properties.

In the register method, we simply call the previously-defined createUser method to create a user in the database and then in the remote Chatkit instance.

Creating REST API Endpoints

After implementing the login and register methods, it's time to create the corresponding endpoints in our application that handle user authentication. We also need to create a /token endpoint that will be used by the Chatkit client SDK to request JWT tokens from our server.

Open the existing src/app.controller.ts file and update it accordingly:

// server/src/app.controller.ts
import { Post, Body,Request, Controller} from '@nestjs/common';
import { AuthService } from './auth/auth.service';
import { User } from './models/user.entity';

@Controller()
export class AppController {
  constructor(private readonly authService: AuthService) {}

  @Post('token')
  async token(@Request() req): Promise<any> {
    return this.authService.getToken(req.query.user_id).body;
  }

  @Post('login')
  async login(@Body() userData: User): Promise<any> {
    return this.authService.login(userData);
  }  

  @Post('register')
  async register(@Body() userData: User): Promise<any> {
    return this.authService.register(userData);
  }    
}

We start by importing the Post, Request and Body decorators, and also AuthService and the User entity. Next, we inject AuthService as an authService instance via the controller's constructor.

Finally, we instruct Node Nest.js to create the three /token, /login and /register routes that accept a POST request by decorating their methods with the @Post decorator (the route is passed as a parameter).

For the login and register methods, we use the @Body() decorator to instruct Node Nest.js to inject the body of the received request in the endpoint handler as userData.

For the token method we need the full request so we use the @Request decorator instead.

Note: We could also create a controller for handling authentication using nest g controller auth but since our Node (Nest.js) app has only one task which is to handle JWT auth we can simply use the existing application controller.

Testing our Auth REST API Endpoints

After creating the authentication endpoints, let's use cURL to test them before we create our front-end mobile application in the next tutorial.

First, run the following command from the root of your project to start the Node (Nest.js) development server:

$ npm start 

Next, make sure you have cURL installed on your system and run the following command from your terminal:

curl -X POST -H 'content-type: application/json'  -d  '{ "email": "[email protected]", "name": "ahmed", "password": "pass001" }' localhost:3000/register

This will create a user in your SQLite database and a Chatkit user that you can see from the Console/INSTANCE INSPECTOR tab in your Chatkit dashboard. The endpoint returns the created Chatkit user with the id, name, created_at and updated_at fields.

You can also test the /login endpoint using:

curl -X POST -H 'content-type: application/json'  -d  '{ "email": "[email protected]", "password": "pass001"}' localhost:3000/login

This should return a response object with an access token and a user id.

Enabling CORS in Node and TypeScript

Since we'll be using Ionic for creating the mobile app that will interact with this server and we'll do most Ionic development on the browser we need to setup CORS (Cross Origin Resource Sharing). Otherwise, the browsers will block the requests to the server due to the same origin policy.

You can easily enable CORS in Node Nest.js by opening the src/main.ts file and calling the app.enableCors method:

// server/src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.enableCors();
  await app.listen(3000);
}
bootstrap();

Conclusion

In this tutorial, we've seen how to create a REST API server for JWT authentication using Node.js (Nest.js) and TypeScript for our Angular 9/Ionic 5 chat mobile application.

In the next tutorial, we'll continue developing our mobile application with Angular 9 and Ionic 5 that uses this server for authentication and Chatkit for implementing the chat features.

You can find the source code for the first part from this GitHub repository.



Angular 9 Components: Input and Output
Angular 13 selectors
Picture-in-Picture with JavaScript and Angular 10
Jasmine Unit Testing for Angular 12
Angular 9 Tutorial By Example: REST CRUD APIs & HTTP GET Requests with HttpClient
Angular 10/9 Elements Tutorial by Example: Building Web Components
Angular 10/9 Router Tutorial: Learn Routing & Navigation by Example
Angular 10/9 Router CanActivate Guards and UrlTree Parsed Routes
Angular 10/9 JWT Authentication Tutorial with Example
Style Angular 10/9 Components with CSS and ngStyle/ngClass Directives
Upload Images In TypeScript/Node & Angular 9/Ionic 5: Working with Imports, Decorators, Async/Await and FormData
Angular 9/Ionic 5 Chat App: Unsubscribe from RxJS Subjects, OnDestroy/OnInit and ChangeDetectorRef
Adding UI Guards, Auto-Scrolling, Auth State, Typing Indicators and File Attachments with FileReader to your Angular 9/Ionic 5 Chat App
Private Chat Rooms in Angular 9/Ionic 5: Working with TypeScript Strings, Arrays, Promises, and RxJS Behavior/Replay Subjects
Building a Chat App with TypeScript/Node.js, Ionic 5/Angular 9 & PubNub/Chatkit
Chat Read Cursors with Angular 9/Ionic 5 Chat App: Working with TypeScript Variables/Methods & Textarea Keydown/Focusin Events
Add JWT REST API Authentication to Your Node.js/TypeScript Backend with TypeORM and SQLite3 Database
Building Chat App Frontend UI with JWT Auth Using Ionic 5/Angular 9
Install Angular 10 CLI with NPM and Create a New Example App with Routing
Styling An Angular 10 Example App with Bootstrap 4 Navbar, Jumbotron, Tables, Forms and Cards
Integrate Bootstrap 4/jQuery with Angular 10 and Styling the UI With Navbar and Table CSS Classes
Angular 10/9 Tutorial and Example: Build your First Angular App
Angular 9/8 ngIf Tutorial & Example
Angular 10 New Features
Create New Angular 9 Workspace and Application: Using Build and Serve
Angular 10 Release Date: Angular 10 Will Focus on Ivy Artifacts and Libraries Support
HTML5 Download Attribute with TypeScript and Angular 9
Angular 9.1+ Local Direction Query API: getLocaleDirection Example
Angular 9.1 displayBlock CLI Component Generator Option by Example
Angular 9 Basics Tutorial by Example
Angular 9/8 ngFor Directive: Render Arrays with ngFor by Example
Responsive Image Breakpoints Example with CDK's BreakpointObserver in Angular 9/8
Angular 9/8 DOM Queries: ViewChild and ViewChildren Example
The Angular 9/8 Router: Route Parameters with Snapshot and ParamMap by Example
Angular 9/8 Nested Routing and Child Routes by Example
Angular 9 Examples: 2 Ways To Display A Component (Selector & Router)
Angular 9/8 Tutorial: Http POST to Node/Express.js Example
Angular 9/8 Feature and Root Modules by Example
Angular 9/8 with PHP: Consuming a RESTful CRUD API with HttpClient and Forms
Angular 9/8 with PHP and MySQL Database: REST CRUD Example & Tutorial
Unit Testing Angular 9/8 Apps Tutorial with Jasmine & Karma by Example
Angular 9 Web Components: Custom Elements & Shadow DOM
Angular 9 Renderer2 with Directives Tutorial by Example
Build Progressive Web Apps (PWA) with Angular 9/8 Tutorial and Example
Angular 9 Internationalization/Localization with ngx-translate Tutorial and Example
Create Angular 9 Calendar with ngx-bootstrap datepicker Example and Tutorial
Multiple File Upload with Angular 9 FormData and PHP by Example
Angular 9/8 Reactive Forms with Validation Tutorial by Example
Angular 9/8 Template Forms Tutorial: Example Authentication Form (ngModel/ngForm/ngSubmit)
Angular 9/8 JAMStack By Example
Angular HttpClient v9/8 — Building a Service for Sending API Calls and Fetching Data
Build an Angular 9/8 CRUD Example & Tutorial
Styling An Angular 9/8/7 Example App with Bootstrap 4 Navbar, Jumbotron, Tables, Forms and Cards
Angular 8/7 Tutorial By Example: (REST API, HttpClient GET, Components, Services & ngFor)

✋If you have any questions about this article, ask them in our GitHub Discussions 👈 community. You can also Gitter

✋ Want to master Angular 14? Read our angular tutorial and join our #DailyAngularChallenge where we learn to build components, directives, services, pipes and complete web, mobile, and desktop applications with latest Angular version.

✋ Make sure to join our Angular 14 Dev Community 👈 to discuss anything related to Angular development.

❤️ Like our page and subscribe to our feed for updates!