Add NestJS.md

This commit is contained in:
2025-07-28 23:15:31 +00:00
parent 0d398956ff
commit ad9bc533f3

363
NestJS.md Normal file
View File

@@ -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<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:
```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 <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.