Curso de Backend con NestJS

Generación automática de módulos CRUD con NestJS y AI

Curso de Backend con NestJS

Contenido del curso

Fundamentos y Primer CRUD

Base de Datos y Persistencia con TypeORM

Generación automática de módulos CRUD con NestJS y AI

Resumen

Crear un CRUD en NestJS para nuevas entidades puede tomar minutos si combinas el generador oficial de Nest con asistencia de IA. Aquí verás cómo construir el módulo de posts (artículos) reutilizando el patrón ya existente de users, apoyándote en Cursor para acelerar el servicio y los DTO sin perder control sobre la entidad ni la lógica de negocio.

Por qué usar el generador de NestJS para escalar tu API

NestJS asume que tu arquitectura va a repetirse: controller, service, entity, DTO y module. Por eso ofrece un CRUD generator que crea todo el boilerplate de una sola vez.

En la documentación oficial, dentro de la sección Recipes, encuentras el generador. Lo invocas desde la terminal y el CLI te pregunta qué tipo de capa transporte quieres exponer:

  • REST API.
  • GraphQL con code-first o schema-first.
  • Microservicio.
  • WebSockets.

Al elegir REST API y confirmar que quieres generar los endpoints, NestJS arma la carpeta completa con el patrón que ya venías trabajando en users. [2:30]

¿Qué genera el CRUD generator de NestJS? Crea el módulo, el controller con los métodos create, findAll, findOne, update y delete, el servicio con su firma, la entity vacía y los DTO separados en archivos individuales (create-post.dto.ts y update-post.dto.ts como PartialType).

Un detalle interesante: el update se expone por PATCH en vez de PUT, lo cual sigue siendo la práctica más extendida en APIs modernas.

Cómo definir la entidad Post con campos de negocio reales

La entity es el único archivo que conviene escribir tú, porque ahí se decide cómo se van a guardar los datos. El generador deja la clase vacía a propósito. [5:40]

Para los artículos definimos estos campos:

  • id como primary generated column.
  • title tipo varchar, obligatorio.
  • content tipo text, sin límite de longitud.
  • coverImage para la imagen de portada.
  • summary como descripción corta.
  • isDraft booleano, con valor por defecto true.
  • createdAt y updatedAt automáticos.

La decisión sobre isDraft viene de la lógica editorial: todo artículo nace como borrador y solo cuando el usuario decide publicarlo cambia a false. Esto es más expresivo que un simple published, y evita confundir la fecha de creación con la fecha de publicación real.

Por qué algunos campos deben ser opcionales en el DTO

Aunque en la base de datos el coverImage parecía obligatorio, al pensar en cómo trabaja un editor real surge una matización: nadie empieza un artículo con la portada lista. [11:20]

Lo natural es que el usuario cree el post solo con un título, vaya escribiendo el contenido, suba la imagen después y al final redacte el summary. Por eso, en CreatePostDto el único campo verdaderamente requerido es title. El resto se completa vía update.

La validación más estricta ("no puedes publicar sin cover ni summary") debe vivir en un endpoint específico de publicación, no en la estructura de base de datos.

Cómo usar Cursor AI para generar el PostService basado en UserService

Con la entity lista, abrimos post.service.ts en Cursor y le pasamos un prompt tageando el UserService como referencia. La instrucción fue clara: crear el PostService para un CRUD, sin relaciones por ahora, replicando las buenas prácticas del servicio de usuarios. [14:10]

La IA respetó varias decisiones importantes del código existente:

  • Todos los métodos son async.
  • Maneja excepciones con NotFoundException cuando no encuentra el recurso.
  • Usa merge para el update, igual que en users.
  • Crea los DTO en archivos separados con decoradores de class-validator.

¿Cuándo conviene reutilizar un método findOne en NestJS? Si vas a hacer queries simples y repetidas, reutilizar evita duplicar código. Pero si cada método necesita un where exhaustivo o relaciones distintas, escribirlo inline en cada función es más claro y mantenible.

Donde la IA falló fue en marcar coverImage como opcional sin que se lo pidiéramos explícitamente, y en olvidar el IsNotEmpty en el título. Nada que no se corrija en segundos revisando los decoradores.

Ajustes manuales que la IA no cubre

Dos cosas siguen siendo trabajo humano:

  1. Importar TypeOrmModule.forFeature([Post]) en el PostModule y exportar el servicio.
  2. Cambiar el +id automático del controller por ParseIntPipe, que convierte el parámetro de string a number de forma más segura y explícita.

Este segundo ajuste no es un error de la IA, sino del scaffold original que entrega NestJS.

Cómo probar el CRUD de Posts en Postman

En Postman conviene organizar los endpoints en carpetas por recurso. Duplicas la carpeta users, la renombras a posts y solo cambias las rutas. [21:50]

Las pruebas confirman el comportamiento esperado:

  • POST /posts con solo { "title": "Hello" } crea el artículo en estado borrador.
  • PATCH /posts/:id permite ir agregando content, coverImage y summary por separado.
  • GET /posts/:id devuelve el artículo con los campos que se hayan ido llenando.
  • DELETE /posts/:id lo elimina y devuelve confirmación.

En la base de datos verificas que la tabla posts quedó con id, title, content, coverImage, summary, isDraft (boolean), createdAt y updatedAt, sin relaciones por ahora.

La relación uno a muchos entre User y Post se aborda en clases siguientes, junto con un endpoint para obtener todos los posts de un usuario específico, similar al patrón ya usado para traer su profile.

Por qué la productividad real viene de combinar conocimiento y AI

Lo que en la primera parte del curso tomó varias clases (construir users a mano), aquí se hizo en una sola sesión. La diferencia no es solo la IA: es saber qué pedirle.

Cuando entiendes cómo funciona el patrón module-controller-service-entity-DTO por dentro, puedes auditar lo que Cursor genera, detectar cuándo pone un campo opcional que no debería, y corregir el ParseIntPipe sin pensarlo. La IA acelera, pero el criterio sigue siendo tuyo. ¿Tú cómo organizas tu prompt cuando quieres replicar un patrón existente? Cuéntamelo en los comentarios.