Scaffolding Your NestJS Project

Introduction: What You’ll Build

Welcome to your first lesson in NestJS Fundamentals. In this lesson, you’ll create the foundation for a full-stack project using NestJS as the backend. The goal is to set up:

  • A functional NestJS server
  • Static file serving (for a minimal frontend)
  • A working API endpoint (/api/hello)
  • Proper CORS configuration so the frontend can talk to the backend
  • SPA (Single Page Application) fallback routing support

The eventual goal is to create a full stack app with integrated React frontend, but for now we’ll focus entirely on the backend and a simple preview using a raw index.html file.

Project Structure Overview

Here’s how the project is structured:

src/
  app.controller.ts
  app.module.ts
  app.service.ts
  main.ts
public/
  index.html

Breakdown:

  • src/ holds the NestJS application code.
  • public/ contains a static HTML file to preview the API. In the browser preview, you can:
  • Visit / to load the static index.html.
  • Visit /api/hello to see a greeting returned from the backend.

This will give you a full round-trip experience between the frontend and backend.

Creating and Understanding the NestJS App

To start a NestJS project (on your own machine), you’d typically run:

nest new reading-tracker-api

This uses the Nest CLI (Command Line Interface) – a tool that helps scaffold your project with best practices and TypeScript support. It automatically creates:

  • a controller to handle routes. In our code, we have app.controller.ts which handles incoming HTTP requests like /api/hello.
  • a service for logic. In the app.service.ts, we have a logic or data used by the controller. You’ll see more examples as we proceed with the course.
  • a module to glue everything together. Our app.module.ts registers all controllers/services and serves as the app's root

💡 We will experiment with CLI commands like nest generate service users to scaffold new parts in our practice sessions.

Understanding main.ts – Bootstrapping the Application

The main.ts file is the entry point of every NestJS application. It’s where the app is created and configured before being run. Here’s a breakdown of what happens in main.ts, line by line:

1. Creating the App Instance:

const app = await NestFactory.create<NestExpressApplication>(AppModule);

This line bootstraps the app by loading the AppModule and initializing NestJS with the Express framework under the hood. The type <NestExpressApplication> gives us access to Express-specific features, like serving static files.

2. Enabling CORS (Cross-Origin Resource Sharing):

app.enableCors({ origin: '*', credentials: true });

CORS is a browser security feature that blocks requests from one domain to another unless explicitly allowed.

In development, your frontend (React) and backend (NestJS) may run on different ports (e.g., React on localhost:5173, NestJS on localhost:3000). CORS needs to be enabled so the frontend can make requests to the backend.

  • origin: '*' allows all origins to access the backend. This is safe for local development, but you should restrict it in production.
  • credentials: true allows cookies or authorization headers to be sent along with requests.

3. Serving Static Files:

const publicDir = join(__dirname, '..', 'public');
app.useStaticAssets(publicDir);

Here, we tell NestJS to serve files from the public/ folder. This is useful for hosting HTML, CSS, and JavaScript files.

Static files are files that do not change dynamically on the server — like HTML or images. They're sent as-is to the browser.

In this case, we’ve added a single index.html file to act as a frontend preview of the backend. Now, if you visit http://localhost:3000/, it loads public/index.html.

4. SPA (Single Page Application) Fallback – Handling Non-API Frontend Routes:

const server = app.getHttpAdapter().getInstance();
server.get(/^(?!\/api).*/, (req: Request, res: Response) =>
  res.sendFile(join(publicDir, 'index.html')),
);

Modern frontend apps like React use client-side routing, which means that when the user navigates to /dashboard or /profile, the browser still loads a single HTML file — index.html. This fallback ensures:

  • Any GET request not starting with /api will return index.html.
  • It avoids 404 errors when users refresh or directly visit a frontend route.

⚠️ Without this line, the app may mistakenly try to treat frontend paths (like /profile) as API routes and return a 404 if no backend route is found.

The regular expression /^(?!\/api).*/ uses a negative lookahead to exclude paths that begin with /api.

5. Listening on a Port:

const port = process.env.PORT || 3000;
await app.listen(port);
console.log(`Application is running on: http://localhost:${port}`);

Here we define the port the app should run on. By default, it's 3000. But using process.env.PORT allows for flexibility when deploying to platforms like Heroku or Vercel.

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal