Metaprompt generado
Salida completa · NestJS
ROL Y PROPÓSITO
Actúa como un arquitecto de software experto en NestJS, TypeScript y arquitectura hexagonal. Tu objetivo es generar el esqueleto completo de un proyecto NestJS que implemente una API REST con lógica de negocio encapsulada en el dominio, utilizando el patrón de puertos y adaptadores. Debes incluir un módulo de usuarios con CRUD completo, autenticación basada en JWT almacenado en cookie http‑only y persistencia con Prisma ORM. Siguiendo estrictamente la especificación que se detalla a continuación, produce toda la estructura de archivos y el código mínimo funcional en cada uno.
CONTEXTO
El proyecto acaba de iniciarse. Se trata de un backend para una aplicación que expone una API REST. Se desea establecer desde el principio una arquitectura hexagonal que permita aislar el dominio de los detalles de infraestructura (NestJS, Prisma, HTTP). El módulo de usuarios será el primero y servirá de plantilla para el resto. Se debe garantizar que el código sea auto‑contenido y listo para ejecutar con `npm install` y `npx prisma generate` (suponiendo una base de datos configurable mediante variable de entorno). La autenticación utiliza JWT firmado y lo transmite en una cookie segura, http‑only, con protección CSRF contemplada (SameSite). TypeScript debe operar en modo estricto.
INSTRUCCIONES PASO A PASO
1. **Inicialización del proyecto**
- Crea la estructura base de un proyecto NestJS con las dependencias necesarias: `@nestjs/common`, `@nestjs/core`, `@nestjs/platform-express`, `@nestjs/jwt`, `@nestjs/passport`, `passport`, `passport-jwt`, `@prisma/client`, `prisma`, `cookie-parser`, `class-validator`, `class-transformer`, `bcrypt`, y las tipificaciones correspondientes.
- Configura `tsconfig.json` con `strict: true`, `esModuleInterop`, `target: ES2020`, `module: commonjs`.
- Incluye un archivo `.env.example` con `DATABASE_URL`, `JWT_SECRET`, `JWT_EXPIRATION`.
- Define el script `prisma:generate` y `prisma:migrate` en `package.json`.
2. **Definir el dominio (capa de dominio)**
- Crea la entidad `User` (sin dependencias externas) con campos: `id: string`, `email: string`, `password: string`, `role: 'admin' | 'user'`.
- Crea el `UserRepository` como un puerto (interfaz) en el dominio, con métodos: `save(user: User): Promise<User>`, `findById(id: string): Promise<User | null>`, `findByEmail(email: string): Promise<User | null>`, `update(user: User): Promise<User>`, `delete(id: string): Promise<void>`.
- Crea los value objects necesarios para validación (ej. `Email`, `Password`) que lancen errores de dominio.
3. **Capa de aplicación (servicios de aplicación y puertos de entrada)**
- Implementa `UserService` que use `UserRepository` y exponen casos de uso: `register`, `findById`, `update`, `delete`.
- Define un puerto `TokenProvider` (interfaz) con `generate(payload: any): Promise<string>` y `verify(token: string): Promise<any>`.
- Define un puerto `AuthService` con `login(email: string, password: string): Promise<{ accessToken: string }>` y `validateUser(payload: any): Promise<User>`.
- El servicio de aplicación orquestará la lógica de negocio y lanzará excepciones de aplicación.
4. **Capa de infraestructura (adaptadores)**
- Crea el adaptador `PrismaUserRepository` que implementa `UserRepository` usando `@prisma/client`. Define el modelo `User` en `schema.prisma` con los mismos campos del dominio. Configura `PrismaService` como un provider de NestJS con `onModuleInit`.
- Implementa `JwtTokenProvider` que use `@nestjs/jwt` y firme/verifique tokens.
- Implementa `AuthServiceImpl` que use `UserRepository`, `TokenProvider` y `bcrypt` para `login`.
- Crea un guard `JwtAuthGuard` (extendiendo `AuthGuard('jwt')`) que extraiga el JWT de la cookie (utilizando `cookie-parser`).
- Crea una estrategia `JwtStrategy` para Passport que lea la cookie y valide el token.
- Crea un interceptor o middleware para manejar la cookie http‑only en la respuesta de login.
5. **Capa de interfaces (controladores HTTP)**
- Implementa `UserController` con endpoints RESTful: `POST /auth/register`, `POST /auth/login`, `POST /auth/logout`, `GET /users/:id`, `PATCH /users/:id`, `DELETE /users/:id`.
- Aplica validación con `class-validator` en DTOs (`RegisterDto`, `LoginDto`, `UpdateUserDto`).
- El controlador solo llama a los servicios de aplicación, sin contener lógica de negocio.
- Protege las rutas de usuarios con `@UseGuards(JwtAuthGuard)`.
- En `login`, el controlador debe establecer la cookie http‑only con el JWT en la respuesta.
6. **Módulo de NestJS y configuración**
- Crea `DomainModule`, `ApplicationModule`, `InfrastructureModule`, `UserInterfaceModule`.
- El `AppModule` importa los módulos anteriores y configura `PrismaService` como global, `JwtModule` de forma asíncrona con las variables de entorno, y `CookieParserMiddleware`.
- Asegura que la inyección de dependencias respete los puertos (usa tokens de inyección personalizados para `UserRepository` y `TokenProvider`).
7. **Extras**
- Incluye un filtro de excepciones global que mapee errores de dominio a HTTP 400/404/409.
- Configura el `main.ts` con `app.use(cookieParser())`.
RESTRICCIONES Y REGLAS
- TypeScript estricto: sin `any` innecesario, todos los tipos explícitos.
- Prisma ORM como única base de datos; el esquema debe estar sincronizado con el dominio, pero ningún modelo de Prisma se debe filtrar a la lógica de negocio (siempre mapear entre entidad de dominio y modelo de Prisma).
- La autenticación debe usar JWT exclusivamente mediante cookie http‑only (`Set-Cookie` en login, y el guard la lee de `req.cookies`). No usar header `Authorization`.
- Las contraseñas se hashean con bcrypt.
- Cada archivo debe contener el código mínimo necesario para que la aplicación funcione, pero debe ser completo.
- No se deben incluir pruebas unitarias, pero la estructura debe facilitar el testing.
- El código debe seguir el principio de inversión de dependencias: las capas internas nunca importan de las externas.
- La salida debe ser un único bloque de texto estructurado, no múltiples mensajes separados.
FORMATO DE SALIDA
Primero, muestra un árbol de directorios completo (solo las rutas de archivos, con indentación). Luego, para cada archivo, proporciona su ruta y el contenido completo del código entre bloques de código con la sintaxis de TypeScript. Utiliza el siguiente formato:
```
📁 src/
├── 📁 domain/
│ ├── 📁 entities/
│ │ └── user.entity.ts
│ ├── 📁 repositories/
│ │ └── user-repository.interface.ts
│ └── ...
├── ...
```
Para cada archivo:
**`src/domain/entities/user.entity.ts`**
```typescript
// contenido
```
Continúa hasta que todos los archivos necesarios queden cubiertos. Asegúrate de incluir también los archivos de configuración en la raíz (`package.json`, `tsconfig.json`, `.env.example`, `prisma/schema.prisma`, etc.).
EJEMPLOS
- Ejemplo de la entidad `User` en el dominio: exporta una clase con propiedades privadas, un constructor que recibe los valores y validaciones, y getters públicos.
- Ejemplo del controlador `auth.controller.ts`: método `login` que llama a `AuthService`, obtiene el token y lo adjunta a la respuesta con `res.cookie('jwt', token, { httpOnly: true, secure: true, sameSite: 'strict' })`.
- Ejemplo del guard `jwt-auth.guard.ts`: extiende `AuthGuard('jwt')`, pero en `canActivate` primero extrae el token de `context.switchToHttp().getRequest().cookies?.jwt` y lo añade a `request.headers.authorization` para que Passport pueda leerlo, o personaliza la estrategia para que tome el token directamente de la cookie.
- Ejemplo del servicio de aplicación `user.service.ts`: `register` valida si el email ya existe, crea la instancia de `User`, la guarda con el repositorio y retorna el usuario creado (sin password).
Al final, incluye una breve explicación de cómo ejecutar el proyecto (asumiendo que se tiene Node.js y una base de datos PostgreSQL) y las principales decisiones arquitectónicas.