Contenido del curso

Fundamentos y Primer CRUD

Base de Datos y Persistencia con TypeORM

Relación uno a muchos con TypeORM

Resumen

Modelar la relación entre usuarios y posts es uno de los retos más comunes cuando trabajas con bases de datos transaccionales como Postgres o MySQL. Aquí aprendes a implementar una relación uno a muchos en TypeORM y NestJS, dejando la bidireccionalidad lista para consultar datos desde ambos lados de la entidad.

Por qué usar una relación uno a muchos entre User y Post

Un usuario puede escribir varios posts, pero cada post pertenece a un único autor. Esa lógica se traduce en una llave foránea dentro de la tabla de posts que apunta al usuario creador.

TypeORM resuelve este patrón con dos decoradores complementarios: @ManyToOne del lado del post y @OneToMany del lado del usuario. Y aquí viene lo interesante: al declarar ambos lados, obtienes bidireccionalidad, lo que significa que puedes preguntar user.posts para traer todos los artículos de un autor, o post.user para saber a quién pertenece un artículo en particular [02:10].

¿Qué es una relación uno a muchos en bases de datos? Es una asociación donde un registro de la tabla A puede vincularse con varios registros de la tabla B, pero cada registro de B solo se vincula con uno de A. En este caso, un usuario tiene muchos posts, pero un post tiene un solo usuario.

Cómo configurar @ManyToOne y @JoinColumn en la entidad Post

Dentro de la entidad Post agregas el decorador @ManyToOne apuntando a la entidad User. Con eso TypeORM ya entiende la relación, pero conviene reforzarla con @JoinColumn para controlar cómo se nombra la llave foránea.

Sin @JoinColumn, TypeORM podría crear la columna solo con el nombre user, lo cual rompe la convención de normalización. Con el decorador explícito, la columna queda como user_id, que es el estándar esperado en bases de datos relacionales [05:30].

Cómo forzar que un post siempre tenga autor

Para evitar posts huérfanos, agrega la opción nullable: false al decorador @ManyToOne. Esto crea una restricción a nivel de base de datos que impide insertar un artículo sin usuario relacionado.

typescript @ManyToOne(() => User, (user) => user.posts, { nullable: false }) @JoinColumn() user: User;

Esa restricción se vuelve parte del esquema y la base de datos rechaza cualquier intento de crear un post sin user_id válido.

Cómo declarar @OneToMany en la entidad User para la bidireccionalidad

Del lado de User necesitas declarar el inverso de la relación con @OneToMany. Aquí no se guarda la llave foránea, solo es un mapeo orientado a objetos que permite a TypeORM resolver los posts asociados a un usuario.

La sintaxis recibe dos argumentos: la entidad relacionada y una función que indica cómo navegar de regreso. Quedaría así: @OneToMany(() => Post, (post) => post.user) y la propiedad se tipa como un array de posts [08:45].

¿Cuál es la diferencia entre @ManyToOne y @OneToMany? @ManyToOne se coloca en la entidad que carga la llave foránea (Post). @OneToMany va en la entidad inversa (User) y solo sirve para navegar la relación, no crea columnas nuevas.

Cómo ajustar el DTO y el servicio para crear posts con relación

Como ahora un post obligatoriamente necesita un autor, el DTO de creación debe incluir un campo userId validado con @IsNumber() y @IsNotEmpty(). Sin ese dato, la API rechaza la petición.

En el servicio aplicas el spread operator para separar el userId del resto del body y lo asignas al atributo user que TypeORM espera:

typescript const post = this.postRepository.create({ ...body, user: { id: userId }, }); await this.postRepository.save(post);

El detalle clave: aunque la columna en base de datos se llame user_id, en el código TypeScript trabajas con el atributo user tal como lo declaraste en la entidad [13:20].

Cómo devolver datos anidados al consultar posts

Crear la relación no basta. Si haces un GET a los posts y solo recibes el id del usuario, la respuesta queda incompleta para renderizar interfaces como las de dev.to, donde cada artículo muestra avatar, nombre y otros datos del autor.

La solución es cargar las relaciones explícitamente en los métodos de consulta. En findOne y find agregas la opción relations, e incluso puedes anidar varios niveles usando notación de punto:

  • relations: ['user'] trae solo el usuario.
  • relations: ['user.profile'] trae el usuario y, dentro de él, su perfil.
  • Funciona tanto para listados como para consultas individuales.

Una buena práctica es que después de guardar un post con save, llames a findOne con el id recién creado para devolver al cliente la respuesta ya enriquecida con las relaciones [18:40].

Cómo aprovechar synchronize durante el desarrollo

La opción synchronize: true de TypeORM detecta cambios en las entidades y actualiza el esquema automáticamente. Es útil mientras desarrollas, pero no es la forma más segura para producción, donde se prefieren migraciones controladas.

Mientras tanto, te permite ver cómo la columna user_id aparece en la tabla de posts apenas guardas el archivo, sin necesidad de scripts manuales.

Reto cómo devolver los posts de un usuario por su ID

Ya tienes un endpoint que devuelve el profile de un usuario a partir de su ID. El reto es replicar esa misma arquitectura para devolver los posts asociados a un usuario específico.

La idea es que al hacer GET a una ruta como /users/:id/posts, recibas la lista completa de artículos escritos por ese autor, aprovechando la relación @OneToMany que ya configuraste. ¿Cómo resolverías la consulta usando el repositorio de TypeORM? Comparte tu propuesta en los comentarios.