Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Distribuidos-Org/ms-alumnos/llms.txt

Use this file to discover all available pages before exploring further.

ms-alumnos persists all student data to a PostgreSQL database using TypeORM. The connection is configured in AppModule via TypeOrmModule.forRoot(), with synchronize: true enabled so that TypeORM automatically creates and alters the database schema to match the entity definitions on every application startup. This keeps the development workflow frictionless — no migration files need to be run manually — but carries important caveats in production environments (see Synchronize in production).

Connection configuration

The TypeORM connection is configured in src/app.module.ts using environment variables that are validated at startup by src/config/envs.ts:
// src/app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule } from '@nestjs/config';
import { AlumnosModule } from './alumnos/alumnos.module';
import { SeedModule } from './seed/seed.module';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
    }),

    TypeOrmModule.forRoot({
      type: 'postgres',
      host: process.env.DB_HOST,
      port: +process.env.DB_PORT!,
      username: process.env.DB_USER,
      password: process.env.DB_PASSWORD,
      database: process.env.DB_NAME,
      entities: [__dirname + '/**/*.entity{.ts,.js}'],
      synchronize: true,
    }),

    AlumnosModule,
    SeedModule,
  ],
})
export class AppModule {}
Each property in the TypeORM config:
PropertyValueDescription
type'postgres'Selects the PostgreSQL driver.
hostDB_HOST env varHostname or IP of the PostgreSQL server (e.g. localhost or a Docker service name).
portDB_PORT env varPort the PostgreSQL server listens on — coerced to a number with the unary + operator.
usernameDB_USER env varDatabase user account.
passwordDB_PASSWORD env varPassword for the database user.
databaseDB_NAME env varName of the target PostgreSQL database.
entitiesglob patternTypeORM discovers entity classes by scanning all files matching **/*.entity{.ts,.js} relative to the compiled output directory.
synchronizetrueAutomatically applies schema changes (CREATE TABLE, ALTER COLUMN, etc.) derived from entity metadata on every startup.
The required environment variables are:
DB_HOST=localhost
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=secret
DB_NAME=alumnos_db

Alumnos table schema

The alumnos table is defined by the Alumno TypeORM entity at src/alumnos/entities/alumno.entity.ts:
import { BeforeInsert, Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
import * as bcrypt from 'bcrypt';

@Entity('alumnos')
export class Alumno {
  @PrimaryGeneratedColumn({ name: 'alumno_id' })
  alumnoId: number;

  @Column({ length: 50 })
  nombre: string;

  @Column({ name: 'apellido_paterno', length: 50 })
  apellidoPaterno: string;

  @Column({ name: 'apellido_materno', length: 50 })
  apellidoMaterno?: string;

  @Column({ length: 8, unique: true })
  dni: string;

  @Column({ type: 'int', nullable: true })
  edad?: number;

  @Column({ length: 100, unique: true })
  email: string;

  @Column({ length: 15, nullable: true })
  celular?: string;

  @Column({ length: 100, default: 'UNMSM' })
  universidad: string;

  @Column({ length: 100, nullable: true })
  facultad?: string;

  @Column({ length: 50, nullable: true })
  profesion?: string;

  @Column({ length: 50, nullable: true })
  grado?: string;

  @Column({ name: 'egresado_local', default: true })
  egresadoLocal: boolean;

  @Column({ length: 60, name: 'contrasena' })
  contrasena: string;

  @BeforeInsert()
  async hashPassword() {
    this.contrasena = await bcrypt.hash(this.contrasena, 10);
  }
}
Every column in detail:
Column nameTypeScript propertyTypeConstraintsNotes
alumno_idalumnoIdSERIAL (auto-increment integer)Primary keyAuto-generated by the database on insert.
nombrenombreVARCHAR(50)NOT NULLStudent’s first name.
apellido_paternoapellidoPaternoVARCHAR(50)NOT NULLPaternal surname.
apellido_maternoapellidoMaternoVARCHAR(50)NullableMaternal surname — optional in Peruvian naming conventions.
dnidniVARCHAR(8)NOT NULL, UNIQUEPeruvian national identity document number (exactly 8 characters).
edadedadINTNullableAge in years. Optional.
emailemailVARCHAR(100)NOT NULL, UNIQUEContact email address. Must be unique across all students.
celularcelularVARCHAR(15)NullableMobile phone number. Optional.
universidaduniversidadVARCHAR(100)NOT NULL, DEFAULT 'UNMSM'University name; defaults to Universidad Nacional Mayor de San Marcos.
facultadfacultadVARCHAR(100)NullableFaculty or school within the university. Optional.
profesionprofesionVARCHAR(50)NullableDeclared profession or degree programme. Optional.
gradogradoVARCHAR(50)NullableAcademic grade or degree level. Optional.
egresado_localegresadoLocalBOOLEANNOT NULL, DEFAULT trueWhether the student graduated from a local (Peruvian) institution.
contrasenacontrasenaVARCHAR(60)NOT NULLStores a bcrypt hash of the student’s password. The 60-character length matches the bcrypt output length.

Password hashing

The Alumno entity registers a @BeforeInsert() lifecycle hook that automatically hashes the contrasena field before TypeORM issues the INSERT statement:
@BeforeInsert()
async hashPassword() {
  this.contrasena = await bcrypt.hash(this.contrasena, 10);
}
The hook uses bcrypt.hash with a cost factor of 10, which produces a 60-character hash string — matching the VARCHAR(60) column definition exactly. Because the hook runs transparently as part of the TypeORM entity lifecycle, callers can pass a plain-text password in CreateAlumnoDto and the service layer never needs to handle hashing manually.
The @BeforeInsert() hook fires on INSERT only. It does not fire on UPDATE. If a student’s password needs to be changed, the service layer (or the caller) is responsible for hashing the new password with bcrypt.hash before passing it to the update method. Failing to do so will store a plain-text password in the database.

Docker Compose

A docker-compose.yml is provided at the project root to spin up a local PostgreSQL instance for development:
services:
  db:
    image: postgres:15
    restart: always
    environment:
      POSTGRES_USER: ${DB_USER}
      POSTGRES_DB: ${DB_NAME}
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    ports:
      - "${DB_PORT}:5432"
    volumes:
      - db_data:/var/lib/postgresql/data

volumes:
  db_data:
The compose file reads DB_USER, DB_NAME, DB_PASSWORD, and DB_PORT from a .env file in the same directory. The db_data named volume ensures that database contents survive container restarts. To start the database:
# Start PostgreSQL in the background
docker compose up -d db

# Confirm the container is healthy
docker compose ps
Once the container is running, start the NestJS microservice normally (npm run start:dev). Because synchronize: true is set, TypeORM will create the alumnos table automatically on first run.

Synchronize in production

The synchronize: true setting in TypeOrmModule.forRoot() instructs TypeORM to automatically apply schema changes — including dropping columns and altering data types — every time the application starts. This is convenient during development but is unsafe in production environments where the database contains real data.Before deploying to production, set synchronize: false and manage schema changes using explicit TypeORM migrations (typeorm migration:generate, typeorm migration:run). Migrations are version-controlled, auditable, and reversible — unlike automatic synchronisation.

Build docs developers (and LLMs) love