Overview
The POS Nest API follows a modular architecture based on NestJS best practices, implementing a clear separation of concerns through modules, controllers, services, and entities.
NestJS Module Structure
The application is organized into feature modules, each responsible for a specific domain:
Root Module Configuration
The AppModule serves as the application’s entry point, registering all feature modules and global providers:
import { Module } from '@nestjs/common' ;
import { ConfigModule , ConfigService } from '@nestjs/config' ;
import { TypeOrmModule } from '@nestjs/typeorm' ;
import { APP_GUARD } from '@nestjs/core' ;
import { AppController } from './app.controller' ;
import { AppService } from './app.service' ;
import { CategoriesModule } from './categories/categories.module' ;
import { typeOrmConfig } from './config/typeorm.config' ;
import { ProductsModule } from './products/products.module' ;
import { TransactionsModule } from './transactions/transactions.module' ;
import { CouponsModule } from './coupons/coupons.module' ;
import { UploadImageModule } from './upload-image/upload-image.module' ;
import { AuthModule } from './auth/auth.module' ;
import { SupabaseAuthGuard } from './auth/guards/supabase-auth.guard' ;
import { RolesGuard } from './auth/guards/roles.guard' ;
@ Module ({
imports: [
ConfigModule . forRoot ({
isGlobal: true ,
}),
TypeOrmModule . forRootAsync ({
useFactory: typeOrmConfig ,
inject: [ ConfigService ],
}),
AuthModule ,
CategoriesModule ,
ProductsModule ,
TransactionsModule ,
CouponsModule ,
UploadImageModule ,
],
controllers: [ AppController ],
providers: [
AppService ,
{
provide: APP_GUARD ,
useClass: SupabaseAuthGuard ,
},
{
provide: APP_GUARD ,
useClass: RolesGuard ,
},
],
})
export class AppModule {}
Global guards are registered at the application level, ensuring authentication and authorization checks run on all routes by default.
MVC Pattern: Controllers, Services, and Entities
The application follows the Model-View-Controller pattern adapted for APIs:
Controllers
Controllers handle HTTP requests and delegate business logic to services:
src/products/products.controller.ts
import {
Controller ,
Get ,
Post ,
Body ,
Patch ,
Param ,
Delete ,
Query ,
} from '@nestjs/common' ;
import { ProductsService } from './products.service' ;
import { CreateProductDto } from './dto/create-product.dto' ;
import { Public } from '../auth/decorators/public.decorator' ;
import { Roles } from '../auth/decorators/roles.decorator' ;
@ Controller ( 'products' )
export class ProductsController {
constructor ( private readonly productsService : ProductsService ) {}
@ Post ()
@ Roles ( 'admin' )
create (@ Body () createProductDto : CreateProductDto ) {
return this . productsService . create ( createProductDto );
}
@ Get ()
@ Public ()
findAll (@ Query () query : GetProductsQueryDto ) {
return this . productsService . findAll ( category , take , skip );
}
@ Get ( ':id' )
@ Public ()
findOne (@ Param ( 'id' , IdValidationPipe ) id : string ) {
return this . productsService . findOne ( + id );
}
}
Services
Services contain business logic and interact with the database through repositories:
Encapsulate business logic
Handle data manipulation
Interact with TypeORM repositories
Throw appropriate exceptions
Entities
Entities define the database schema and relationships using TypeORM decorators:
src/products/entities/product.entity.ts
import { Category } from '../../categories/entities/category.entity' ;
import { Column , Entity , ManyToOne , PrimaryGeneratedColumn } from 'typeorm' ;
@ Entity ()
export class Product {
@ PrimaryGeneratedColumn ()
id : number ;
@ Column ({ type: 'varchar' , length: 60 })
name : string ;
@ Column ({
type: 'varchar' ,
length: 255 ,
nullable: true ,
default: 'default.svg' ,
})
image : string ;
@ Column ({ type: 'decimal' })
price : number ;
@ Column ({ type: 'int' })
inventory : number ;
@ Column ()
categoryId : number ;
@ ManyToOne (() => Category )
category : Category ;
}
Request Lifecycle
Every HTTP request flows through several layers:
Request Flow Breakdown
Middleware : CORS, body parsing, static file serving
Guards : Authentication (SupabaseAuthGuard) → Authorization (RolesGuard)
Pipes : Validation (ValidationPipe), transformation (IdValidationPipe)
Controller : Route handler execution
Service : Business logic processing
Repository/Database : Data persistence
Module Dependencies
Feature modules can import other modules to use their services:
@ Module ({
imports: [ TypeOrmModule . forFeature ([ Product , Category ])],
controllers: [ ProductsController ],
providers: [ ProductsService ],
exports: [ ProductsService ], // Make service available to other modules
})
export class ProductsModule {}
The ConfigModule is marked as global, making configuration available to all modules without explicit imports.
Global Configuration
Environment Configuration
The application uses @nestjs/config for environment variable management:
import { NestFactory } from '@nestjs/core' ;
import { AppModule } from './app.module' ;
import { ValidationPipe } from '@nestjs/common' ;
async function bootstrap () {
const app = await NestFactory . create ( AppModule );
app . enableCors ({
origin: true ,
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS' ,
allowedHeaders: 'Content-Type,Authorization' ,
credentials: true ,
});
app . useGlobalPipes (
new ValidationPipe ({
whitelist: true ,
}),
);
await app . listen ( process . env . PORT ?? 3000 );
}
bootstrap ();
Global Pipes
The ValidationPipe is configured globally with:
whitelist: true - Strips properties not in the DTO
Automatic validation of all DTOs using class-validator
Best Practices
Single Responsibility Each module handles one feature domain
Dependency Injection Services injected via constructors
Separation of Concerns Controllers handle routing, services handle logic
Type Safety TypeScript ensures compile-time type checking
Authentication Learn about Supabase authentication and guards
Database Understand TypeORM configuration and entities
Error Handling Handle exceptions and validation errors
API Reference Explore available endpoints