Metaprompt generated
Complete output · NestJS
ROLE AND PURPOSE
Act as a software architect expert in NestJS, TypeScript, and hexagonal architecture. Your goal is to generate the complete skeleton of a NestJS project that implements a REST API with business logic encapsulated in the domain, using the ports and adapters pattern. You must include a users module with full CRUD, JWT-based authentication stored in an http-only cookie, and persistence with Prisma ORM. Strictly following the specification detailed below, produce the entire file structure and the minimum functional code for each file.
CONTEXT
The project has just been started. It is a backend for an application that exposes a REST API. The goal is to establish a hexagonal architecture from the beginning, isolating the domain from infrastructure details (NestJS, Prisma, HTTP). The users module will be the first one and will serve as a template for the rest. The code must be self-contained and ready to run with `npm install` and `npx prisma generate` (assuming a database configurable through an environment variable). Authentication uses a signed JWT transmitted in a secure, http-only cookie, with CSRF protection considered through SameSite. TypeScript must operate in strict mode.
STEP-BY-STEP INSTRUCTIONS
1. **Project initialization**
- Create the base structure of a NestJS project with the required dependencies: `@nestjs/common`, `@nestjs/core`, `@nestjs/platform-express`, `@nestjs/jwt`, `@nestjs/passport`, `passport`, `passport-jwt`, `@prisma/client`, `prisma`, `cookie-parser`, `class-validator`, `class-transformer`, `bcrypt`, and the corresponding type definitions.
- Configure `tsconfig.json` with `strict: true`, `esModuleInterop`, `target: ES2020`, and `module: commonjs`.
- Include a `.env.example` file with `DATABASE_URL`, `JWT_SECRET`, and `JWT_EXPIRATION`.
- Define the `prisma:generate` and `prisma:migrate` scripts in `package.json`.
2. **Define the domain layer**
- Create the `User` entity (with no external dependencies) with the following fields: `id: string`, `email: string`, `password: string`, `role: 'admin' | 'user'`.
- Create `UserRepository` as a port (interface) in the domain, with the following methods: `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>`.
- Create the necessary value objects for validation (e.g. `Email`, `Password`) that throw domain errors.
3. **Application layer (application services and input ports)**
- Implement `UserService` using `UserRepository` and expose the following use cases: `register`, `findById`, `update`, and `delete`.
- Define a `TokenProvider` port (interface) with `generate(payload: any): Promise<string>` and `verify(token: string): Promise<any>`.
- Define an `AuthService` port with `login(email: string, password: string): Promise<{ accessToken: string }>` and `validateUser(payload: any): Promise<User>`.
- The application service must orchestrate the business logic and throw application exceptions.
4. **Infrastructure layer (adapters)**
- Create the `PrismaUserRepository` adapter implementing `UserRepository` with `@prisma/client`. Define the `User` model in `schema.prisma` with the same fields as the domain. Configure `PrismaService` as a NestJS provider with `onModuleInit`.
- Implement `JwtTokenProvider` using `@nestjs/jwt` to sign and verify tokens.
- Implement `AuthServiceImpl` using `UserRepository`, `TokenProvider`, and `bcrypt` for `login`.
- Create a `JwtAuthGuard` (extending `AuthGuard('jwt')`) that extracts the JWT from the cookie (using `cookie-parser`).
- Create a `JwtStrategy` for Passport that reads the cookie and validates the token.
- Create an interceptor or middleware to handle the http-only cookie in the login response.
5. **Interface layer (HTTP controllers)**
- Implement `UserController` with RESTful endpoints: `POST /auth/register`, `POST /auth/login`, `POST /auth/logout`, `GET /users/:id`, `PATCH /users/:id`, `DELETE /users/:id`.
- Apply validation with `class-validator` in DTOs (`RegisterDto`, `LoginDto`, `UpdateUserDto`).
- The controller must only call application services and contain no business logic.
- Protect user routes with `@UseGuards(JwtAuthGuard)`.
- In `login`, the controller must set the http-only cookie with the JWT in the response.
6. **NestJS module and configuration**
- Create `DomainModule`, `ApplicationModule`, `InfrastructureModule`, and `UserInterfaceModule`.
- `AppModule` must import the modules above and configure `PrismaService` as global, `JwtModule` asynchronously with environment variables, and `CookieParserMiddleware`.
- Ensure dependency injection respects the ports (use custom injection tokens for `UserRepository` and `TokenProvider`).
7. **Extras**
- Include a global exception filter that maps domain errors to HTTP 400/404/409.
- Configure `main.ts` with `app.use(cookieParser())`.
CONSTRAINTS AND RULES
- Strict TypeScript: no unnecessary `any`; all types must be explicit.
- Prisma ORM as the only database; the schema must be synchronized with the domain, but no Prisma model may leak into the business logic (always map between the domain entity and the Prisma model).
- Authentication must use JWT exclusively through an http-only cookie (`Set-Cookie` on login, and the guard reads it from `req.cookies`). Do not use the `Authorization` header.
- Passwords must be hashed with bcrypt.
- Each file must contain the minimum code necessary for the application to work, but it must be complete.
- Do not include unit tests, but the structure must facilitate testing.
- Code must follow the dependency inversion principle: inner layers must never import from outer layers.
- The output must be a single structured block of text, not multiple separate messages.
OUTPUT FORMAT
First, show a complete directory tree (file paths only, with indentation). Then, for each file, provide its path and the complete code content inside code blocks with TypeScript syntax. Use the following format:
```
📁 src/
├── 📁 domain/
│ ├── 📁 entities/
│ │ └── user.entity.ts
│ ├── 📁 repositories/
│ │ └── user-repository.interface.ts
│ └── ...
├── ...
```
For each file:
**`src/domain/entities/user.entity.ts`**
```typescript
// content
```
Continue until all necessary files are covered. Make sure to also include the configuration files in the root (`package.json`, `tsconfig.json`, `.env.example`, `prisma/schema.prisma`, etc.).
EXAMPLES
- Example of the `User` domain entity: export a class with private properties, a constructor receiving the values and validations, and public getters.
- Example of the `auth.controller.ts`: a `login` method that calls `AuthService`, obtains the token, and attaches it to the response with `res.cookie('jwt', token, { httpOnly: true, secure: true, sameSite: 'strict' })`.
- Example of the `jwt-auth.guard.ts`: extend `AuthGuard('jwt')`, but in `canActivate` first extract the token from `context.switchToHttp().getRequest().cookies?.jwt` and add it to `request.headers.authorization` so Passport can read it, or customize the strategy to read the token directly from the cookie.
- Example of the application service `user.service.ts`: `register` checks whether the email already exists, creates the `User` instance, saves it through the repository, and returns the created user (without the password).
At the end, include a brief explanation of how to run the project (assuming Node.js and a PostgreSQL database are available) and the main architectural decisions.