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:
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 staticindex.html. - Visit
/api/helloto 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:
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.tswhich handles incomingHTTPrequests 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.tsregisters all controllers/services and serves as the app's root
💡 We will experiment with CLI commands like
nest generate service usersto 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:
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):
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: trueallows cookies or authorization headers to be sent along with requests.
3. Serving Static Files:
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:
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
/apiwill returnindex.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:
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.
