In this era, legacy relational databases it's rarely used in new architectures. The microservice pattern mostly uses a NoSQL database. There are many types of NoSQL databases:

  • Graph Oriented neo4j, Dgraph
  • Document Oriented mongoDB
  • Real-Time Oriented fireBase, RethinkDB

This time I'll tell how to integrate RethinkDB, an open-source, distributed & sharded database for real-time communications with NestJS a nodeJS framework which will be coded with TypeScript.

By default, NestJS comes with the ORM TypeORM integrated, but I don't really like the ORM pattern and TypeORM isn't natively supported in TypeORM, so I'll integrate myself RethinkDB with a NestJS microservice.

Creating the project

If it's your first time with nodeJS, you will need NodeJS to be installed on your machine.

NodeJS dependencies can be easily managed with npm, which it's installed by default with nodeJS.

With nodeJS you can have 3 types of dependencies

  • Project Local dependencies for the current project, under node_modules directory.
  • Project (dev) Local dependencies for the current project but for development.
  • Global Available for whole system and available in the $PATH

NestJS

First, you need to install NestJS globally:

npm i -g @nestjs/cli

Once NestJS CLI is installed we can create a new project under the current directory with the following command:

nest new <project_name>

Answer the prompted questions and you are ready to go with the new project.

File organization

A NestJS application it's quite similar to Angular 2+. We have common file types:

  • Controllers Defines the endpoint and its behaviour
  • Providers All stuff needed to interconnect Controllers to Bussiness Logic. Can be Services, Repositories...
  • Modules Modules are classes for organizing the application, it can inherit Controllers, Providers and other Modules as well.

More detailed info can be found on NestJS official documentation

Providing rethinkDB connection via async custom Provider

In order to dynamically provide RethinkDB connections asynchronously it's required to create an Async custom provider (Detailed Info).

First of all we need to add rethinkdb dependency:

npm install --save rethinkdb @types/rethinkdb

After dependecies are installed, we can start defining our first provider:

rethink/database.provider.ts

import * as rethink from "rethinkdb"

export const RethinkProvider = {
    provide: 'RethinkProvider',
    useFactory: async () => {
      const conn = await rethink.connect("localhost")
      return conn
    }
  }

With the following code we are defining a 'RethinkProvider' which it's an async Factory which provides connections dynamically.

In order to use this provider, we need to define it in a module, let's create the Rethink module:

/rethink/rethink.module.ts

import { Module } from '@nestjs/common';
import { RethinkController } from './rethink.controller';
import { RethinkProvider } from './database.provider'

@Module({
  imports: [],
  controllers: [RethinkController],
  providers: [RethinkProvider],
  exports: [RethinkProvider]
})
export class RethinkModule {}

Now we can use our RethinkDB connection Factory via @Inject() decorator in our module components.

Let's create a basic RethinkService which will provide common methods relative to the databse.

rethink/rethink.service.ts

import { Injectable } from "@nestjs/common";

@Injectable()
export class RethinkService {
}

With this snippet, we are defining a new Injectable Provider called 'RethinkService'.

In order to inject our previously created connection provider, we should code the following constructor with the @Inject() decorator:

private connection: rethink.Connection

constructor(@Inject('RethinkProvider') connection) {
    this.connection = connection
}

As always, in order to use a new Provider, we need to declare it in the proper module, RethinkModule in our case, so we need to modify the file:

rethink/rethink.module.ts

import { Module } from '@nestjs/common';
import { RethinkController } from './rethink.controller';
import { RethinkService } from './rethink.service';
import { RethinkProvider } from './database.provider'

@Module({
  imports: [],
  controllers: [RethinkController],
  providers: [RethinkService, RethinkProvider],
  exports: [RethinkProvider]
})
export class RethinkModule {}

Adding functions to the RethinkDB Service

Now, we are ready to declare new functions in the RethinkService. We are going to create a new function for creating new Tables in the database:

rethink/rethink.service.ts

 /**
     * Creates a new table in the RethinkDB instance
     * @param tableName Name of the new Table
     * @returns Creation status promise
     */
async createTable(tableName:string): Promise<rethink.CreateResult> {
    let result = await rethink.db('test').tableCreate(tableName).run(this.connection)
    return result
}

With the previous code we are calling to the rethinkDB library and creating a new Table under 'Test' database.

In order to use our new function we can call it via a Controller or another Service injecting this Serivice/Provider on it:

Creating a new Controller which will use the Rethink Service/Provider

Injecting a Provider defined in the module

rethink/rethink.controller.ts

import { Controller } from '@nestjs/common';
import { RethinkService } from 'rethink/rethink.service';

@Controller('rethink')
export class RethinkController {
  constructor(private readonly rethinkService: RethinkService) {}
}

Dependency inversion is achived via constructor() with the private readonly declaration

Creating a new API method

HTTP API methods are defined via Decorators @Get @Post...

rethink/rethink.controller.ts

@Post('table/:name')
  async newTable(@Param ('name') name): Promise<string> {
    let response = await this.rethinkService.createTable(name)
      .then(result => {
        return "Name " + name + " received!\n" + JSON.stringify(result)
      })
      .catch(reason => {
        return reason
      })
  
      return response
  }

You can check the GitHub repository here

With these simple steps we will have a HTTP API with only 1 method that creates a new table inside 'test' database in our local RethinkDB instance

In the next chapter we will make some new methods to the RethinkService for data creation and manipulation. Then we will apply the Repository pattern in order to organize our code for readability and testing purposes.

Next chapter will be available in few days