Files
In360/NestJS.md
2025-07-28 23:15:31 +00:00

14 KiB

Excellent. Building this with NestJS is a superb choice, especially if you or your team have a JavaScript/TypeScript background. NestJS provides a structured, scalable architecture that prevents many common pitfalls.

This guide will walk you through starting your project from scratch using NestJS, React, and PostgreSQL. We'll use Docker to run our database easily, which is a modern best practice.

Phase 0: Prerequisites (Your Developer Machine Setup)

  1. Node.js and npm: (Node LTS version). This is required for both the frontend and backend. Download Node.js
  2. Docker Desktop: We will use this to run our PostgreSQL database in a container. This is far easier than installing PostgreSQL directly on your system. Download Docker Desktop
  3. NestJS CLI: The command-line interface for creating and managing NestJS projects.
    npm i -g @nestjs/cli
    
  4. Git: For version control. Download Git
  5. A Code Editor: VS Code is highly recommended due to its excellent TypeScript support.
  6. An API Client: Postman or Insomnia to test your API endpoints.

Phase 1: Project Initialization & Structure

Just like before, we'll create a clean project structure.

# 1. Create a main project folder
mkdir incident-reporter-app
cd incident-reporter-app

# 2. Initialize a Git repository
git init

# 3. Create folders for backend and frontend
mkdir backend
mkdir frontend

Now, we'll add a docker-compose.yml file in the root directory. This file tells Docker how to run our PostgreSQL database.

Create docker-compose.yml in the incident-reporter-app/ root folder:

version: '3.8'
services:
  db:
    image: postgres:14-alpine # Use a trusted PostgreSQL image
    restart: always
    environment:
      - POSTGRES_USER=myuser         # The database username
      - POSTGRES_PASSWORD=mypassword # The database password
      - POSTGRES_DB=incident_db      # The database name
    ports:
      - '5432:5432' # Map port 5432 on your machine to port 5432 in the container
    volumes:
      - db-data:/var/lib/postgresql/data # Persist database data

volumes:
  db-data: # Define the volume for data persistence

Start your database now: Open your terminal in the project root and run:

docker-compose up -d

Your PostgreSQL database is now running in the background!


Phase 2: Building the Backend Foundation (NestJS)

We'll use the NestJS CLI to quickly bootstrap a professional-grade project structure.

  1. Navigate into the backend folder and create a new NestJS project.

    cd backend
    nest new .  # The "." creates the project in the current folder
    

    When prompted to choose a package manager, select npm. This will create a complete NestJS project structure.

  2. Install dependencies for database integration. We'll use TypeORM, a powerful Object-Relational Mapper that works wonderfully with NestJS.

    npm install @nestjs/typeorm typeorm pg
    
    • @nestjs/typeorm: The official NestJS module for TypeORM.
    • typeorm: The TypeORM library itself.
    • pg: The driver that allows Node.js to communicate with PostgreSQL.
  3. Configure the database connection in src/app.module.ts. This tells your NestJS application how to find and connect to the Dockerized database.

    import { Module } from '@nestjs/common';
    import { AppController } from './app.controller';
    import { AppService } from './app.service';
    import { TypeOrmModule } from '@nestjs/typeorm';
    
    @Module({
      imports: [
        TypeOrmModule.forRoot({
          type: 'postgres',
          host: 'localhost',
          port: 5432,
          username: 'myuser',      // From docker-compose.yml
          password: 'mypassword',  // From docker-compose.yml
          database: 'incident_db', // From docker-compose.yml
          entities: [__dirname + '/**/*.entity{.ts,.js}'], // Auto-load entities
          synchronize: true, // Auto-creates DB tables based on entities. Great for dev, NOT for prod.
        }),
      ],
      controllers: [AppController],
      providers: [AppService],
    })
    export class AppModule {}
    

    CRITICAL WARNING: synchronize: true is amazing for development as it automatically updates your database schema. However, you must disable it and use migrations in a production environment to avoid potential data loss.

  4. Generate a "Resource" for incidents. The Nest CLI can generate all the necessary files for a feature (Module, Controller, Service, Entity, DTOs). This is a massive time-saver.

    nest generate resource incidents --no-spec # --no-spec skips creating test files for now
    

    Choose REST API when prompted. This command will create a src/incidents folder with all the boilerplate code.

  5. Define your Incident Entity in src/incidents/entities/incident.entity.ts. This is the TypeORM equivalent of a Django Model. It defines the incidents table structure.

    import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn } from 'typeorm';
    
    @Entity() // This marks the class as a database table
    export class Incident {
      @PrimaryGeneratedColumn('uuid') // Use UUIDs for primary keys - a good practice
      id: string;
    
      @Column()
      employee_name: string;
    
      @Column({ type: 'timestamp with time zone' })
      incident_date: Date;
    
      @Column()
      location: string;
    
      @Column('text')
      description: string;
    
      @Column({
        type: 'varchar',
        default: 'open',
      })
      status: 'open' | 'in_progress' | 'closed';
    
      @CreateDateColumn()
      created_at: Date;
    }
    
  6. Import the IncidentsModule into your root AppModule. Your nest g resource command should have done this already, but it's good to check src/app.module.ts:

    // src/app.module.ts
    // ... imports
    import { IncidentsModule } from './incidents/incidents.module';
    
    @Module({
      imports: [
        TypeOrmModule.forRoot({ ... }), // Your DB connection
        IncidentsModule,                // Make sure this is here
      ],
      //...
    })
    export class AppModule {}
    

