Backend
Skill VoDaiLocz/kilo-kit-mcp/skills/kilo-kit/development/backend
An MCP server for safer coding agents: skill routing, C4 workflow gates, memory checks, and verification before completion.
npx -y skills add VoDaiLocz/kilo-kit-mcp --skill backendAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 24 stars24 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Comprehensive backend API development skill for building robust, scalable APIs. Use when creating new endpoints, services, or backend functionality. Keywords: API, backend, endpoint, service, REST, GraphQL, server, controller, route
SKILL.md
12.9 KB, as published. Nobody here has run it
π§ Backend API Development Skill
Philosophy: APIs are contracts. Build them right the first time.
When to Use
Use this skill when:
- Creating a new API endpoint
- Building a new service/module
- Refactoring existing API code
- Adding new functionality to backend
- Need to follow RESTful/GraphQL best practices
Do NOT use this skill when:
- Just fixing a small bug (use debugging skill)
- Only modifying frontend (use frontend skill)
- Database-only changes (use database skill)
Prerequisites
Before starting:
- Requirements are clear (what the API should do)
- Understand the existing architecture
- Know the target stack (NestJS, Express, FastAPI, etc.)
- Database schema exists (or will be created)
Process
Phase 1: DESIGN π
Goal: Design the API before writing code.
Steps:
-
Define the Resource
resource: name: User description: Represents a platform user domain: authentication -
Design Endpoints (REST)
endpoints: - method: GET path: /users description: List all users query_params: [page, limit, search] response: User[] - method: GET path: /users/:id description: Get single user response: User - method: POST path: /users description: Create new user body: CreateUserDto response: User - method: PUT path: /users/:id description: Update user body: UpdateUserDto response: User - method: DELETE path: /users/:id description: Delete user response: void -
Define DTOs (Data Transfer Objects)
// CreateUserDto interface CreateUserDto { email: string; // required, email format password: string; // required, min 8 chars name: string; // required, min 2 chars role?: UserRole; // optional, default: 'user' } // UpdateUserDto type UpdateUserDto = Partial<CreateUserDto>; // UserResponseDto interface UserResponseDto { id: string; email: string; name: string; role: UserRole; createdAt: DateTime; updatedAt: DateTime; // Note: password NOT included } -
Plan Error Responses
errors: - code: 400 when: Invalid input response: { message, errors: [{field, message}] } - code: 401 when: Not authenticated response: { message: "Unauthorized" } - code: 403 when: Not authorized response: { message: "Forbidden" } - code: 404 when: Resource not found response: { message: "User not found" } - code: 409 when: Conflict (e.g., email exists) response: { message: "Email already registered" }
Output: Complete API design document.
Phase 2: STRUCTURE ποΈ
Goal: Set up the file structure.
NestJS Structure:
src/
βββ users/
βββ users.module.ts # Module definition
βββ users.controller.ts # HTTP layer
βββ users.service.ts # Business logic
βββ users.repository.ts # Data access (optional)
βββ dto/
β βββ create-user.dto.ts
β βββ update-user.dto.ts
β βββ user-response.dto.ts
βββ entities/
β βββ user.entity.ts
βββ guards/
β βββ user-owner.guard.ts
βββ users.controller.spec.ts
FastAPI Structure:
app/
βββ users/
βββ __init__.py
βββ router.py # Routes
βββ service.py # Business logic
βββ repository.py # Data access
βββ schemas.py # Pydantic models
βββ models.py # SQLAlchemy models
βββ dependencies.py # Dependency injection
Phase 3: IMPLEMENTATION π»
Goal: Implement the API layer by layer.
Order of Implementation:
-
Entity/Model First
// user.entity.ts @Entity('users') export class User { @PrimaryGeneratedColumn('uuid') id: string; @Column({ unique: true }) @IsEmail() email: string; @Column() @Exclude() // Never expose password password: string; @Column() name: string; @Column({ default: 'user' }) role: UserRole; @CreateDateColumn() createdAt: Date; @UpdateDateColumn() updatedAt: Date; } -
DTOs with Validation
// create-user.dto.ts export class CreateUserDto { @IsEmail() @Transform(({ value }) => value.toLowerCase().trim()) email: string; @IsString() @MinLength(8) @Matches(/^(?=.*[A-Za-z])(?=.*\d)/, { message: 'Password must contain letters and numbers' }) password: string; @IsString() @MinLength(2) @MaxLength(50) name: string; @IsOptional() @IsEnum(UserRole) role?: UserRole; } -
Service Layer (Business Logic)
// users.service.ts @Injectable() export class UsersService { constructor( @InjectRepository(User) private usersRepository: Repository<User>, ) {} async create(dto: CreateUserDto): Promise<User> { // Check for existing email const existing = await this.findByEmail(dto.email); if (existing) { throw new ConflictException('Email already registered'); } // Hash password const hashedPassword = await bcrypt.hash(dto.password, 10); // Create and save const user = this.usersRepository.create({ ...dto, password: hashedPassword, }); return this.usersRepository.save(user); } async findAll(options: PaginationOptions): Promise<PaginatedResult<User>> { // Implementation with pagination } // ... other methods } -
Controller (HTTP Layer)
// users.controller.ts @Controller('users') @UseInterceptors(ClassSerializerInterceptor) export class UsersController { constructor(private readonly usersService: UsersService) {} @Post() @HttpCode(HttpStatus.CREATED) async create(@Body() dto: CreateUserDto): Promise<UserResponseDto> { const user = await this.usersService.create(dto); return plainToInstance(UserResponseDto, user); } @Get() @UseGuards(AuthGuard) async findAll( @Query() query: PaginationQueryDto ): Promise<PaginatedResult<UserResponseDto>> { return this.usersService.findAll(query); } @Get(':id') @UseGuards(AuthGuard) async findOne(@Param('id', ParseUUIDPipe) id: string): Promise<UserResponseDto> { const user = await this.usersService.findOne(id); if (!user) { throw new NotFoundException('User not found'); } return plainToInstance(UserResponseDto, user); } // ... other endpoints }
Phase 4: SECURITY π
Goal: Ensure API is secure.
Security Checklist:
-
Input Validation
- All inputs validated with DTOs
- Types enforced
- Length limits set
- Format validation (email, UUID, etc.)
-
Authentication
- Protected routes require authentication
- JWT or session validation
- Token expiration handled
-
Authorization
- Role-based access control
- Resource ownership verified
- Admin-only routes protected
-
Data Protection
- Passwords hashed (bcrypt, argon2)
- Sensitive data not logged
- Passwords excluded from responses
-
Rate Limiting
- Login attempts limited
- API rate limiting in place
-
SQL Injection Prevention
- Parameterized queries used
- ORM used correctly
- Raw queries avoided or sanitized
Phase 5: TESTING π§ͺ
Goal: Write comprehensive tests.
Test Types:
-
Unit Tests
describe('UsersService', () => { describe('create', () => { it('should create a new user', async () => { const dto = { email: '[email protected]', ... }; const result = await service.create(dto); expect(result.email).toBe(dto.email); }); it('should hash the password', async () => { const dto = { password: 'plaintext', ... }; const result = await service.create(dto); expect(result.password).not.toBe(dto.password); }); it('should throw on duplicate email', async () => { // Setup: create user first await service.create({ email: '[email protected]', ... }); // Act & Assert await expect( service.create({ email: '[email protected]', ... }) ).rejects.toThrow(ConflictException); }); }); }); -
Integration Tests
describe('Users API', () => { it('POST /users should create user', async () => { const response = await request(app.getHttpServer()) .post('/users') .send({ email: '[email protected]', password: 'Password1', name: 'Test' }) .expect(201); expect(response.body.email).toBe('[email protected]'); expect(response.body.password).toBeUndefined(); }); it('GET /users should require auth', async () => { await request(app.getHttpServer()) .get('/users') .expect(401); }); });
Phase 6: DOCUMENTATION π
Goal: Document the API.
OpenAPI/Swagger:
@ApiTags('users')
@Controller('users')
export class UsersController {
@Post()
@ApiOperation({ summary: 'Create a new user' })
@ApiResponse({ status: 201, type: UserResponseDto })
@ApiResponse({ status: 400, description: 'Invalid input' })
@ApiResponse({ status: 409, description: 'Email already exists' })
async create(@Body() dto: CreateUserDto): Promise<UserResponseDto> {
// ...
}
}
Response DTO Documentation:
export class UserResponseDto {
@ApiProperty({ example: '550e8400-e29b-41d4-a716-446655440000' })
id: string;
@ApiProperty({ example: '[email protected]' })
email: string;
@ApiProperty({ example: 'John Doe' })
name: string;
}
Best Practices
API Design
| Practice | Do | Don't |
|---|---|---|
| Naming | GET /users/:id/orders | GET /getUserOrders |
| Versioning | /api/v1/users | No versioning |
| Pluralization | /users, /orders | /user, /order |
| HTTP Methods | Use correctly (GET=read, POST=create) | POST for everything |
| Status Codes | 201 for created, 204 for no content | 200 for everything |
Error Handling
// Global exception filter
@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const status = exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
const message = exception instanceof HttpException
? exception.message
: 'Internal server error';
response.status(status).json({
statusCode: status,
message,
timestamp: new Date().toISOString(),
});
}
}
Guidelines
DO β
- Design API before coding
- Use proper HTTP methods and status codes
- Validate all inputs
- Handle errors gracefully
- Write tests first (TDD)
- Document with OpenAPI
DON'T β
- Expose internal IDs when UUIDs are better
- Return password or sensitive data
- Use GET for mutations
- Skip input validation
- Catch and swallow errors
- Use magic strings/numbers
Success Criteria
Before considering API complete:
- All endpoints implemented per design
- Input validation on all endpoints
- Authentication/Authorization in place
- Error handling comprehensive
- Unit tests with >80% coverage
- Integration tests for main flows
- API documented (OpenAPI/Swagger)
- Security checklist passed
Related Skills
skills/kilo-kit/development/database/- For data layerskills/kilo-kit/development/security/- For security concernsskills/kilo-kit/quality/testing/- For test coverageskills/kilo-kit/architecture/system-design/- For architecture decisions
Backend API Development Skill v1.0.0 β APIs built right