From ad9bc533f3ff5bde21f32cd28b026a61d6c57fbb Mon Sep 17 00:00:00 2001 From: Angelo Date: Mon, 28 Jul 2025 23:15:31 +0000 Subject: [PATCH] Add NestJS.md --- NestJS.md | 363 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 363 insertions(+) create mode 100644 NestJS.md diff --git a/NestJS.md b/NestJS.md new file mode 100644 index 0000000..6a29ca8 --- /dev/null +++ b/NestJS.md @@ -0,0 +1,363 @@ +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](https://nodejs.org/) +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](https://www.docker.com/products/docker-desktop/) +3. **NestJS CLI:** The command-line interface for creating and managing NestJS projects. + ```bash + npm i -g @nestjs/cli + ``` +4. **Git:** For version control. [Download Git](https://git-scm.com/downloads) +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. + +```bash +# 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:** +```yaml +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: +```bash +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.** + + ```bash + 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. + + ```bash + 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. + + ```typescript + 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. + + ```bash + 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. + + ```typescript + 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`: + + ```typescript + // 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). + + ```typescript + 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, + ) {} + + create(createIncidentDto: CreateIncidentDto): Promise { + const newIncident = this.incidentsRepository.create(createIncidentDto); + return this.incidentsRepository.save(newIncident); + } + + findAll(): Promise { + return this.incidentsRepository.find(); + } + + findOne(id: string): Promise { + 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: + + ```typescript + 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: + ```json + { + "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.** + + ```bash + # (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.** + ```bash + 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:** + ```typescript + 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:** + ```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
Error: {error}
; + + return ( +
+

Incident Reports

+
    + {incidents.length > 0 ? ( + incidents.map(incident => ( +
  • + {incident.location}: {incident.description} ({incident.status}) +
  • + )) + ) : ( +

    No incidents reported yet. Create one via Postman!

    + )} +
+
+ ); + } + + 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. \ No newline at end of file