Skip to main content
The Restaurante API is built with NestJS, backed by PostgreSQL 17, and uses Drizzle ORM for all database access. Every HTTP route is served under the /api global prefix, and a single Socket.IO gateway handles real-time kitchen display.

Layered architecture

Each feature module follows a strict three-layer pattern:
HTTP Request


 Controller          ← validates input (ValidationPipe), maps routes


 Service             ← business logic, orchestrates queries


 Drizzle ORM         ← type-safe query builder


 PostgreSQL 17       ← persistent storage

Modules

auth

JWT-based authentication. Issues and validates bearer tokens used by every protected route.

usuarios

User management. Creates, updates, and soft-deletes staff accounts with roles (e.g. cajero).

caja

Cash register shifts (caja_turno). Tracks opening/closing amounts, bill denominations, and cash-out records.

productos

Non-dish retail products with stock tracking and unit pricing.

ingredientes

Ingredient inventory with quantity and minimum-stock thresholds.

platos

Menu dishes and their ingredient compositions (plato_ingredientes).

transacciones

Sales transactions, order status, kitchen state, line items, extras, and payments. Also owns the CocinaGateway WebSocket.

dashboard

Aggregated reporting queries used by the front-end dashboard.

Module responsibilities

ModuleController routesKey tables
authPOST /api/auth/loginusuarios
usuariosGET/POST/PUT/DELETE /api/usuariosusuarios
cajaGET/POST/PATCH /api/cajacaja_turno, gastos_caja
productosGET/POST/PATCH/DELETE /api/productosproductos
ingredientesGET/POST/PATCH/DELETE /api/ingredientesingredientes
platosGET/POST/PATCH/DELETE /api/platosplatos, plato_ingredientes
transaccionesGET/POST/PATCH/DELETE /api/transaccionestransacciones, detalle_items, detalle_item_extras, pagos
dashboardGET /api/dashboard/*all tables (read-only)

NestJS module structure

The root AppModule imports all feature modules and the shared infrastructure modules:
src/app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { DrizzleModule } from './drizzle/drizzle.module';
import { AuthModule } from './modules/auth/auth.module';
import { UsuariosModule } from './modules/usuarios/usuarios.module';
import { CajaModule } from './modules/caja/caja.module';
import { ProductosModule } from './modules/productos/productos.module';
import { IngredientesModule } from './modules/ingredientes/ingredientes.module';
import { PlatosModule } from './modules/platos/platos.module';
import { TransaccionesModule } from './modules/transacciones/transacciones.module';
import { DashboardModule } from './modules/dashboard/dashboard.module';

@Module({
  imports: [
    ConfigModule.forRoot({ isGlobal: true }),
    DrizzleModule,
    AuthModule,
    UsuariosModule,
    CajaModule,
    ProductosModule,
    IngredientesModule,
    PlatosModule,
    TransaccionesModule,
    DashboardModule,
  ],
})
export class AppModule {}

Global configuration

Global prefix

All routes are prefixed with /api:
app.setGlobalPrefix('api');

CORS

The server accepts requests from the following origins:
src/main.ts
const allowedOrigins = [
  'http://localhost:5173',
  'https://charqueria-oruro.vercel.app',
  'http://localhost:9000',
];

app.enableCors({
  origin: allowedOrigins,
  credentials: true,
  methods: ['GET', 'HEAD', 'PUT', 'PATCH', 'POST', 'DELETE', 'OPTIONS'],
  allowedHeaders: ['Content-Type', 'Authorization'],
});
The WebSocket gateway at /cocina uses a separate CORS configuration (origin: '*'). See WebSocket gateway for details.

Global validation pipe

Every incoming request body is validated and transformed before it reaches a controller:
app.useGlobalPipes(
  new ValidationPipe({
    whitelist: true,            // strips undeclared properties
    forbidNonWhitelisted: true, // throws on undeclared properties
    transform: true,            // coerces types automatically
  }),
);

Date formatter interceptor

All API responses pass through the global DateFormatterInterceptor (src/common/interceptors/date-formatter.interceptor.ts). It recursively walks every response object and formats any date/timestamp field into the HH:mm - dd/MM/yyyy pattern using the America/La_Paz timezone. The timezone is also set at process level so that Node.js date operations default to Bolivia time:
process.env.TZ = 'America/La_Paz';

Build docs developers (and LLMs) love