How to run NestJS inside Watt

Matteo CollinaMatteo Collina
6 min read

NestJS is a framework designed for building server-side applications using Node.js and TypeScript (although it supports pure JavaScript as well). It provides an architecture inspired by Angular, offering a structured approach with modules, controllers, and services that promote code organization and maintainability for complex applications. Key features include support for decorators, dependency injection, and various built-in modules for everyday tasks, such as handling HTTP requests, WebSockets, and database integration.

The @platformatic/nest package streamlines the integration of NestJS applications into the Watt runtime environment. Watt provides benefits such as automatic scaling across multiple CPU cores, built-in monitoring and metrics, and easy management of various services. By leveraging @platformatic/nest, developers can take advantage of Watt's operational capabilities without modifying their existing NestJS codebase.

This tutorial guides users through the steps of migrating both a NestJS backend and a Vite frontend application into Watt, demonstrating how Platformatic Composer can then be used to expose these applications as a unified endpoint. This setup allows developers to build scalable and observable full-stack applications with ease.

Watch the full tutorial below

Requirements

You will need Node 20.16.0 or greater and a package manager like npm or pnpm.

Create the NestJS (backend) application

The backend will be a simple NestJS which just returns the current time.

Let’s start by creating a NestJS application using @nest/cli.

$ pnpx @nestjs/cli new -p pnpm --skip-git --directory nest nest
$ cd nest

Now create a controller and a service which return the current time.

// src/app.controller.ts

import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get('/time')
  getTime(): object {
    return this.appService.getTime();
  }
}

And now let’s modify the application service to actually return the time:

// src/app.service.ts

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

@Injectable()
export class AppService {
  getTime(): object {
    return { now: Date.now() };
  }
}

Initially, Cross-Origin Resource Sharing (CORS) must be enabled in the NestJS application because the backend and frontend operate as separate processes on distinct ports. This is necessary to allow the frontend, running in a browser, to make requests to the backend's API.

However, when integrating these applications into Watt and the Platformatic Composer, the necessity for explicitly configuring CORS is eliminated.

To enable CORS you have to modify the main.ts file:

// 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(process.env.PORT ?? 3000);
}
bootstrap();

Now if we run the application with npm start and then fetch the route we have defined we should see the current time as UNIX timestamp (millseconds from Jan 1st 1970).

$ curl http://localhost:3000/time
{"now":1748868040090}

Create the Vite (frontend) application

The frontend will be a simple Vite application which shows the current time on a page.

Let’s start by creating a Next.js application in another terminal window using vite create.

$ pnpm create vite@5 vite --template react-ts
$ cd vite
$ pnpm install

Now modify the src/App.tsx to invoke our backend:

// src/App.tsx

import { useCallback, useEffect, useState } from "react";
import "./App.css";
import viteLogo from "/vite.svg";

const url = 'http://localhost:3000/time';

async function fetchTime(): Promise<number> {
  const response = await fetch(url);
  return (await response.json()).now;
}

export default function App() {
  const [time, setTime] = useState('');
  const [loading, setLoading] = useState(true);

  const onRefresh = useCallback(() => {
    const timeout = setTimeout(() => {
      setLoading(true);
    }, 250);

    fetchTime()
      .then((t) => {
        setTime(new Date(t).toLocaleTimeString());
      })
      .catch(() => {
        setTime("UNAVAILABLE");
      })
      .finally(() => {
        clearTimeout(timeout);
        setLoading(false);
      });
  }, [setLoading, setTime]);

  useEffect(() => {
    onRefresh();
  }, [onRefresh]);

  if (loading) {
    return <div>Loading...</div>;
  }

  return (
    <>
      <h1>Hello from Vite</h1>
      <div className="card">
        <button onClick={onRefresh}>
          <p>Current time is {time}.</p>
        </button>
      </div>
    </>
  );
}

We can now start the application using pnpm run dev and see the application in our browser.

Integrate the applications into Watt

Previously, the NestJS backend and Vite frontend applications operated as distinct entities. This required separate monitoring, management, and resource allocation for each.

Furthermore, since the frontend and backend run on different ports, Cross-Origin Resource Sharing (CORS) had to be explicitly enabled in the NestJS application to allow the browser to make requests across different origins.

Integrating these applications into Watt streamlines this setup by managing them within a unified runtime environment. This eliminates the need for separate monitoring and simplifies deployment, while also automatically handling cross-origin requests through its built-in proxying capabilities thanks to Platformatic Composer.

To create a new Watt application which integrates the existing applications, you can use the wattpm create command.

$ pnpx wattpm@latest create -P pnpm

Hello Paolo Insogna, welcome to Platformatic 2.68.0!
? Where would you like to create your project? platformatic
? Which kind of service do you want to create? @platformatic/vite
✔ Installing @platformatic/vite using pnpm ...
? What is the name of the service? frontend
? Where is your application located? /home/user/work/vite
? Do you want to import or copy your application? copy
? Do you want to create another service? yes
? Which kind of service do you want to create? @platformatic/nest
✔ Installing @platformatic/nest using pnpm ...
? What is the name of the service? backend
? Where is your application located? /home/user/work/vite/nest
? Do you want to import or copy your application? copy
? Do you want to create another service? yes
? Which kind of service do you want to create? @platformatic/composer
✔ Installing @platformatic/composer@^2.68.0 using pnpm ...
? What is the name of the service? composer
? Do you want to create another service? no
? Which service should be exposed? composer
? Do you want to use TypeScript? no
? What port do you want to use? 3042
? Do you want to init the git repository? no

$ cd platformatic

Start the new application

Configure the Composer to correctly serve our services. Modify the watt.json in the web/composer folder as follows:

// web/composer/watt.json

{
  "$schema": "https://schemas.platformatic.dev/@platformatic/composer/2.67.0.json",
  "composer": {
    "services": [
      {
        "id": "backend",
        "proxy": {
          "prefix": "/api"
        }
      },
      {
        "id": "frontend",
        "proxy": {
          "prefix": "/"
        }
      }
    ],
    "refreshTimeout": 1000
  },
  "watch": true
}

Now we can start our application:

$ wattpm dev

On the browser you will see that the current time is “UNAVAILABLE”. This is expected as the frontend is still pointing to the old application. Let’s fix it.

Modify line 4 in web/frontend/src/App.tsx to look like this:

// web/frontend/src/App.tsx, line 4

const url = '/api/time'

Also, we can remove CORS from the backend. Let’s change web/backend/src/main.ts as follows:

// web/backend/src/main.ts

import {NestFactory} from '@nestjs/core';
import {AppModule} from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(process.env.PORT ?? 3000);
}
bootstrap();

If you refresh the page on the browser again, you will see that everything is running smoothly thanks to Watt.

Building and starting in production mode

After integrating your NestJS and Vite applications into Watt, you can get them ready for deployment. Watt streamlines this process by providing the wattpm build command.

Once built, start the application in production mode using wattpm start from the root of your Watt project.

This will use the configurations in your project to run your services in a production-optimized environment, benefiting from Watt's stability and performance features.

This ensures efficient serving and readiness for real-world traffic. Try it yourself to see it in action.

If you want to see it in action or get a custom Platformatic product tour, send an email to hello@platformatic.dev.

1
Subscribe to my newsletter

Read articles from Matteo Collina directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Matteo Collina
Matteo Collina