Avatar of olabemiwo danielolabemiwo daniel

Building a NestJS App on Railway

Nestjs is a javascript framework with a typescript flavour. Often described as the swiss-army of javascript backend applications, it allows you to write following an object-oriented programming paradigm or functional programming and is easily a good switch for anyone coming from a non-js background to make the switch.

This article covers the nitty gritty of getting out Nestjs running on Railway. Here is the link to the project we will build in GitHub.

Let's get started.

You can use your preferred cli tool on your machine to get started.

To create a new project, just type the commands below sequentially

  • Install nestjs:
npm i -g @nestjs/cli
  • Create a new nestjs project:
nest new rail-nest

The project name is rail-nest which can be replaced with any name you like.

  • Run the command below to enter the new project directory and start the nestjs project locally on your machine:
cd rail-nest
npm run start

If you navigate to http://localhost:3000 on your browser, you should get a positive response showing your nestjs application is running.

To have an application that can easily scale, where we can continually add more components as our application grows, containerization is a great approach to ensuring we can have an all-in-one application.

To get started, we need to create a Dockerfile, where we can add detailed instructions for our Docker applications.

  • In your cli, run the command below to create a Dockerfile and a .dockerignore in your project root directory.
touch Dockerfile .dockerignore
  • Then let's add the instruction below to the Dockerfile
# Base image
FROM node:20

# Create app directory
WORKDIR /usr/src/app

# A wildcard is used to ensure both package.json AND package-lock.json are copied
COPY package*.json ./

# Install app dependencies
RUN npm ci

# Bundle app source
COPY . .

# Creates a "dist" folder with the production build
RUN npm run build

# Expose the port on which the app will run
EXPOSE 3000

# Start the server using the production build
CMD ["npm", "run", "start:prod"]
  • Add the following to your .dockerignore.
Dockerfile
 .dockerignore
node_modules
npm-debug.log
Dist

Now let’s deploy the project to Railway. See Railway docs for reference if needed.

  • Push the NestJS code to a github repo.
  • On your project canvas in Railway
    • Create a new project
    • Add a new Github service
    • Select your Github repo
    • Connect your github repo to the source repo in the source section.
    • Apply changes to deploy the app

The setup ensures that the database, migrations, queues and scheduled tasks are all operational and can run seamlessly.

We will start by using the Nestjs cache manager, the native cache manager stores the cache in memory, we will then configure the cache to be stored in Redis.

  • Install this cache manager package via your cli:
