Backend en NestJS con Prisma, pg y Joi para TrueRate - Rommel Romero
←Volver al blog
Fase 1 — El Backend Core

Arquitectura backend de TrueRate: NestJS y Prisma

Cómo estructurar el backend core de TrueRate usando NestJS, validación estricta de entorno con Joi y Prisma conectado mediante el adaptador nativo de PostgreSQL.

NestJSPrismaPostgreSQLTypeScriptTrueRateBuild in Public

En el artículo anterior dejamos listos los cimientos de TrueRate: un modelo relacional robusto de 8 tablas en PostgreSQL diseñado para aislar la seguridad, controlar costes fijos y auditar el flujo de caja real de un freelancer.

Tener una base de datos impecable sobre el papel no sirve de nada si la capa de aplicación es frágil. En este post te cuento cómo he dado el salto al código levantando el backend con NestJS, asegurando un arranque a prueba de fallos con Joi y conectando la persistencia mediante Prisma apoyado en el adaptador moderno de pg.


1. De la base de datos al código

Cuando montas un SaaS financiero, no puedes permitirte que la aplicación arranque con variables de entorno vacías o conexiones mal configuradas a mitad de producción. Necesitábamos una arquitectura modular, limpia y con tipado estricto en TypeScript.

Para ello, estructuramos la conexión en tres piezas clave:

  1. Construcción tipada de la URL: Un utilitario puro que centraliza las credenciales y arma la cadena de conexión de PostgreSQL de forma segura.
  2. Validación estricta con Joi: Un esquema que actúa como un muro de contención en el arranque de NestJS; si falta cualquier parámetro crítico en el archivo .env, la aplicación se detiene inmediatamente (fail-fast).
  3. PrismaService adaptado: Integración del cliente de Prisma utilizando por debajo el adaptador nativo de pg (@prisma/adapter-pg), aprovechando el rendimiento del pool de conexiones de Node.

2. Implementación técnica en NestJS

Aquí tienes el código real que da vida a los cimientos de nuestra API:

La construcción de la URL de conexión

export interface DatabaseConfig {
  host: string;
  port: number;
  user: string;
  password: string;
  name: string;
  schema: string;
}

export function buildDatabaseUrl(config: DatabaseConfig): string {
  return `postgresql://${config.user}:${config.password}@${config.host}:${config.port}/${config.name}?schema=${config.schema}`;
}

Validación de entorno estricta con Joi

import Joi from 'joi';

export const validationSchema = Joi.object({
  DB_HOST: Joi.string().required(),
  DB_PORT: Joi.number().port().required(),
  DB_USER: Joi.string().required(),
  DB_PASSWORD: Joi.string().required(),
  DB_NAME: Joi.string().required(),
  DB_SCHEMA: Joi.string().required(),
});

El PrismaService integrado en el ciclo de vida de NestJS

import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { PrismaClient } from '../generated/prisma/client.js';
import { PrismaPg } from '@prisma/adapter-pg';
import { buildDatabaseUrl } from '../config/database-url.js';

@Injectable()
export class PrismaService
  extends PrismaClient
  implements OnModuleInit, OnModuleDestroy
{
  constructor(configService: ConfigService) {
    super({
      adapter: new PrismaPg({
        connectionString: buildDatabaseUrl({
          host: configService.getOrThrow<string>('DB_HOST'),
          port: configService.getOrThrow<number>('DB_PORT'),
          user: configService.getOrThrow<string>('DB_USER'),
          password: configService.getOrThrow<string>('DB_PASSWORD'),
          name: configService.getOrThrow<string>('DB_NAME'),
          schema: configService.getOrThrow<string>('DB_SCHEMA'),
        }),
      }),
    });
  }

  async onModuleInit() {
    await this.$connect();
  }

  async onModuleDestroy() {
    await this.$disconnect();
  }
}

3. Schema-first: Prisma como fuente de verdad

Con el motor de conexión y la validación de entorno blindados, el siguiente paso fue meternos de lleno con la persistencia. En lugar de diseñar las tablas a mano en la base de datos y traerlas después, opté por el enfoque schema-first, definir todo el modelo relacional directamente en el archivo schema.prisma y dejar que el propio ORM levante y controle la base de datos.

La clave de este flujo con prisma migrate dev es que el código se convierte en la única fuente de verdad:

Con el backend funcionando y la base de datos versionada desde el código, el terreno está totalmente listo. En el siguiente artículo nos meteremos de fondo con la arquitectura modular del proyecto.


← Más artículos