Template - API
WelcomePrerequisitesNodeJS
  • Welcome!
    • About me
  • Prerequisites
    • Installation and Database Setup
    • Installing Node.js on the Server
  • NodeJS
    • πŸ†“Getting Started with the Free Template
    • πŸŽ—οΈPremium Template - REST API
      • Structure
      • Middlewares and Guards
      • Core Modules
        • Prisma with NestJS
        • Swagger with NestJS
      • Dynamic Module
        • Create or Import Module
  • Modules
    • πŸŽ—οΈWebsockets
    • πŸŽ—οΈMailer
    • πŸŽ—οΈUpload
Powered by GitBook
On this page

NodeJS

erview

This section provides exemple guide on how the API functions, including the structure, key endpoints, and how to interact with the system. This API is designed to provide a robust and scalable solution for developers, with clear separation of concerns through models, controllers, and services.


Here are some basic examples:

API Routes

The API is built using NestJS, and the routes are defined in the src/users/ module. Each route points to a specific controller that handles the business logic through service methods.

User Routes

  • GET /users

    Description: Fetches all users from the database.

    Response: An array of users in JSON format.

    Status Codes:

    • 200: Success

    • 500: Internal server error

  • POST /users

    Description: Creates a new user.

{
  "name": "John Doe",
  "email": "john.doe@example.com"
}

Response: The newly created user.

Status Codes:

  • 201: Created

  • 400: Bad request (e.g., invalid input)

  • 500: Internal server error

The API routes are centralized in the UsersModule and handled by the UsersController as follows:

// src/users/users.controller.ts
import { Controller, Get, Post, Body } from '@nestjs/common';
import { UsersService } from './users.service';

@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Get()
  getAllUsers() {
    return this.usersService.findAll();
  }

  @Post()
  createUser(@Body() createUserDto: CreateUserDto) {
    return this.usersService.create(createUserDto);
  }
}

The business logic is handled by the UsersService, and the CreateUserDto is a data transfer object (DTO) for validation.


Controllers

Controllers handle the business logic for each API endpoint. In NestJS, controllers are defined within a specific module folder (e.g., src/users/), and they communicate with services to fetch or manipulate data.

User Controller

createUser: Creates a new user with the data provided in the request body.

// src/users/users.controller.ts
import { Controller, Post, Body, Res, HttpStatus } from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';
import { Response } from 'express';

@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Post()
  async createUser(@Body() createUserDto: CreateUserDto, @Res() res: Response) {
    try {
      const user = await this.usersService.create(createUserDto);
      return res.status(HttpStatus.CREATED).json(user);
    } catch (error) {
      return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ message: 'Server Error' });
    }
  }
}

getAllUsers: Fetches all users from the database and returns them in JSON format.

import { Controller, Get, Res, HttpStatus } from '@nestjs/common';
import { UsersService } from './users.service';
import { Response } from 'express';

@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Get()
  async getAllUsers(@Res() res: Response) {
    try {
      const users = await this.usersService.findAll();
      return res.status(HttpStatus.OK).json(users);
    } catch (error) {
      return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ message: 'Server Error' });
    }
  }
}

Explanation:

  • In NestJS, controllers handle the HTTP requests and responses.

  • The UsersController communicates with the UsersService to handle database operations.

  • The CreateUserDto is used to validate and transfer the user data in createUser.


Models

In NestJS, models are defined using TypeORM, an ORM (Object Relational Mapping) tool that allows easy interaction with the database. The models are located in the src/users/ folder and define the structure of the tables in the database.

User Model

The User model represents a user in the system. It defines the columns and data types in the database, such as id, name, and email.

// src/users/user.entity.ts
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column({ type: 'varchar', length: 255, nullable: false })
  name: string;

  @Column({ type: 'varchar', length: 255, nullable: false, unique: true })
  email: string;
}

Explanation:

  • Entity: The User class is marked as an entity with the @Entity() decorator, representing a table in the database.

  • PrimaryGeneratedColumn: The id field is automatically generated and serves as the primary key.

  • Column: The name and email fields are defined with specific types and constraints (e.g., varchar, unique, nullable).


Middlewares

Middlewares are used to handle requests before they reach the route handler. They are applied globally or to specific routes to enforce security policies, handle errors, or validate input.

Cors Middleware

