Initial commit: The Ultimate Antigravity Skills Collection (58 Skills)
This commit is contained in:
638
skills/backend-dev-guidelines/resources/complete-examples.md
Normal file
638
skills/backend-dev-guidelines/resources/complete-examples.md
Normal file
@@ -0,0 +1,638 @@
|
||||
# Complete Examples - Full Working Code
|
||||
|
||||
Real-world examples showing complete implementation patterns.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Complete Controller Example](#complete-controller-example)
|
||||
- [Complete Service with DI](#complete-service-with-di)
|
||||
- [Complete Route File](#complete-route-file)
|
||||
- [Complete Repository](#complete-repository)
|
||||
- [Refactoring Example: Bad to Good](#refactoring-example-bad-to-good)
|
||||
- [End-to-End Feature Example](#end-to-end-feature-example)
|
||||
|
||||
---
|
||||
|
||||
## Complete Controller Example
|
||||
|
||||
### UserController (Following All Best Practices)
|
||||
|
||||
```typescript
|
||||
// controllers/UserController.ts
|
||||
import { Request, Response } from 'express';
|
||||
import { BaseController } from './BaseController';
|
||||
import { UserService } from '../services/userService';
|
||||
import { createUserSchema, updateUserSchema } from '../validators/userSchemas';
|
||||
import { z } from 'zod';
|
||||
|
||||
export class UserController extends BaseController {
|
||||
private userService: UserService;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.userService = new UserService();
|
||||
}
|
||||
|
||||
async getUser(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
this.addBreadcrumb('Fetching user', 'user_controller', {
|
||||
userId: req.params.id,
|
||||
});
|
||||
|
||||
const user = await this.withTransaction(
|
||||
'user.get',
|
||||
'db.query',
|
||||
() => this.userService.findById(req.params.id)
|
||||
);
|
||||
|
||||
if (!user) {
|
||||
return this.handleError(
|
||||
new Error('User not found'),
|
||||
res,
|
||||
'getUser',
|
||||
404
|
||||
);
|
||||
}
|
||||
|
||||
this.handleSuccess(res, user);
|
||||
} catch (error) {
|
||||
this.handleError(error, res, 'getUser');
|
||||
}
|
||||
}
|
||||
|
||||
async listUsers(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const users = await this.userService.getAll();
|
||||
this.handleSuccess(res, users);
|
||||
} catch (error) {
|
||||
this.handleError(error, res, 'listUsers');
|
||||
}
|
||||
}
|
||||
|
||||
async createUser(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
// Validate input with Zod
|
||||
const validated = createUserSchema.parse(req.body);
|
||||
|
||||
// Track performance
|
||||
const user = await this.withTransaction(
|
||||
'user.create',
|
||||
'db.mutation',
|
||||
() => this.userService.create(validated)
|
||||
);
|
||||
|
||||
this.handleSuccess(res, user, 'User created successfully', 201);
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
return this.handleError(error, res, 'createUser', 400);
|
||||
}
|
||||
this.handleError(error, res, 'createUser');
|
||||
}
|
||||
}
|
||||
|
||||
async updateUser(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const validated = updateUserSchema.parse(req.body);
|
||||
|
||||
const user = await this.userService.update(
|
||||
req.params.id,
|
||||
validated
|
||||
);
|
||||
|
||||
this.handleSuccess(res, user, 'User updated');
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
return this.handleError(error, res, 'updateUser', 400);
|
||||
}
|
||||
this.handleError(error, res, 'updateUser');
|
||||
}
|
||||
}
|
||||
|
||||
async deleteUser(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
await this.userService.delete(req.params.id);
|
||||
this.handleSuccess(res, null, 'User deleted', 204);
|
||||
} catch (error) {
|
||||
this.handleError(error, res, 'deleteUser');
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Complete Service with DI
|
||||
|
||||
### UserService
|
||||
|
||||
```typescript
|
||||
// services/userService.ts
|
||||
import { UserRepository } from '../repositories/UserRepository';
|
||||
import { ConflictError, NotFoundError, ValidationError } from '../types/errors';
|
||||
import type { CreateUserDTO, UpdateUserDTO, User } from '../types/user.types';
|
||||
|
||||
export class UserService {
|
||||
private userRepository: UserRepository;
|
||||
|
||||
constructor(userRepository?: UserRepository) {
|
||||
this.userRepository = userRepository || new UserRepository();
|
||||
}
|
||||
|
||||
async findById(id: string): Promise<User | null> {
|
||||
return await this.userRepository.findById(id);
|
||||
}
|
||||
|
||||
async getAll(): Promise<User[]> {
|
||||
return await this.userRepository.findActive();
|
||||
}
|
||||
|
||||
async create(data: CreateUserDTO): Promise<User> {
|
||||
// Business rule: validate age
|
||||
if (data.age < 18) {
|
||||
throw new ValidationError('User must be 18 or older');
|
||||
}
|
||||
|
||||
// Business rule: check email uniqueness
|
||||
const existing = await this.userRepository.findByEmail(data.email);
|
||||
if (existing) {
|
||||
throw new ConflictError('Email already in use');
|
||||
}
|
||||
|
||||
// Create user with profile
|
||||
return await this.userRepository.create({
|
||||
email: data.email,
|
||||
profile: {
|
||||
create: {
|
||||
firstName: data.firstName,
|
||||
lastName: data.lastName,
|
||||
age: data.age,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async update(id: string, data: UpdateUserDTO): Promise<User> {
|
||||
// Check exists
|
||||
const existing = await this.userRepository.findById(id);
|
||||
if (!existing) {
|
||||
throw new NotFoundError('User not found');
|
||||
}
|
||||
|
||||
// Business rule: email uniqueness if changing
|
||||
if (data.email && data.email !== existing.email) {
|
||||
const emailTaken = await this.userRepository.findByEmail(data.email);
|
||||
if (emailTaken) {
|
||||
throw new ConflictError('Email already in use');
|
||||
}
|
||||
}
|
||||
|
||||
return await this.userRepository.update(id, data);
|
||||
}
|
||||
|
||||
async delete(id: string): Promise<void> {
|
||||
const existing = await this.userRepository.findById(id);
|
||||
if (!existing) {
|
||||
throw new NotFoundError('User not found');
|
||||
}
|
||||
|
||||
await this.userRepository.delete(id);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Complete Route File
|
||||
|
||||
### userRoutes.ts
|
||||
|
||||
```typescript
|
||||
// routes/userRoutes.ts
|
||||
import { Router } from 'express';
|
||||
import { UserController } from '../controllers/UserController';
|
||||
import { SSOMiddlewareClient } from '../middleware/SSOMiddleware';
|
||||
import { auditMiddleware } from '../middleware/auditMiddleware';
|
||||
|
||||
const router = Router();
|
||||
const controller = new UserController();
|
||||
|
||||
// GET /users - List all users
|
||||
router.get('/',
|
||||
SSOMiddlewareClient.verifyLoginStatus,
|
||||
auditMiddleware,
|
||||
async (req, res) => controller.listUsers(req, res)
|
||||
);
|
||||
|
||||
// GET /users/:id - Get single user
|
||||
router.get('/:id',
|
||||
SSOMiddlewareClient.verifyLoginStatus,
|
||||
auditMiddleware,
|
||||
async (req, res) => controller.getUser(req, res)
|
||||
);
|
||||
|
||||
// POST /users - Create user
|
||||
router.post('/',
|
||||
SSOMiddlewareClient.verifyLoginStatus,
|
||||
auditMiddleware,
|
||||
async (req, res) => controller.createUser(req, res)
|
||||
);
|
||||
|
||||
// PUT /users/:id - Update user
|
||||
router.put('/:id',
|
||||
SSOMiddlewareClient.verifyLoginStatus,
|
||||
auditMiddleware,
|
||||
async (req, res) => controller.updateUser(req, res)
|
||||
);
|
||||
|
||||
// DELETE /users/:id - Delete user
|
||||
router.delete('/:id',
|
||||
SSOMiddlewareClient.verifyLoginStatus,
|
||||
auditMiddleware,
|
||||
async (req, res) => controller.deleteUser(req, res)
|
||||
);
|
||||
|
||||
export default router;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Complete Repository
|
||||
|
||||
### UserRepository
|
||||
|
||||
```typescript
|
||||
// repositories/UserRepository.ts
|
||||
import { PrismaService } from '@project-lifecycle-portal/database';
|
||||
import type { User, Prisma } from '@prisma/client';
|
||||
|
||||
export class UserRepository {
|
||||
async findById(id: string): Promise<User | null> {
|
||||
return PrismaService.main.user.findUnique({
|
||||
where: { id },
|
||||
include: { profile: true },
|
||||
});
|
||||
}
|
||||
|
||||
async findByEmail(email: string): Promise<User | null> {
|
||||
return PrismaService.main.user.findUnique({
|
||||
where: { email },
|
||||
include: { profile: true },
|
||||
});
|
||||
}
|
||||
|
||||
async findActive(): Promise<User[]> {
|
||||
return PrismaService.main.user.findMany({
|
||||
where: { isActive: true },
|
||||
include: { profile: true },
|
||||
orderBy: { createdAt: 'desc' },
|
||||
});
|
||||
}
|
||||
|
||||
async create(data: Prisma.UserCreateInput): Promise<User> {
|
||||
return PrismaService.main.user.create({
|
||||
data,
|
||||
include: { profile: true },
|
||||
});
|
||||
}
|
||||
|
||||
async update(id: string, data: Prisma.UserUpdateInput): Promise<User> {
|
||||
return PrismaService.main.user.update({
|
||||
where: { id },
|
||||
data,
|
||||
include: { profile: true },
|
||||
});
|
||||
}
|
||||
|
||||
async delete(id: string): Promise<User> {
|
||||
// Soft delete
|
||||
return PrismaService.main.user.update({
|
||||
where: { id },
|
||||
data: {
|
||||
isActive: false,
|
||||
deletedAt: new Date(),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Refactoring Example: Bad to Good
|
||||
|
||||
### BEFORE: Business Logic in Routes ❌
|
||||
|
||||
```typescript
|
||||
// routes/postRoutes.ts (BAD - 200+ lines)
|
||||
router.post('/posts', async (req, res) => {
|
||||
try {
|
||||
const username = res.locals.claims.preferred_username;
|
||||
const responses = req.body.responses;
|
||||
const stepInstanceId = req.body.stepInstanceId;
|
||||
|
||||
// ❌ Permission check in route
|
||||
const userId = await userProfileService.getProfileByEmail(username).then(p => p.id);
|
||||
const canComplete = await permissionService.canCompleteStep(userId, stepInstanceId);
|
||||
if (!canComplete) {
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
}
|
||||
|
||||
// ❌ Business logic in route
|
||||
const post = await postRepository.create({
|
||||
title: req.body.title,
|
||||
content: req.body.content,
|
||||
authorId: userId
|
||||
});
|
||||
|
||||
// ❌ More business logic...
|
||||
if (res.locals.isImpersonating) {
|
||||
impersonationContextStore.storeContext(...);
|
||||
}
|
||||
|
||||
// ... 100+ more lines
|
||||
|
||||
res.json({ success: true, data: result });
|
||||
} catch (e) {
|
||||
handler.handleException(res, e);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### AFTER: Clean Separation ✅
|
||||
|
||||
**1. Clean Route:**
|
||||
```typescript
|
||||
// routes/postRoutes.ts
|
||||
import { PostController } from '../controllers/PostController';
|
||||
|
||||
const router = Router();
|
||||
const controller = new PostController();
|
||||
|
||||
// ✅ CLEAN: 8 lines total!
|
||||
router.post('/',
|
||||
SSOMiddlewareClient.verifyLoginStatus,
|
||||
auditMiddleware,
|
||||
async (req, res) => controller.createPost(req, res)
|
||||
);
|
||||
|
||||
export default router;
|
||||
```
|
||||
|
||||
**2. Controller:**
|
||||
```typescript
|
||||
// controllers/PostController.ts
|
||||
export class PostController extends BaseController {
|
||||
private postService: PostService;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.postService = new PostService();
|
||||
}
|
||||
|
||||
async createPost(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const validated = createPostSchema.parse({
|
||||
...req.body,
|
||||
});
|
||||
|
||||
const result = await this.postService.createPost(
|
||||
validated,
|
||||
res.locals.userId
|
||||
);
|
||||
|
||||
this.handleSuccess(res, result, 'Post created successfully');
|
||||
} catch (error) {
|
||||
this.handleError(error, res, 'createPost');
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**3. Service:**
|
||||
```typescript
|
||||
// services/postService.ts
|
||||
export class PostService {
|
||||
async createPost(
|
||||
data: CreatePostDTO,
|
||||
userId: string
|
||||
): Promise<SubmissionResult> {
|
||||
// Permission check
|
||||
const canComplete = await permissionService.canCompleteStep(
|
||||
userId,
|
||||
data.stepInstanceId
|
||||
);
|
||||
|
||||
if (!canComplete) {
|
||||
throw new ForbiddenError('No permission to complete step');
|
||||
}
|
||||
|
||||
// Execute workflow
|
||||
const engine = await createWorkflowEngine();
|
||||
const command = new CompleteStepCommand(
|
||||
data.stepInstanceId,
|
||||
userId,
|
||||
data.responses
|
||||
);
|
||||
const events = await engine.executeCommand(command);
|
||||
|
||||
// Handle impersonation
|
||||
if (context.isImpersonating) {
|
||||
await this.handleImpersonation(data.stepInstanceId, context);
|
||||
}
|
||||
|
||||
return { events, success: true };
|
||||
}
|
||||
|
||||
private async handleImpersonation(stepInstanceId: number, context: any) {
|
||||
impersonationContextStore.storeContext(stepInstanceId, {
|
||||
originalUserId: context.originalUserId,
|
||||
effectiveUserId: context.effectiveUserId,
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Result:**
|
||||
- Route: 8 lines (was 200+)
|
||||
- Controller: 25 lines
|
||||
- Service: 40 lines
|
||||
- **Testable, maintainable, reusable!**
|
||||
|
||||
---
|
||||
|
||||
## End-to-End Feature Example
|
||||
|
||||
### Complete User Management Feature
|
||||
|
||||
**1. Types:**
|
||||
```typescript
|
||||
// types/user.types.ts
|
||||
export interface User {
|
||||
id: string;
|
||||
email: string;
|
||||
isActive: boolean;
|
||||
profile?: UserProfile;
|
||||
}
|
||||
|
||||
export interface CreateUserDTO {
|
||||
email: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
age: number;
|
||||
}
|
||||
|
||||
export interface UpdateUserDTO {
|
||||
email?: string;
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
}
|
||||
```
|
||||
|
||||
**2. Validators:**
|
||||
```typescript
|
||||
// validators/userSchemas.ts
|
||||
import { z } from 'zod';
|
||||
|
||||
export const createUserSchema = z.object({
|
||||
email: z.string().email(),
|
||||
firstName: z.string().min(1).max(100),
|
||||
lastName: z.string().min(1).max(100),
|
||||
age: z.number().int().min(18).max(120),
|
||||
});
|
||||
|
||||
export const updateUserSchema = z.object({
|
||||
email: z.string().email().optional(),
|
||||
firstName: z.string().min(1).max(100).optional(),
|
||||
lastName: z.string().min(1).max(100).optional(),
|
||||
});
|
||||
```
|
||||
|
||||
**3. Repository:**
|
||||
```typescript
|
||||
// repositories/UserRepository.ts
|
||||
export class UserRepository {
|
||||
async findById(id: string): Promise<User | null> {
|
||||
return PrismaService.main.user.findUnique({
|
||||
where: { id },
|
||||
include: { profile: true },
|
||||
});
|
||||
}
|
||||
|
||||
async create(data: Prisma.UserCreateInput): Promise<User> {
|
||||
return PrismaService.main.user.create({
|
||||
data,
|
||||
include: { profile: true },
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**4. Service:**
|
||||
```typescript
|
||||
// services/userService.ts
|
||||
export class UserService {
|
||||
private userRepository: UserRepository;
|
||||
|
||||
constructor() {
|
||||
this.userRepository = new UserRepository();
|
||||
}
|
||||
|
||||
async create(data: CreateUserDTO): Promise<User> {
|
||||
const existing = await this.userRepository.findByEmail(data.email);
|
||||
if (existing) {
|
||||
throw new ConflictError('Email already exists');
|
||||
}
|
||||
|
||||
return await this.userRepository.create({
|
||||
email: data.email,
|
||||
profile: {
|
||||
create: {
|
||||
firstName: data.firstName,
|
||||
lastName: data.lastName,
|
||||
age: data.age,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**5. Controller:**
|
||||
```typescript
|
||||
// controllers/UserController.ts
|
||||
export class UserController extends BaseController {
|
||||
private userService: UserService;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.userService = new UserService();
|
||||
}
|
||||
|
||||
async createUser(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const validated = createUserSchema.parse(req.body);
|
||||
const user = await this.userService.create(validated);
|
||||
this.handleSuccess(res, user, 'User created', 201);
|
||||
} catch (error) {
|
||||
this.handleError(error, res, 'createUser');
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**6. Routes:**
|
||||
```typescript
|
||||
// routes/userRoutes.ts
|
||||
const router = Router();
|
||||
const controller = new UserController();
|
||||
|
||||
router.post('/',
|
||||
SSOMiddlewareClient.verifyLoginStatus,
|
||||
async (req, res) => controller.createUser(req, res)
|
||||
);
|
||||
|
||||
export default router;
|
||||
```
|
||||
|
||||
**7. Register in app.ts:**
|
||||
```typescript
|
||||
// app.ts
|
||||
import userRoutes from './routes/userRoutes';
|
||||
|
||||
app.use('/api/users', userRoutes);
|
||||
```
|
||||
|
||||
**Complete Request Flow:**
|
||||
```
|
||||
POST /api/users
|
||||
↓
|
||||
userRoutes matches /
|
||||
↓
|
||||
SSOMiddleware authenticates
|
||||
↓
|
||||
controller.createUser called
|
||||
↓
|
||||
Validates with Zod
|
||||
↓
|
||||
userService.create called
|
||||
↓
|
||||
Checks business rules
|
||||
↓
|
||||
userRepository.create called
|
||||
↓
|
||||
Prisma creates user
|
||||
↓
|
||||
Returns up the chain
|
||||
↓
|
||||
Controller formats response
|
||||
↓
|
||||
200/201 sent to client
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Related Files:**
|
||||
- [SKILL.md](SKILL.md)
|
||||
- [routing-and-controllers.md](routing-and-controllers.md)
|
||||
- [services-and-repositories.md](services-and-repositories.md)
|
||||
- [validation-patterns.md](validation-patterns.md)
|
||||
Reference in New Issue
Block a user