npm install @nestjs/cache-manager [email protected] @nestjs/config
  • Then import the CacheModule into yourapp.module.ts:
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { CacheModule } from '@nestjs/cache-manager';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [
	ConfigModule.forRoot(),
	CacheModule.register()
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

Implement the cache module globally (Optional)

If you want to leverage the cache module across different modules, you can pass the isGlobal flag property:

CacheModule.register({ isGlobal: true })

For example, here's a CacheModule implementation applied globally to a NestJS app in the app.module.ts file:

import { CacheModule, Module } from '@nestjs/common';
import * as redisStore from 'cache-manager-redis-store';
import { ConfigModule } from '@nestjs/config';

@Module({
  imports: [
    ConfigModule.forRoot(),
    CacheModule.register({
      isGlobal: true,
    }),
  ],
})
export class AppModule {}

Implement Redis for caching

Up to this point, our cache will be stored in memory, to migrate easily to Redis, we will need to update the cache module configuration and also create a Redis server in Railway.

  • Let's install the relevant packages:
npm install [email protected]
npm install --save-dev @types/cache-manager-redis-store
  • Now, proceed to change the cache module to use Redis.
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { CacheModule } from '@nestjs/cache-manager';
import * as redisStore from 'cache-manager-redis-store';
import { ConfigModule } from '@nestjs/config';

@Module({
  imports: [
    ConfigModule.forRoot(),
    CacheModule.register({
    isGlobal: true,
    store: redisStore,
    host: process.env.REDISHOST,
    port: process.env.REDISPORT
  })],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}
  • After, we will tweak our cache module to the instruction below:
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ConfigModule } from '@nestjs/config';
import { CacheModule, CacheStore } from '@nestjs/cache-manager';
import { redisStore } from 'cache-manager-redis-store';

@Module({
  imports: [
    ConfigModule.forRoot(),
    CacheModule.registerAsync({
      isGlobal: true,
      useFactory: async () => {
        const store = await redisStore({
          socket: {
            host: process.env.REDISHOST,
            port: +process.env.REDISPORT,
          },
          url: process.env.REDISURL,
          password: process.env.REDISPASSWORD
        });
        return {
          store: store as unknown as CacheStore,
          ttl: 60 * 60 * 24 * 7,
        }
      }
    })
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

All set locally, simply push to your github repo the latest changes.

Add Redis to the Railway project

Now let’s add a Redis service in Railway and connect it to NestJS.

  • On your railway project canvas, create a new redis service.
  • Head back to your App service and click on Variables.
  • Add all necessary environment variables
REDISHOST="${{Redis.REDISHOST}}"
REDISPASSWORD="${{Redis.REDIS_PASSWORD}}"
REDISPORT="${{Redis.REDISPORT}}"
REDISURL="${{Redis.REDIS_URL}}"
  • Click Deploy
View of NestJS project in Railway with Redis

View of NestJS project in Railway with Redis

Currently, we have Redis infused into our Nestjs app. Let's add the postgres database.

  • Locally, we are going to add typeorm and the postgres adapter package.
npm install @nestjs/typeorm typeorm
npm install pg
  • Then add the following in our app.module.ts
	TypeOrmModule.forRootAsync({
      useFactory: () => ({
        type: 'postgres',
        host: process.env.POSTGRESHOST,
        port: +process.env.POSTGRESPORT,
        username: process.env.POSTGRESUSER,
        password: process.env.POSTGRESPASSWORD,
        database: process.env.POSTGRESDB,
        synchronize: true,
        entities: []
      })
    })

Note the values in the env are random variables and should be replaced with their original representation based on your setup.

  • Simply push to your github repo the latest changes.

Add Postgres to the Railway project

Now let’s add the Postgres service to our Railway project and connect it to NestJS.

  • On your railway project canvas, create a new Postgres service.
  • Head back to your App service and click on Variables.
  • Add all necessary environment variables
POSTGRESDB="${{Postgres.PGDATABASE}}"
POSTGRESHOST="${{Postgres.PGHOST}}"
POSTGRESPASSWORD="${{Postgres.PGPASSWORD}}"
POSTGRESPORT="${{Postgres.PGPORT}}"
POSTGRESUSER="${{Postgres.PGUSER}}"
  • Click Deploy
View of NestJS project in Railway with Postgres

View of NestJS project in Railway with Postgres

Let us add a task scheduler in Nestjs.

  • Installing the packages below:
npm install --save @nestjs/schedule
npm install --save-dev @types/cron
  • Add the instruction below to your app.module.ts import section:
ScheduleModule.forRoot()

Nestjs schedule runs alongside your main application, by integrating the cron functionality directly into the application lifecycle.

  • A final push to the github repo and our setup is complete.

Let’s make the NestJS app available on the public internet.

  • Navigate to the Networking section under the Settings tab of your new service.
  • Click Generate Domain to create a public URL for your app.

We now have a fully functional Nestjs application running in Railway!

Let’s explore one more option for starting a NestJS project in Railway.

Railway provides templates as a way to get started almost immediately with your NestJS project, enabling you to provision a service or set of services by simple clicks.

If you’re looking for the fastest way to get started, the one-click deploy option is ideal. It sets up your Nestjs app along with a Redis database, a Postgres database and the Nestjs scheduler. Just click the button below to get started.

After deploying, we recommend that you eject from the template to create a copy of the repository under your own GitHub account. This will give you full control over the source code and project.

With that, we've successfully set up a Nestjs application coupled with everything needed to get started immediately, database, caching and cron schedule inclusive. You should also be able to use this as a reference to deploy your Nestjs application from scratch or via Github. There is no limit to what you can do, and can decide to extend to other Railway compatible service.