Documentation Index Fetch the complete documentation index at: https://mintlify.com/LizandroCanul/back_sdo/llms.txt
Use this file to discover all available pages before exploring further.
Overview
The Yucatan Public Works API uses TypeORM as its ORM layer with PostgreSQL as the database backend. Geographic data is handled using PostGIS extensions for spatial operations.
Key Entities
Obra (Public Work)
The central entity representing a public works project.
@ Entity ( 'obras' )
export class Obra {
@ PrimaryGeneratedColumn ( 'increment' )
id : number ;
@ Column ({ name: 'numero_obra' , type: 'int' })
numeroObra : number ; // Auto-calculated annual consecutive number
@ Column ({ name: 'clave_unica' , length: 50 , unique: true })
claveUnica : string ; // Unique identifier
@ Column ({ length: 255 })
nombre : string ;
@ Column ({ type: 'text' , nullable: true })
descripcion : string ;
@ Column ({ type: 'decimal' , precision: 18 , scale: 2 , default: 0 })
monto : number ; // Budget amount
// Foreign key relationships
@ ManyToOne (() => EjercicioFiscal , { nullable: false , eager: true })
ejercicioFiscal : EjercicioFiscal ;
@ ManyToOne (() => Dependencia , { nullable: false , eager: true })
dependencia : Dependencia ;
@ ManyToOne (() => Municipio , { nullable: false , eager: true })
municipio : Municipio ;
@ ManyToOne (() => TipoProyecto , { nullable: true , eager: true })
tipoProyecto : TipoProyecto ;
@ ManyToOne (() => EstatusObra , { nullable: false , eager: true })
estatusObra : EstatusObra ;
// Child relationships
@ OneToMany (() => ObraUbicacion , ( ubicacion ) => ubicacion . obra , {
cascade: true
})
ubicaciones : ObraUbicacion [];
}
Key Fields:
numeroObra: Auto-generated annual consecutive number
claveUnica: Unique project identifier (max 50 chars)
monto: Budget with 2 decimal precision (up to 18 digits)
Uses eager: true for automatic relation loading
ObraUbicacion (Location)
Handles geographic data for public works using PostGIS.
export enum TipoGeometria {
PUNTO = 'PUNTO' ,
RUTA = 'RUTA' ,
POLIGONO = 'POLIGONO' ,
}
@ Entity ( 'obra_ubicaciones' )
export class ObraUbicacion {
@ PrimaryGeneratedColumn ( 'increment' )
id : number ;
@ ManyToOne (() => Municipio , { nullable: false })
municipio : Municipio ;
@ Column ({ length: 255 })
direccion : string ;
@ Column ({ name: 'localidad_referencia' , length: 150 , nullable: true })
localidadReferencia : string ;
@ Column ({ name: 'referencia_lugar' , length: 150 , nullable: true })
referenciaLugar : string ;
@ Column ({ type: 'enum' , enum: TipoGeometria , default: TipoGeometria . PUNTO })
tipoGeometria : TipoGeometria ;
@ Column ({ name: 'geometria_json' , type: 'json' })
geometriaJson : any ; // GeoJSON data
@ Column ({ default: 0 })
orden : number ;
@ ManyToOne (() => Obra , ( obra ) => obra . ubicaciones , { onDelete: 'CASCADE' })
obra : Obra ;
}
Each location can be a PUNTO (point), RUTA (line), or POLIGONO (polygon). Geographic data is stored as JSON for flexibility.
User
User authentication and authorization entity.
@ Entity ( 'users' )
export class User {
@ PrimaryGeneratedColumn ( 'uuid' )
id : string ; // UUID for security
@ Column ( 'text' )
nombreCompleto : string ;
@ Column ( 'text' , { unique: true })
email : string ;
@ Column ( 'text' , { select: false })
password : string ; // Never returned in queries
@ Column ( 'text' , { default: 'user' })
roles : string ; // 'admin' | 'user'
@ Column ( 'bool' , { default: true })
isActive : boolean ;
@ Column ( 'bool' , { default: true })
mustChangePassword : boolean ;
@ CreateDateColumn ()
createdAt : Date ;
@ UpdateDateColumn ()
updatedAt : Date ;
@ OneToMany (() => UserFavoriteObra , ( fav ) => fav . user , { cascade: true })
favoritasObras : UserFavoriteObra [];
}
The password field uses select: false to prevent accidental exposure. You must explicitly select it when needed.
Municipio (Municipality)
Municipality entity with PostGIS spatial indexing.
import type { Point } from 'geojson' ;
@ Entity ( 'municipios' )
export class Municipio {
@ PrimaryGeneratedColumn ()
id : number ;
@ Column ({ type: 'text' , unique: true })
nombre : string ;
// PostGIS geometry column
@ Index ({ spatial: true })
@ Column ({
type: 'geometry' ,
spatialFeatureType: 'Point' ,
srid: 4326 , // WGS84 coordinate system
nullable: true ,
})
ubicacion : Point ;
@ Column ({ type: 'boolean' , default: true })
activo : boolean ; // Soft delete
@ CreateDateColumn ({ select: false })
created_at : Date ;
@ UpdateDateColumn ({ select: false })
updated_at : Date ;
}
PostGIS Integration:
Uses geometry type with spatial indexing
srid: 4326 = WGS84 (standard GPS coordinates)
Supports spatial queries and distance calculations
Catalog Entities
Simple catalog tables for dropdowns and classifications.
Dependencia
EjercicioFiscal
TipoProyecto
EstatusObra
@ Entity ( 'dependencias' )
export class Dependencia {
@ PrimaryGeneratedColumn ()
id : number ;
@ Column ({ type: 'text' , unique: true })
nombre : string ;
@ Column ({ type: 'text' , nullable: true })
siglas : string ;
@ Column ({ type: 'boolean' , default: true })
activo : boolean ;
}
Entity Relationships
Obra → Municipio
Many obras belong to one municipio (nullable: false, eager: true)
Obra → Dependencia
Many obras belong to one dependencia (nullable: false, eager: true)
Obra → EjercicioFiscal
Many obras belong to one fiscal year (nullable: false, eager: true)
Obra → TipoProyecto
Many obras can have one project type (nullable: true, eager: true)
Obra → EstatusObra
Many obras have one status (nullable: false, eager: true)
Obra ↔ ObraUbicacion
One obra has many ubicaciones (cascade: true, onDelete: CASCADE)
User ↔ Obra (Favorites)
Many-to-many through UserFavoriteObra junction table
UserFavoriteObra Junction Table
user-favorite-obra.entity.ts
@ Entity ( 'user_favorite_obra' )
@ Unique ([ 'user' , 'obra' ]) // Prevents duplicate favorites
export class UserFavoriteObra {
@ PrimaryGeneratedColumn ()
id : number ;
@ ManyToOne (() => User , ( user ) => user . favoritasObras , { onDelete: 'CASCADE' })
user : User ;
@ Column ({ name: 'user_id' , type: 'uuid' })
userId : string ;
@ ManyToOne (() => Obra , ( obra ) => obra . usuariosQueLoMarcanFavorita , { onDelete: 'CASCADE' })
obra : Obra ;
@ Column ({ name: 'obra_id' })
obraId : number ;
@ CreateDateColumn ()
createdAt : Date ;
}
Foreign Key Conventions
The project follows these TypeORM patterns:
Relation Column Stores the full related entity @ ManyToOne (() => Municipio )
municipio : Municipio ;
ID Column Stores just the foreign key ID @ Column ({ name: 'municipio_id' })
municipioId : number ;
Eager Loading Automatically loads relations @ ManyToOne (() => Municipio , { eager: true })
Cascade Operations Auto-saves/deletes children @ OneToMany ( ... , { cascade: true })
Soft Deletes
Catalog entities use the activo boolean field for soft deletion:
@ Column ({ type: 'boolean' , default: true })
activo : boolean ;
This preserves historical data while hiding inactive records from normal queries.
Next Steps
Validation Learn about DTO validation patterns
Error Handling Understand error responses