The CORS functionality can be handled directly in the main.ts file of the NestJS application using built-in middleware, but if you want to implement a custom CORS middleware like the one in your

// src/common/middleware/cors.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class CorsMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
    res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
    next();
  }
}

Applying the Middleware

To apply this custom middleware globally or to specific routes, it needs to be configured in your module:

// src/app.module.ts
import { Module, MiddlewareConsumer, RequestMethod } from '@nestjs/common';
import { CorsMiddleware } from './common/middleware/cors.middleware';

@Module({
  imports: [],
  controllers: [],
  providers: [],
})
export class AppModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(CorsMiddleware)
      .forRoutes({ path: '*', method: RequestMethod.ALL });
  }
}

Explanation:

  • CorsMiddleware: The custom middleware is implemented as a class using the NestMiddleware interface.

  • use() method: It applies the necessary CORS headers and calls next() to pass control to the next middleware or route handler.

  • AppModule: The middleware is applied globally to all routes using .forRoutes({ path: '*', method: RequestMethod.ALL }).

Alternatively, CORS can also be configured in the main.ts file with:

app.enableCors({
  origin: '*',
  methods: 'GET,POST,PUT,DELETE',
  allowedHeaders: 'Content-Type,Authorization',
});

Error Handling

Errors are caught at the controller level, and proper status codes and messages are returned. For centralized error handling, you can add an error-handling middleware like the following:

// src/common/middleware/error-handler.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class ErrorHandlerMiddleware implements NestMiddleware {
  use(err: any, req: Request, res: Response, next: NextFunction) {
    const statusCode = res.statusCode !== 200 ? res.statusCode : 500;
    res.status(statusCode).json({
      message: err.message,
      stack: process.env.NODE_ENV === 'production' ? null : err.stack,
    });
  }
}

Applying the Middleware

To apply the error-handling middleware globally or to specific routes, configure it in the AppModule:

// src/app.module.ts
import { Module, MiddlewareConsumer } from '@nestjs/common';
import { ErrorHandlerMiddleware } from './common/middleware/error-handler.middleware';

@Module({
  imports: [],
  controllers: [],
  providers: [],
})
export class AppModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(ErrorHandlerMiddleware)
      .forRoutes('*'); // Apply to all routes
  }
}

Authentication

In the full version of the API, JWT authentication is implemented to secure certain routes. Here's a basic example of how JWT middleware can be used:

// src/auth/jwt-auth.guard.ts
import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { Request } from 'express';

@Injectable()
export class JwtAuthGuard implements CanActivate {
  constructor(private readonly jwtService: JwtService) {}

  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest<Request>();
    const token = this.extractTokenFromHeader(request);

    if (!token) {
      throw new UnauthorizedException('Access denied.');
    }

    try {
      const verified = this.jwtService.verify(token, {
        secret: process.env.JWT_SECRET,
      });
      request.user = verified;
      return true;
    } catch (error) {
      throw new UnauthorizedException('Invalid token');
    }
  }

  private extractTokenFromHeader(request: Request): string | null {
    const authHeader = request.headers.authorization;
    if (!authHeader) {
      return null;
    }

    const token = authHeader.replace('Bearer ', '');
    return token || null;
  }
}

Testing

Unit tests for the API are written using Jest and Supertest to ensure the functionality of the routes and controllers in NestJS. Here’s an example of a test for the GET /users route:

import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from '../src/app.module';

describe('UsersController (e2e)', () => {
  let app: INestApplication;

  beforeAll(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [AppModule],
    }).compile();

    app = moduleFixture.createNestApplication();
    await app.init();
  });

  it('/users (GET) should return all users', async () => {
    const res = await request(app.getHttpServer()).get('/users');
    expect(res.status).toBe(200);
    expect(Array.isArray(res.body)).toBeTruthy();
  });

  afterAll(async () => {
    await app.close();
  });
});

Explanation:

  • TestingModule: In NestJS, tests are created with a TestingModule that allows you to instantiate and initialize the application or modules you want to test.

  • request(app.getHttpServer()): This method creates an HTTP server for the NestJS application, which is then used with Supertest to make requests.

  • beforeAll/afterAll: These hooks initialize and tear down the application before and after all tests are run.


PreviousInstalling Node.js on the ServerNextGetting Started with the Free Template

Last updated 8 months ago