Backend Checkpoint!

  • Start the development server: npm run start:dev
  • The server should start without errors. Thanks to synchronize: true, if you check your PostgreSQL database (using a tool like DBeaver or pgAdmin), you will see an incident table has been created automatically.

Phase 3: Building the API Endpoints (NestJS)

The resource command already created skeleton files. Now we just fill in the logic.

  1. Review the DTO (Data Transfer Object) in src/incidents/dto/create-incident.dto.ts. This class defines the shape of the data we expect when someone creates a new incident. You can add validation here later. For now, it's a good representation of our entity's fields.

  2. Implement the Logic in the Service (src/incidents/incidents.service.ts). The service handles the business logic (interacting with the database).

    import { Injectable } from '@nestjs/common';
    import { InjectRepository } from '@nestjs/typeorm';
    import { Repository } from 'typeorm';
    import { CreateIncidentDto } from './dto/create-incident.dto';
    import { Incident } from './entities/incident.entity';
    
    @Injectable()
    export class IncidentsService {
      constructor(
        @InjectRepository(Incident) // Inject the Incident repository
        private incidentsRepository: Repository<Incident>,
      ) {}
    
      create(createIncidentDto: CreateIncidentDto): Promise<Incident> {
        const newIncident = this.incidentsRepository.create(createIncidentDto);
        return this.incidentsRepository.save(newIncident);
      }
    
      findAll(): Promise<Incident[]> {
        return this.incidentsRepository.find();
      }
    
      findOne(id: string): Promise<Incident> {
        return this.incidentsRepository.findOneBy({ id });
      }
    
      // We will skip update and remove for this initial guide, but they follow a similar pattern
    }
    
  3. Ensure the Controller is Wired Correctly (src/incidents/incidents.controller.ts). The controller handles incoming HTTP requests and calls the service. The nest g resource command set this up perfectly. It should already look like this:

    import { Controller, Get, Post, Body, Param } from '@nestjs/common';
    import { IncidentsService } from './incidents.service';
    import { CreateIncidentDto } from './dto/create-incident.dto';
    
    @Controller('incidents') // All routes in this controller are prefixed with /incidents
    export class IncidentsController {
      constructor(private readonly incidentsService: IncidentsService) {}
    
      @Post() // Handles POST /incidents
      create(@Body() createIncidentDto: CreateIncidentDto) {
        return this.incidentsService.create(createIncidentDto);
      }
    
      @Get() // Handles GET /incidents
      findAll() {
        return this.incidentsService.findAll();
      }
    
      @Get(':id') // Handles GET /incidents/:id
      findOne(@Param('id') id: string) {
        return this.incidentsService.findOne(id);
      }
    }
    

