api-controller
This skill teaches the agent how to implement the controller layer in the API project. MANDATORY - You MUST read this skill before modifying any controller files.
- Category: discovered
- Tags: github, community
- Version: 1.0.0
- Author: walid
- Downloads: 0
- Updated: 2025-11-22
Download the latest version (v1.0.0) from this page.
SKILL.md
---
name: api-controller
description: This skill teaches the agent how to implement the controller layer in the API project. MANDATORY - You MUST read this skill before modifying any controller files.
---
# API Controller Layer Skill
## Purpose
This skill covers the controller layer of the API project - the HTTP request/response handling layer that delegates to services for business logic and provides API documentation.
## When to Use This Skill
- Adding new API endpoints
- Handling HTTP request/response flow
- Adding Swagger documentation
- Managing authentication/authorization middleware
- Parsing and validating request parameters
## Key Patterns from Codebase
### 1. Controller Structure with Swagger Documentation
```typescript
// api/src/api/controllers/community.controller.ts
import { Response, NextFunction } from 'express';
import { AuthenticatedRequest } from '@/types/auth.types';
import { communityService } from '@/services/community.service';
import { ApiResponse } from '@/utils/response';
import logger from '@/utils/logger';
/**
* @swagger
* tags:
* name: Communities
* description: Community management endpoints
*/
class CommunityController {
/**
* @swagger
* /api/v1/communities:
* post:
* summary: Create a new community
* tags: [Communities]
* security:
* - bearerAuth: []
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - name
* properties:
* name:
* type: string
* minLength: 1
* maxLength: 100
* description:
* type: string
* responses:
* 201:
* description: Community created successfully
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Community'
* 400:
* description: Invalid request data
* 401:
* description: Unauthorized
* 500:
* description: Internal server error
*/
async create(req: AuthenticatedRequest, res: Response, next: NextFunction) {
try {
const userId = req.session!.userId;
const community = await communityService.createCommunity(req.body, userId);
logger.info("Community created", { communityId: community.id, userId });
return ApiResponse.created(res, community, "Community created successfully");
} catch (error) {
next(error);
}
}
/**
* @swagger
* /api/v1/communities/{id}:
* get:
* summary: Get community by ID
* tags: [Communities]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* format: uuid
* description: Community ID
* responses:
* 200:
* description: Community details
* 404:
* description: Community not found
*/
async getById(req: AuthenticatedRequest, res: Response, next: NextFunction) {
try {
const { id } = req.params;
const userId = req.session?.userId; // Optional for public communities
const community = await communityService.getCommunityById(id, userId);
return ApiResponse.success(res, community);
} catch (error) {
next(error);
}
}
/**
* @swagger
* /api/v1/communities:
* get:
* summary: List communities
* tags: [Communities]
* security:
* - bearerAuth: []
* parameters:
* - in: query
* name: page
* schema:
* type: integer
* minimum: 0
* default: 0
* - in: query
* name: limit
* schema:
* type: integer
* minimum: 1
* maximum: 100
* default: 20
* responses:
* 200:
* description: List of communities
* content:
* application/json:
* schema:
* type: object
* properties:
* rows:
* type: array
* items:
* $ref: '#/components/schemas/Community'
* total:
* type: integer
*/
async list(req: AuthenticatedRequest, res: Response, next: NextFunction) {
try {
const { page = 0, limit = 20 } = req.query;
const userId = req.session?.userId;
const result = await communityService.listCommunities(
Number(page),
Number(limit),
userId
);
return ApiResponse.success(res, result);
} catch (error) {
next(error);
}
}
}
export const communityController = new CommunityController();
```
### 2. Using ApiResponse Utility
```typescript
import { ApiResponse } from '@/utils/response';
// Success responses
ApiResponse.success(res, data); // 200
ApiResponse.created(res, data, "Resource created"); // 201
ApiResponse.noContent(res); // 204
// Error responses are handled by error middleware
// Just throw AppError from service layer
```
### 3. Controller with Complex Query Parameters
```typescript
/**
* @swagger
* /api/v1/communities/search:
* get:
* summary: Search communities
* tags: [Communities]
* parameters:
* - in: query
* name: q
* schema:
* type: string
* maxLength: 200
* description: Search query
* - in: query
* name: page
* schema:
* type: integer
* minimum: 0
* - in: query
* name: limit
* schema:
* type: integer
* minimum: 1
* maximum: 100
*/
async search(req: AuthenticatedRequest, res: Response, next: NextFunction) {
try {
const { q, page = 0, limit = 20 } = req.query;
const userId = req.session?.userId;
const result = await communityService.searchCommunities({
query: q as string,
page: Number(page),
limit: Number(limit),
userId
});
return ApiResponse.success(res, result);
} catch (error) {
next(error);
}
}
```
### 4. Controller with Path and Body Parameters
```typescript
/**
* @swagger
* /api/v1/communities/{id}/members/{userId}:
* put:
* summary: Update member role
* tags: [Communities]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* format: uuid
* - in: path
* name: userId
* required: true
* schema:
* type: string
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - role
* properties:
* role:
* type: string
* minLength: 1
* maxLength: 64
*/
async updateMemberRole(req: AuthenticatedRequest, res: Response, next: NextFunction) {
try {
const { id, userId: targetUserId } = req.params;
const { role } = req.body;
const actorUserId = req.session!.userId;
await communityService.updateMemberRole(id, targetUserId, role, actorUserId);
return ApiResponse.success(res, null, "Member role updated");
} catch (error) {
next(error);
}
}
```
### 5. Delete Endpoint
```typescript
/**
* @swagger
* /api/v1/communities/{id}:
* delete:
* summary: Delete a community
* tags: [Communities]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* format: uuid
* responses:
* 204:
* description: Community deleted successfully
* 403:
* description: Forbidden - admin only
* 404:
* description: Community not found
*/
async delete(req: AuthenticatedRequest, res: Response, next: NextFunction) {
try {
const { id } = req.params;
const userId = req.session!.userId;
await communityService.deleteCommunity(id, userId);
logger.info("Community deleted via API", { communityId: id, userId });
return ApiResponse.noContent(res);
} catch (error) {
next(error);
}
}
```
## Swagger Schema Definitions
Define reusable schemas in your Swagger configuration:
```typescript
/**
* @swagger
* components:
* schemas:
* Community:
* type: object
* properties:
* id:
* type: string
* format: uuid
* name:
* type: string
* description:
* type: string
* createdBy:
* type: string
* createdAt:
* type: string
* format: date-time
* updatedAt:
* type: string
* format: date-time
* securitySchemes:
* bearerAuth:
* type: http
* scheme: bearer
* bearerFormat: JWT
*/
```
## Key Principles
1. **No Business Logic**: Controllers only orchestrate, never implement logic
2. **Thin Layer**: Extract userId from session, call service, return response
3. **Error Handling**: Always use try-catch and pass errors to next()
4. **Swagger Documentation**: Every endpoint must have complete JSDoc @swagger
5. **Logging**: Log significant actions (create, update, delete) with context
6. **Type Safety**: Use AuthenticatedRequest for protected routes
7. **Consistent Responses**: Always use ApiResponse utility
8. **Parameter Extraction**: Parse query/params/body, validate in middleware
## Controller Testing
Controllers are tested indirectly through integration tests. Focus on service layer unit tests.
## Related Skills
- `api-service` - Business logic layer
- `api-routes` - Route definitions
- `api-validators` - Request validation
- `api-testing` - Integration testing
## Feature Documentation
Before implementing controller endpoints, **MUST READ** the relevant feature documentation in `docs/features/` to understand:
- API endpoint requirements
- Request/response formats
- Authorization requirements
- Related endpoints