|
| 1 | +# GitHub Copilot Instructions for lenne.Tech Nest Server |
| 2 | + |
| 3 | +This file provides specific guidance for GitHub Copilot when working with this NestJS-based server project. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +This is a **NestJS server framework** with: |
| 8 | +- **GraphQL API** using Apollo Server |
| 9 | +- **MongoDB** integration with Mongoose |
| 10 | +- **JWT authentication** with refresh tokens |
| 11 | +- **Role-based access control** |
| 12 | +- **Modular architecture** with Core/Server separation |
| 13 | + |
| 14 | +## Code Architecture & Patterns |
| 15 | + |
| 16 | +### Module Structure |
| 17 | +- **Core modules** (`src/core/`): Reusable framework components |
| 18 | +- **Server modules** (`src/server/`): Project-specific implementations |
| 19 | +- Use `CoreModule.forRoot()` for dynamic module configuration |
| 20 | +- Extend core classes rather than creating from scratch |
| 21 | + |
| 22 | +### Model Patterns |
| 23 | +```typescript |
| 24 | +// Always extend CorePersistenceModel for database entities |
| 25 | +@MongooseSchema({ timestamps: true }) |
| 26 | +@ObjectType({ description: 'Your Model' }) |
| 27 | +@Restricted(RoleEnum.S_EVERYONE) |
| 28 | +export class YourModel extends CorePersistenceModel { |
| 29 | + @UnifiedField({ |
| 30 | + description: 'Field description', |
| 31 | + roles: RoleEnum.S_EVERYONE, |
| 32 | + validator: () => [IsString()], |
| 33 | + }) |
| 34 | + fieldName: string = undefined; |
| 35 | +} |
| 36 | +``` |
| 37 | + |
| 38 | +### Input Classes |
| 39 | +```typescript |
| 40 | +// Use CoreInput as base and UnifiedField decorator |
| 41 | +@InputType({ description: 'Your input' }) |
| 42 | +@Restricted(RoleEnum.S_EVERYONE) |
| 43 | +export class YourInput extends CoreInput { |
| 44 | + @UnifiedField({ |
| 45 | + description: 'Field description', |
| 46 | + isOptional: false, |
| 47 | + roles: RoleEnum.S_EVERYONE, |
| 48 | + validator: () => [IsString(), IsNotEmpty()], |
| 49 | + }) |
| 50 | + fieldName: string = undefined; |
| 51 | +} |
| 52 | +``` |
| 53 | + |
| 54 | +### Service Patterns |
| 55 | +```typescript |
| 56 | +// Extend CrudService for standard CRUD operations |
| 57 | +@Injectable() |
| 58 | +export class YourService extends CrudService<YourModel, YourCreateInput, YourUpdateInput> { |
| 59 | + constructor( |
| 60 | + @InjectModel('YourModel') protected readonly model: Model<YourModelDocument>, |
| 61 | + protected readonly configService: ConfigService, |
| 62 | + ) { |
| 63 | + super({ model }); |
| 64 | + } |
| 65 | +} |
| 66 | +``` |
| 67 | + |
| 68 | +## Security & Authorization |
| 69 | + |
| 70 | +### Access Control |
| 71 | +- Use `@Restricted(RoleEnum.ROLE_NAME)` on classes and fields |
| 72 | +- Use `@Roles(RoleEnum.ROLE_NAME)` on methods |
| 73 | +- Never expose sensitive fields without proper restrictions |
| 74 | +- Password fields should use `@Restricted(RoleEnum.S_NO_ONE)` |
| 75 | + |
| 76 | +### Authentication |
| 77 | +- JWT tokens contain: `id`, `deviceId`, `tokenId`, `deviceDescription` |
| 78 | +- Refresh tokens are stored in `user.refreshTokens[deviceId]` |
| 79 | +- Use `@CurrentUser()` decorator to access authenticated user |
| 80 | +- Device tracking is automatic via `deviceId` |
| 81 | + |
| 82 | +## Database Patterns |
| 83 | + |
| 84 | +### Mongoose Configuration |
| 85 | +- Use `@Prop()` for simple fields |
| 86 | +- Use `@Prop(raw({}))` for dynamic objects |
| 87 | +- Index important fields with `@Prop({ index: true })` |
| 88 | +- Always use timestamps: `@MongooseSchema({ timestamps: true })` |
| 89 | + |
| 90 | +### Filtering & Pagination |
| 91 | +```typescript |
| 92 | +// Use built-in filtering and pagination |
| 93 | +async findMany( |
| 94 | + filter?: FilterArgs, |
| 95 | + pagination?: PaginationArgs, |
| 96 | + serviceOptions?: ServiceOptions, |
| 97 | +): Promise<YourModel[]> { |
| 98 | + return super.find(filter, serviceOptions, pagination); |
| 99 | +} |
| 100 | +``` |
| 101 | + |
| 102 | +## GraphQL Patterns |
| 103 | + |
| 104 | +### Resolvers |
| 105 | +```typescript |
| 106 | +@Resolver(() => YourModel) |
| 107 | +export class YourResolver { |
| 108 | + @Query(() => YourModel) |
| 109 | + @Roles(RoleEnum.USER) |
| 110 | + async getYour(@Args() args: GetArgs): Promise<YourModel> { |
| 111 | + return this.yourService.get(args.id); |
| 112 | + } |
| 113 | + |
| 114 | + @Mutation(() => YourModel) |
| 115 | + @Roles(RoleEnum.USER) |
| 116 | + async createYour(@Args('input') input: YourCreateInput): Promise<YourModel> { |
| 117 | + return this.yourService.create(input); |
| 118 | + } |
| 119 | +} |
| 120 | +``` |
| 121 | + |
| 122 | +### Field Resolvers |
| 123 | +```typescript |
| 124 | +@ResolverField(() => [RelatedModel]) |
| 125 | +async relatedItems(@Parent() parent: YourModel): Promise<RelatedModel[]> { |
| 126 | + return this.relatedService.find({ yourId: parent.id }); |
| 127 | +} |
| 128 | +``` |
| 129 | + |
| 130 | +## Development Practices |
| 131 | + |
| 132 | +### Error Handling |
| 133 | +- Use NestJS exceptions: `BadRequestException`, `UnauthorizedException` |
| 134 | +- Provide meaningful error messages |
| 135 | +- Handle validation errors automatically via `MapAndValidatePipe` |
| 136 | + |
| 137 | +### Testing |
| 138 | +- Write E2E tests in `tests/` directory |
| 139 | +- Use `test.helper.ts` utilities |
| 140 | +- Test with `NODE_ENV=local` |
| 141 | +- Mock external services appropriately |
| 142 | + |
| 143 | +### Configuration |
| 144 | +- Environment configs in `src/config.env.ts` |
| 145 | +- Support multiple config sources: |
| 146 | + - Direct environment variables |
| 147 | + - `NEST_SERVER_CONFIG` JSON |
| 148 | + - `NSC__*` prefixed variables |
| 149 | + |
| 150 | +## File Organization |
| 151 | + |
| 152 | +### New Features |
| 153 | +1. Create module in appropriate location (`src/core/` or `src/server/`) |
| 154 | +2. Include: model, service, resolver, controller (if needed) |
| 155 | +3. Add input/output classes in dedicated folders |
| 156 | +4. Export everything in module's `index.ts` |
| 157 | + |
| 158 | +### Naming Conventions |
| 159 | +- Models: `YourModel` (singular) |
| 160 | +- Services: `YourService` |
| 161 | +- Resolvers: `YourResolver` |
| 162 | +- Controllers: `YourController` |
| 163 | +- Inputs: `YourCreateInput`, `YourUpdateInput` |
| 164 | +- Files: kebab-case (e.g., `your-model.model.ts`) |
| 165 | + |
| 166 | +## Common Commands |
| 167 | + |
| 168 | +### Development |
| 169 | +```bash |
| 170 | +npm start # Start local development |
| 171 | +npm run start:dev # Start with file watching |
| 172 | +npm run build # Build application |
| 173 | +npm run lint # Run ESLint |
| 174 | +npm run test:e2e # Run E2E tests |
| 175 | +npm run docs # Generate documentation |
| 176 | +``` |
| 177 | + |
| 178 | +### Package Development |
| 179 | +```bash |
| 180 | +npm run build:dev # Build and push to yalc |
| 181 | +npm run build:pack # Create tarball for testing |
| 182 | +``` |
| 183 | + |
| 184 | +## Anti-Patterns to Avoid |
| 185 | + |
| 186 | +❌ **Don't do:** |
| 187 | +- Create models without extending `CorePersistenceModel` |
| 188 | +- Use plain `@Field()` instead of `@UnifiedField()` |
| 189 | +- Expose sensitive data without `@Restricted()` |
| 190 | +- Create services without proper CRUD patterns |
| 191 | +- Skip input validation |
| 192 | +- Hardcode configuration values |
| 193 | +- Create duplicate authentication logic |
| 194 | + |
| 195 | +✅ **Do instead:** |
| 196 | +- Follow established inheritance patterns |
| 197 | +- Use framework decorators and helpers |
| 198 | +- Implement proper security restrictions |
| 199 | +- Leverage existing CRUD functionality |
| 200 | +- Use configuration system |
| 201 | +- Follow modular architecture |
| 202 | + |
| 203 | +## TypeScript Guidelines |
| 204 | + |
| 205 | +- Use strict typing, avoid `any` |
| 206 | +- Use interfaces for complex data structures |
| 207 | +- Prefer composition over inheritance where appropriate |
| 208 | +- Use proper generics for reusable components |
| 209 | +- Export types alongside implementation files |
| 210 | + |
| 211 | +## Email & Templates |
| 212 | + |
| 213 | +- Use `EmailService` for sending emails |
| 214 | +- Templates are in `src/templates/` using EJS |
| 215 | +- Support both SMTP and Mailjet providers |
| 216 | +- Use `TemplateService` for rendering |
| 217 | + |
| 218 | +Remember: This is a **framework package** that can be extended. Focus on creating reusable, well-documented components that follow the established patterns. |
0 commit comments