API Test!

  • Make sure your NestJS server is running.
  • Open Postman/Insomnia.
    • Create: Send a POST request to http://localhost:3000/incidents with a JSON body like:
      {
        "employee_name": "John Doe",
        "incident_date": "2023-10-27T10:00:00Z",
        "location": "Warehouse A",
        "description": "Slipped on a wet floor."
      }
      
    • List: Send a GET request to http://localhost:3000/incidents. You should get back an array containing the incident you just created.

You have a fully working, well-structured API.


Phase 4 & 5: Frontend (React) & Connection

This part is almost identical to the previous guide, but we'll connect to our NestJS endpoints.

  1. Navigate to the frontend directory (in a new terminal!) and create the React app.

    # (In a new terminal)
    cd incident-reporter-app/frontend
    npm create vite@latest
    
    • Name it . and select React and JavaScript.
  2. Install dependencies and run the app.

    npm install
    npm install axios react-router-dom @mui/material @emotion/react @emotion/styled
    npm run dev
    

    Your React app is now running at http://localhost:5173.

  3. Enable CORS in the NestJS backend. A web browser will block the React app from talking to the NestJS API unless we explicitly allow it. This is a one-line change.

    backend/src/main.ts:

    import { NestFactory } from '@nestjs/core';
    import { AppModule } from './app.module';
    
    async function bootstrap() {
      const app = await NestFactory.create(AppModule);
    
      app.enableCors(); // <-- ADD THIS LINE
    
      await app.listen(3000);
    }
    bootstrap();
    

    Restart your NestJS server (npm run start:dev) for the change to take effect.

  4. Fetch and display data in React. Replace frontend/src/App.jsx with code to call your NestJS API.

    frontend/src/App.jsx:

    import { useState, useEffect } from 'react';
    import axios from 'axios';
    
    function App() {
      const [incidents, setIncidents] = useState([]);
      const [error, setError] = useState(null);
    
      useEffect(() => {
        // Fetch incidents from the NestJS API (running on port 3000)
        axios.get('http://localhost:3000/incidents') // <-- NOTE THE PORT AND ENDPOINT
          .then(response => {
            setIncidents(response.data);
          })
          .catch(error => {
            console.error("There was an error fetching the data!", error);
            setError("Could not load data. Is the backend server running?");
          });
      }, []);
    
      if (error) return <div>Error: {error}</div>;
    
      return (
        <div>
          <h1>Incident Reports</h1>
          <ul>
            {incidents.length > 0 ? (
              incidents.map(incident => (
                <li key={incident.id}>
                  <strong>{incident.location}:</strong> {incident.description} ({incident.status})
                </li>
              ))
            ) : (
              <p>No incidents reported yet. Create one via Postman!</p>
            )}
          </ul>
        </div>
      );
    }
    
    export default App;
    

Grand Finale:

Refresh your React app in the browser (http://localhost:5173). It will now display the list of incidents fetched directly from your NestJS API and PostgreSQL database.

You now have a robust, full-stack foundation ready to be built upon.

Next Steps from Here:

  1. Implement Validation: Use NestJS's class-validator library in your DTOs to automatically validate incoming API requests.
  2. Build Out React Components: Create a proper form for submitting incidents, a details page for viewing one, and use MUI for a professional UI.
  3. Authentication & Authorization: This is where NestJS excels. Use the @nestjs/passport and @nestjs/jwt libraries to implement secure token-based authentication.
  4. Implement RBAC (Role-Based Access Control): Use NestJS Guards to protect endpoints based on user roles (e.g., only a supervisor can see all reports).
  5. File Uploads: Implement file uploads to an object store like S3 using NestJS's MulterModule.
  6. Deploy: Containerize your NestJS and React apps with Docker and deploy them to a HIPAA-compliant cloud provider.