TypeScript con MongoDB: documentos, filtros y DTOs | Nicolás Garzón
Inicio Wiki MongoDB TypeScript con MongoDB Volver a MongoDBMongoDB
TypeScript con MongoDB Explica cómo tipar documentos, colecciones, filtros, updates y resultados del driver sin confundir tipos de TypeScript con validación real de datos.
Última actualización Actualizada 25 de jul de 2026
TypeScript mejora autocomplete y detecta incompatibilidades durante desarrollo, pero no valida JSON recibido ni documentos existentes. Un tipo es una promesa del código; el dato BSON sigue siendo runtime.
Nota anteriorMongoDB con Node.js Nota siguiente Mongoose Texto
Copiar input HTTP
→ validación runtime
→ modelo de dominio
→ documento BSON
→ DTO públicoSeparar estas representaciones evita assertions, fugas de campos y conversiones incorrectas.
TypeScript
Copiar type OrderDocument = {
_id: ObjectId;
businessId: ObjectId;
status: 'pending' | 'confirmed' | 'cancelled' ;
total: Decimal128;
createdAt: Date;
schemaVersion: number ;
} ; Este tipo describe cómo esperas encontrar el documento. No demuestra que todos los registros cumplan. Usa schema validation, migraciones y lectura defensiva.
TypeScript
Copiar type CreateOrderInput = {
items: Array < {
productId: string ;
quantity: number ;
} > ;
} ; Valídalo con Zod, Valibot u otra librería:
TypeScript
Copiar const input = createOrderSchema. parse ( req. body) ; Después convierte IDs y tipos. No aceptes businessId, total calculado o estados internos desde el cliente.
El dominio puede usar tipos más cómodos o value objects:
TypeScript
Copiar type Money = {
amount: string ;
currency: 'COP' ;
} ; La capa de persistencia transforma a Decimal128. Así la decisión de BSON no contamina toda la aplicación.
TypeScript
Copiar type OrderDto = {
id: string ;
status: string ;
total: string ;
createdAt: string ;
} ; Nunca serialices documentos directamente. ObjectId, Decimal128 y Date requieren representación explícita, y campos internos como businessId, version o secretos pueden no pertenecer a la API.
El driver ofrece Filter<T>, UpdateFilter<T>, OptionalUnlessRequiredId<T> y otros helpers. Son útiles dentro de la capa de datos:
TypeScript
Copiar const filter: Filter< OrderDocument> = {
businessId,
status: 'pending'
} ; No los expongas como contratos de controller. Permitir que una request construya Filter<T> es inseguro aunque compile.
Modela según persistencia real. Si MongoDB genera ObjectId:
TypeScript
Copiar type ProductDocument = {
_id: ObjectId;
} ; Para inserts, utiliza un tipo separado o helper del driver. No hagas _id?: ObjectId en todo el dominio si después de persistir siempre existe.
JavaScript number no representa todos los enteros de 64 bits ni decimales monetarios exactos.
TypeScript
Copiar const price = Decimal128. fromString ( '18.50' ) ; Convierte a string para DTO. Evita parseFloat si la precisión es contractual.
Usa Long o BigInt con una estrategia de serialización cuando los valores superen Number.MAX_SAFE_INTEGER.
El servidor distingue tipos; schema drift puede afectar sort, comparación y índices aunque TypeScript declare solo number.
TypeScript
Copiar createdAt: new Date ( ) TypeScript
Copiar createdAt: document. createdAt. toISOString ( ) Valida que runtime sea Date. Un documento viejo puede contener string y romper .toISOString().
Estos estados son distintos:
TypeScript
Copiar middleName? : string | null ;
missing: no existe el campo;
null: existe sin valor;
string: valor válido.
Define semántica y queries. Partial<T> no expresa automáticamente cómo MongoDB trata $unset o null.
Una projection devuelve un subtipo. No declares que retorna OrderDocument completo:
TypeScript
Copiar type OrderListItem = Pick<
OrderDocument,
'_id' | 'status' | 'total' | 'createdAt'
> ; TypeScript
Copiar const rows = await orders. find < OrderListItem> ( filter, {
projection: {
status: 1 ,
total: 1 ,
createdAt: 1
}
} ) . toArray ( ) ; Confirma la API del driver actual y evita casts para silenciar incompatibilidades.
El driver no puede inferir siempre la salida de pipelines complejos. Define el tipo final:
TypeScript
Copiar type DailySales = {
day: Date;
total: Decimal128;
orders: number ;
} ;
const result = orders. aggregate < DailySales> ( pipeline) ; Esto tampoco valida runtime. Añade tests y validación en boundaries críticos.
Para documentos polimórficos:
TypeScript
Copiar type PaymentDocument =
| {
type: 'cash' ;
received: Decimal128;
}
| {
type: 'card' ;
providerReference: string ;
} ; Schema validation debe reflejar variantes. Maneja un type desconocido porque datos y código pueden evolucionar desincronizados.
TypeScript
Copiar const order = raw as OrderDocument; Una assertion elimina la advertencia sin convertir ni validar. Prefiere parseo:
TypeScript
Copiar const order = orderDocumentSchema. parse ( raw) ; o lectura tolerante por schemaVersion.
Puedes diferenciar IDs en compile time:
TypeScript
Copiar type OrderId = ObjectId & { readonly __brand: 'OrderId' } ;
type UserId = ObjectId & { readonly __brand: 'UserId' } ; Ayuda a evitar mezclas accidentales, pero no existe en runtime. La autorización y validación siguen siendo necesarias.
Modela resultados de dominio, no todos los errores del driver como un union imposible. En la capa de infraestructura traduce:
duplicate key → conflict;
not found/precondition → dominio;
timeout/network → unavailable o retry policy;
programmer error → fail fast.
Conserva cause para observabilidad sin exponer detalles.
Durante una transición, el tipo real puede ser union de versiones:
TypeScript
Copiar type UserStored = UserV1 | UserV2; El lector normaliza a un modelo actual. No declares UserV2 mientras todavía existen V1 sin fallback.
ObjectId llega como string;
Decimal128 se serializa como objeto;
projection omite campo requerido;
aggregation devuelve null;
documento tiene enum nuevo;
campo optional existe con tipo incorrecto;
query generic permite una key pero runtime usa path diferente;
restore reintroduce schema antiguo.
una interfaz para input, DB y response;
assertions después de cada query;
any en pipelines;
representar dinero como number;
declarar _id optional siempre;
ignorar null/missing;
confiar en compile time para seguridad;
devolver documentos BSON directamente;
no modelar versiones.
Inspecciona BSON real.
Valida inputs.
Prueba tipos mixtos.
Serializa ObjectId/Date/Decimal128.
Ejecuta projections.
Valida aggregation outputs.
Prueba enum desconocido.
Restaura documento viejo.
Elimina assertions injustificadas.
Alinea validator y tipos.
TypeScript documenta y restringe el código, no la base. Separa input, dominio, documento y DTO; modela BSON explícitamente y valida runtime. Las assertions ocultan schema drift, mientras una frontera de transformación permite evolución, seguridad y contratos claros.
Comprueba lo aprendido
¿Por qué un generic no valida documentos?
¿Qué representaciones separarías?
¿Cómo serializarías Decimal128?
¿Qué diferencia existe entre null y missing?
¿Cómo tiparías una projection?
¿Por qué evitar as OrderDocument?