Overview
The POS Nest API uses TypeORM as its Object-Relational Mapping (ORM) layer, connecting to a PostgreSQL database. TypeORM handles entity management, relationships, and database synchronization.
TypeORM Configuration
The database configuration is managed through a factory function that injects environment variables:
src/config/typeorm.config.ts
import { ConfigService } from '@nestjs/config' ;
import type { TypeOrmModuleOptions } from '@nestjs/typeorm' ;
import { join } from 'path' ;
export const typeOrmConfig = (
configService : ConfigService ,
) : TypeOrmModuleOptions => ({
type: 'postgres' ,
host: configService . get ( 'DATABASE_HOST' ),
port: configService . get ( 'DATABASE_PORT' ),
username: configService . get ( 'DATABASE_USER' ),
password: configService . get ( 'DATABASE_PASS' ),
database: configService . get ( 'DATABASE_NAME' ),
ssl: {
rejectUnauthorized: false ,
},
logging: false ,
entities: [ join ( __dirname , '..' , '**' , '*.entity.{ts,js}' )],
synchronize: true ,
});
Configuration Parameters
Database type - PostgreSQL is used for this application
Auto-creates database tables from entities. Disable in production!
Auto-discovers all files matching *.entity.ts pattern
Enables SSL connections (required for most cloud databases)
Set synchronize: false in production. Use migrations instead to avoid data loss.
Module Registration
TypeORM is registered asynchronously in the root module:
import { TypeOrmModule } from '@nestjs/typeorm' ;
import { ConfigService } from '@nestjs/config' ;
import { typeOrmConfig } from './config/typeorm.config' ;
@ Module ({
imports: [
TypeOrmModule . forRootAsync ({
useFactory: typeOrmConfig ,
inject: [ ConfigService ],
}),
// ... other modules
],
})
export class AppModule {}
Database Schema
The application uses four main entities with the following relationships:
Entities
Category Entity
Represents product categories:
src/categories/entities/category.entity.ts
import { Product } from '../../products/entities/product.entity' ;
import { BaseEntity , Column , Entity , OneToMany , PrimaryGeneratedColumn } from 'typeorm' ;
@ Entity ()
export class Category extends BaseEntity {
@ PrimaryGeneratedColumn ()
id : number ;
@ Column ({ type: 'varchar' , length: 60 })
name : string ;
@ OneToMany (() => Product , ( product ) => product . category , { cascade: true })
products : Product []
}
Relationships:
One-to-Many with Products (one category can have many products)
Cascade operations enabled (deleting a category affects its products)
Product Entity
Represents products available for sale:
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 ;
}
Key Features:
Default image value (default.svg)
Decimal price type for accurate currency handling
Many-to-One relationship with Category
Transaction Entities
Represents sales transactions and their contents:
src/transactions/entities/transaction.entity.ts
import { Product } from '../../products/entities/product.entity' ;
import {
Column ,
Entity ,
ManyToOne ,
OneToMany ,
PrimaryGeneratedColumn ,
} from 'typeorm' ;
@ Entity ()
export class Transaction {
@ PrimaryGeneratedColumn ()
id : number ;
@ Column ({ type: 'uuid' , nullable: true })
userId : string ;
@ Column ( 'decimal' )
total : number ;
@ Column ({ type: 'varchar' , length: 30 , nullable: true })
coupon : string ;
@ Column ({ type: 'decimal' , default: 0 })
discount : number ;
@ Column ({ type: 'timestamp without time zone' })
transactionDate : Date ;
@ OneToMany (
() => TransactionContents ,
( transactions ) => transactions . transaction ,
)
contents : TransactionContents [];
}
@ Entity ()
export class TransactionContents {
@ PrimaryGeneratedColumn ()
id : number ;
@ Column ( 'int' )
quantity : number ;
@ Column ( 'decimal' )
price : number ;
@ ManyToOne (() => Product , ( product ) => product . id , {
eager: true ,
cascade: true ,
})
product : Product ;
@ ManyToOne (() => Transaction , ( transaction ) => transaction . contents , {
cascade: true ,
})
transaction : Transaction ;
}
Relationships:
Transaction has many TransactionContents (one-to-many)
TransactionContents references Product (many-to-one with eager loading)
Supports coupon codes and discount tracking
Coupon Entity
Represents discount coupons:
src/coupons/entities/coupon.entity.ts
import { Column , Entity , PrimaryGeneratedColumn } from 'typeorm' ;
@ Entity ()
export class Coupon {
@ PrimaryGeneratedColumn ()
id : number ;
@ Column ({ type: 'varchar' , length: 30 })
name : string ;
@ Column ({ type: 'int' })
percentage : number ;
@ Column ({ type: 'date' })
expirationDate : Date ;
}
Entity Relationships
One-to-Many
Categories to Products:
// In Category entity
@ OneToMany (() => Product , ( product ) => product . category , { cascade: true })
products : Product []
// In Product entity
@ ManyToOne (() => Category )
category : Category ;
Cascade Operations
Cascade options determine how operations propagate through relationships:
cascade: true - All operations cascade
cascade: ['insert', 'update'] - Only specific operations cascade
Eager Loading
The eager: true option automatically loads related entities:
@ ManyToOne (() => Product , ( product ) => product . id , {
eager: true , // Product automatically loaded with TransactionContents
cascade: true ,
})
product : Product ;
Repository Pattern
Services interact with the database using TypeORM repositories:
import { Injectable } from '@nestjs/common' ;
import { InjectRepository } from '@nestjs/typeorm' ;
import { Repository } from 'typeorm' ;
import { Product } from './entities/product.entity' ;
@ Injectable ()
export class ProductsService {
constructor (
@ InjectRepository ( Product )
private readonly productRepository : Repository < Product >,
) {}
async findAll () {
return await this . productRepository . find ();
}
async findOne ( id : number ) {
return await this . productRepository . findOne ({ where: { id } });
}
async create ( createProductDto : CreateProductDto ) {
const product = this . productRepository . create ( createProductDto );
return await this . productRepository . save ( product );
}
async update ( id : number , updateProductDto : UpdateProductDto ) {
await this . productRepository . update ( id , updateProductDto );
return this . findOne ( id );
}
async remove ( id : number ) {
return await this . productRepository . delete ( id );
}
}
Column Types
Common column types used in the application:
TypeORM Type PostgreSQL Type Use Case varcharVARCHAR Short text (names, emails) decimalNUMERIC Prices, monetary values intINTEGER Counts, IDs uuidUUID User IDs (from Supabase) timestampTIMESTAMP Date and time dateDATE Dates only
Environment Variables
Required database configuration variables:
DATABASE_HOST = your-database-host
DATABASE_PORT = 5432
DATABASE_USER = your-username
DATABASE_PASS = your-password
DATABASE_NAME = pos_nest_db
Best Practices
Use Repositories Always inject and use TypeORM repositories for database operations
Disable Synchronize Set synchronize: false in production and use migrations
Handle Relationships Use TypeORM decorators to define entity relationships clearly
Use Transactions Wrap complex operations in database transactions
Migrations
This application currently uses synchronize: true for automatic schema updates. For production, implement TypeORM migrations:
# Generate migration
npm run migration:generate -- -n MigrationName
# Run migrations
npm run migration:run
# Revert migration
npm run migration:revert
Architecture Learn about the overall application structure
API Reference Explore database-backed endpoints
Error Handling Handle database errors properly