Implementing Backend
Provides patterns for building backend API services with modular architecture, structured testing, and production-ready infrastructure following TSH best practices.
When to Use
- Building new REST or GraphQL API endpoints
- Implementing CRUD operations with filtering, sorting, and pagination
- Setting up authentication and authorization (JWT)
- Integrating with external/third-party services
- Writing integration tests for endpoints or unit tests for business logic
- Configuring database migrations, seeding, or repository patterns
- Setting up Docker and docker-compose for local development
- Implementing logging and observability
- Documenting APIs with Swagger/OpenAPI
- Designing modular architecture with vertical slices
Guiding Principles
| Principle |
Application |
| SRP |
Each class/module has one reason to change. Controllers handle HTTP, services handle business logic, repositories handle data access. |
| DRY |
Extract shared logic into reusable services or utilities. Do not duplicate validation, mapping, or query logic. |
| KISS |
Prefer simple, readable solutions. Avoid over-engineering. Do not add abstractions until they are needed. |
| YAGNI |
Do not build features or infrastructure "just in case". Implement what is needed now. |
| Pragmatism |
Follow patterns when they add value. Break rules when strict adherence creates unnecessary complexity. Document the reasoning. |
Architecture: Vertical Slice / Modular Structure
Organize code by domain/feature, not by technical layer. All artifacts related to a domain live in the same directory.
src/
├── users/
│ ├── users.controller.ts # HTTP layer (routes, request/response)
│ ├── users.service.ts # Business logic
│ ├── users.repository.ts # Data access
│ ├── users.module.ts # Module registration / DI wiring
│ ├── dto/
│ │ ├── create-user.dto.ts
│ │ └── update-user.dto.ts
│ ├── entities/
│ │ └── user.entity.ts
│ ├── tests/
│ │ ├── users.integration.test.ts
│ │ └── users.service.unit.test.ts
│ └── users.swagger.yml # (if using separate swagger files)
├── orders/
│ ├── orders.controller.ts
│ ├── orders.service.ts
│ ├── orders.repository.ts
│ └── ...
├── shared/ # Cross-cutting concerns only
│ ├── middleware/
│ ├── guards/
│ ├── filters/
│ ├── interceptors/
│ └── utils/
└── config/
├── database.config.ts
├── auth.config.ts
└── app.config.ts
Rules:
- A module should be self-contained. Moving or removing a feature module should not break other modules.
- Cross-module communication goes through well-defined interfaces (service interfaces, events), never direct imports of internal classes.
- Shared utilities go in
shared/ only when used by 3+ modules. Otherwise keep them in the feature module.
REST API Design
Resource Naming & HTTP Methods
| Method |
Path |
Purpose |
Success Code |
GET |
/resources |
List with filtering, sorting, pagination |
200 |
GET |
/resources/:id |
Single resource details |
200 |
POST |
/resources |
Create resource |
201 |
PATCH |
/resources/:id |
Partial update |
200 |
PUT |
/resources/:id |
Full replace (use sparingly) |
200 |
DELETE |
/resources/:id |
Remove resource |
204 |
Naming conventions:
- Use plural nouns for resource names:
/users, /orders, /products
- Use kebab-case for multi-word resources:
/order-items
- Nest sub-resources max 1 level deep:
/users/:id/orders (avoid deeper nesting)
- Use query parameters for filtering, not path segments
Standard Error Response Codes
| Code |
Meaning |
400 |
Validation errors (malformed request body, missing fields) |
401 |
Unauthenticated (missing or invalid token) |
403 |
Unauthorized (valid token but insufficient permissions) |
404 |
Resource not found |
409 |
Conflict (e.g. duplicate unique field) |
422 |
Business logic errors (foreign key violation, state conflict) |
500 |
Unexpected server error (never expose stack traces in production) |
Standard Error Response Format
{
"statusCode": 400,
"error": "Bad Request",
"message": "Validation failed",
"details": [
{ "field": "email", "message": "must be a valid email address" }
]
}
DataGrid: Filtering, Sorting & Pagination (TSH Standard)
Every list endpoint returning paginated data MUST follow this schema.
Request Query Parameters
| Parameter |
Format |
Description |
page |
page=1 |
Page number (starting from 1) |
limit |
limit=10 |
Max results per page |
sort[field] |
sort[lastName]=ASC |
Sort by field, direction: ASC or DESC |
filter[field] |
filter[firstName]=John |
Filter by field value |
search |
search=john |
General text search (implementation-specific: LIKE, full-text, etc.) |
Example: GET /users?page=1&limit=10&sort[lastName]=ASC&filter[status]=active&search=john
Filter Behavior
- Same field, multiple values → interpreted as
OR:?filter[firstName]=Ewa&filter[firstName]=Adam
→ WHERE (firstName = 'Ewa' OR firstName = 'Adam')
- Different fields → interpreted as
AND:?filter[firstName]=Ewa&filter[lastName]=Kowalska
→ WHERE (firstName = 'Ewa' AND lastName = 'Kowalska')
- LIKE search → use URL-encoded
%25 suffix:?filter[lastName]=Now%25
→ WHERE lastName LIKE 'Now%'
Advanced Filter Operators (when applicable)
| Operator |
SQL Equivalent |
Example |
eq |
= |
filter[status][eq]=active |
neq |
<> |
filter[status][neq]=deleted |
lt, lte |
<, <= |
filter[age][lt]=30 |
gt, gte |
>, >= |
filter[age][gte]=18 |
include |
LIKE %val% |
filter[name][include]=john |
in |
IN (...) |
filter[status][in]=active,pending |
Response Format (Mandatory)
{
"meta": {
"pagination": {
"page": 1,
"limit": 10,
"total": 57,
"totalPages": 6
},
"filter": {
"status": "active"
},
"sort": {
"lastName": "ASC"
},
"search": "john"
},
"data": [{ "..." }]
}
Rules:
meta always reflects the actual applied parameters back to the client.
- Invalid filter/sort field names are silently ignored (no error thrown), but not applied.
- Default
limit should be defined in app configuration (e.g. 20 or 50).
- Maximum
limit should be capped to prevent abuse (e.g. 100 or 250).
Authentication & Authorization
JWT-Based Authentication
- Use JSON Web Tokens (JWT) for stateless authentication.
- Token is passed in the
Authorization header: Bearer <token>.
- Validate the token signature, expiration (
exp), and issuer (iss) on every protected request.
- Store secrets/keys in environment variables, never in code.
- Use short-lived access tokens (15-60 minutes) with refresh token rotation where appropriate.
Current User Endpoint
- Expose a
GET /me endpoint that returns the profile of the currently authenticated user.
- This endpoint should extract the user identity from the JWT (e.g.
sub claim) and return the full user profile.
- Do not accept user ID as a parameter — always derive from the token.
GET /me
Authorization: Bearer <token>
→ 200 { "id": "...", "email": "...", "roles": [...] }
Authorization
- Implement role-based access control (RBAC) or attribute-based access control (ABAC) depending on project complexity.
- Authorization checks happen via middleware/guards before reaching the controller action.
- Always validate that the authenticated user has permission to access/modify the specific resource (not just the endpoint).
Dependency Injection
- Always use a DI container. Register services, repositories, and infrastructure in a central container/module.
- Inject dependencies via constructor injection.
- Depend on interfaces/abstractions, not concrete implementations.
- DI enables testability: in tests, swap real implementations with mocks/stubs.
See the technology-specific references below for recommended DI frameworks per language.
Database Handling
ORM & Repository Pattern
- Use the project's ORM for all database operations (TypeORM, MikroORM, Doctrine, Entity Framework, Hibernate, GORM, etc.).
- Implement the Repository Pattern: all database queries go through repository classes, never directly from services or controllers.
- Repositories return domain entities/models, not raw database rows.
- Use transactions for operations that modify multiple tables/records.
Migrations
- All database schema changes go through migration files. Never modify the database manually.
- Each migration must have both
up (apply) and down (revert) methods.
- Migrations run automatically on application startup (for containerized apps) or via a dedicated migration command/lambda (for serverless).
- Never modify an existing migration that has been deployed. Create a new migration instead.
- Name migrations descriptively:
2025-02-08-add-status-column-to-orders.
Seeding
- Provide seed data for development, test, and staging environments only.
- Never seed production or UAT environments with test data.
- Seeds should be idempotent — running them multiple times produces the same result.
- Separate seed files by domain (e.g.
seed-users.ts, seed-products.ts).
Database Best Practices
- Use
UUID or ULID for primary keys where appropriate (better for distributed systems).
- Define proper indexes for foreign keys, frequently queried columns, and unique constraints.
- Use snake_case naming for tables and columns (e.g.
order_items, created_at).
- Always define
created_at and updated_at timestamps.
- Use soft deletes (
deleted_at) when business rules require record retention.
External Service Adapters (Third-Party Clients)
When integrating with external APIs, always create a dedicated client/adapter class.
Pattern
src/
├── integrations/
│ ├── payment-gateway/
│ │ ├── payment-gateway.client.ts # HTTP calls, request/response mapping
│ │ ├── payment-gateway.types.ts # External API types/interfaces
│ │ └── payment-gateway.module.ts # DI registration
│ ├── email-provider/
│ │ ├── email-provider.client.ts
│ │ └── ...
Rules
- Isolate all HTTP communication with external services into a client class. Never call HTTP clients (Axios, fetch, HttpClient) directly from services or controllers.
- Map external types to internal domain types at the adapter boundary. The rest of the application should not know about the external API's data format.
- Store configuration (API URLs, keys, tokens) in environment variables and inject via config.
- Handle errors gracefully: catch HTTP errors, map them to domain-specific exceptions, and log the details.
- Make clients testable: depend on an interface so the client can be mocked in tests.
- Add retry logic and timeouts for resilience. Use circuit breaker patterns for critical integrations.
Testing Strategy
Test Pyramid
| Level |
What to Test |
| Unit Tests |
Pure business logic in services, domain models, utility functions. Mock all external dependencies. |
| Integration Tests |
API endpoints end-to-end (HTTP request → response). Use a real test database. |
| E2E Tests |
Critical user flows across the full stack. |
See the technology-specific references below for recommended testing tools per language.
Integration Tests for Endpoints
- Test every endpoint with valid and invalid inputs.
- Use a dedicated test database (same engine as production, e.g. PostgreSQL).
- Each test should set up its own data (arrange), call the endpoint (act), and verify the response (assert).
- Clean up test data after each test (use transactions or truncation).
- Verify: HTTP status code, response body structure, side effects (database state, events emitted).
describe('POST /users', () => {
it('should create a user and return 201', async () => {
// Arrange
const payload = { email: 'test@example.com', name: 'Test User' };
// Act
const response = await request(app).post('/users').send(payload);
// Assert
expect(response.status).toBe(201);
expect(response.body.data.email).toBe('test@example.com');
});
it('should return 400 for invalid email', async () => {
const response = await request(app).post('/users').send({ email: 'invalid' });
expect(response.status).toBe(400);
});
});
Unit Tests for Business Logic
- Test services and domain models in isolation.
- Mock repositories, external clients, and infrastructure.
- Focus on edge cases, error paths, and business rules.
- Keep unit tests fast — no database, no network, no filesystem.
- Use descriptive test names:
should throw InsufficientFundsError when balance is below transfer amount.
Testing Rules
- Every new endpoint or business rule must have tests before merging.
- Aim for meaningful coverage of critical paths, not arbitrary percentage targets.
- Integration tests are the primary quality gate for API behavior.
- Unit tests are the primary quality gate for business logic.
- Mock external services (payment gateways, email providers) — never call real external APIs in tests.
API Documentation
Swagger / OpenAPI
- Every API must be documented using OpenAPI/Swagger specification.
- Prefer auto-generated docs from code annotations/decorators when the framework supports it.
- If auto-generation is not available, maintain a separate
swagger.yml file split by domain.
- Serve documentation at
/api-docs endpoint.
- Document: request/response schemas, query parameters, authentication requirements, error responses, and example values.
- Keep documentation in sync with the actual API — stale docs are worse than no docs.
See the technology-specific references below for recommended Swagger tooling per language.
Docker & Local Development
Docker Setup
- Every project must include a
Dockerfile and docker-compose.yml for local development.
- The
docker-compose.yml should include all required services: app, database (PostgreSQL), cache (Redis), mail catcher (Mailhog), etc.
- Use
docker-compose.override.yml for developer-specific customizations (additional ports, volumes, debug settings).
- Application should be fully runnable with a single
docker-compose up command.
Dockerfile Best Practices
- Use multi-stage builds to keep images small.
- Pin base image versions (e.g.
node:20-alpine, php:8.3-fpm-alpine, mcr.microsoft.com/dotnet/aspnet:8.0).
- Install only production dependencies in the final stage.
- Use
.dockerignore to exclude build artifacts, test files, etc.
- Run as a non-root user in the container.
Health Check
Every application must expose a GET /health endpoint:
- Placed before all middleware and auth guards.
- Publicly accessible (no authentication required).
- Returns
200 with status information.
{
"status": "ok"
}
For more thorough health checks, optionally verify database connectivity and critical service availability.
Logging & Observability
Structured Logging
- Use a structured logger — never
console.log or print in production.
- Log in JSON format for machine parseability.
- Include contextual fields in every log entry:
timestamp, level, requestId/correlationId, userId (if authenticated), service.
See the technology-specific references below for recommended logging libraries per language.
Log Levels
| Level |
When to Use |
error |
Unexpected failures, unhandled exceptions, critical issues |
warn |
Recoverable issues, deprecation notices, approaching limits |
info |
Significant business events: user created, order placed, payment processed |
debug |
Detailed diagnostic information (disabled in production) |
What to Log
- Always log: incoming requests (method, path, status code, duration), authentication failures, authorization failures, external service calls (URL, status, duration), errors with stack traces.
- Never log: passwords, tokens, API keys, credit card numbers, PII (personally identifiable information) unless encrypted or masked.
Request Logging
- Log every HTTP request with: method, path, status code, response time, and correlation/request ID.
- Use middleware (Morgan, express request logger, or framework equivalent) for automatic request logging.
- Propagate a
correlationId / requestId header through the entire request lifecycle for tracing.
Scalability & Security
Scalability
- Design for horizontal scaling: no in-memory state, no sticky sessions.
- Never store temporary data in application memory — use Redis or an external cache.
- Use message queues (SQS, RabbitMQ, Bull) for async operations (email sending, PDF generation, data processing).
- Use database connection pooling.
- Apply rate limiting on public endpoints.
- Implement pagination on all list endpoints (never return unbounded result sets).
Security (OWASP TOP 10)
- Input validation: Validate and sanitize all user input at the API boundary (request body, query params, headers).
- SQL Injection: Always use parameterized queries / ORM. Never concatenate user input into SQL.
- Authentication: Use short-lived JWTs, validate signatures, handle token expiration.
- Authorization: Enforce at every endpoint. Check resource ownership, not just role membership.
- Sensitive data: Never expose stack traces, internal paths, or database details in error responses.
- CORS: Configure explicitly — never use
* in production.
- Security headers: Use framework-appropriate middleware to set security headers (CSP, X-Frame-Options, etc.).
- Dependencies: Regularly audit and update dependencies. Use tools like
npm audit, Snyk, or Dependabot.
- Rate limiting: Apply on authentication and public endpoints.
- Secrets: Store in environment variables or a secrets manager. Never commit to source control.
Configuration & Environment
- Use
.env files for local development with a .env.dist (or .env.example) template committed to the repo.
- Validate all configuration on application startup (using Joi, Zod, class-validator, or equivalent). Fail fast on missing required config.
- Group configuration by concern:
database, auth, cache, externalServices.
- Never hardcode environment-specific values. Everything must come from environment variables.
Implementation Procedure
When implementing a new backend feature, follow this workflow:
Implementation progress:
- [ ] Step 1: Understand the requirements
- [ ] Step 2: Design the data model
- [ ] Step 3: Create migration(s)
- [ ] Step 4: Implement the domain layer (entities, services, repositories)
- [ ] Step 5: Implement the API layer (controllers, DTOs, validation)
- [ ] Step 6: Add authentication/authorization guards
- [ ] Step 7: Write integration tests for endpoints
- [ ] Step 8: Write unit tests for business logic
- [ ] Step 9: Document the API (Swagger)
- [ ] Step 10: Verify logging and error handling
Step 1: Understand the requirements
Read the task description, acceptance criteria, and any research documents. Clarify ambiguities before starting.
Step 2: Design the data model
Define entities, relationships, indexes, and constraints. Review with the team if the model is non-trivial.
Step 3: Create migration(s)
Generate migration files for all schema changes. Ensure both up and down are implemented. Run and verify locally.
Step 4: Implement the domain layer
Create entity classes, repository interfaces and implementations, and service classes with business logic. Follow vertical slice structure.
Step 5: Implement the API layer
Create controllers with proper HTTP methods. Define DTOs for request/response. Add input validation. Follow the DataGrid standard for list endpoints.
Step 6: Add authentication/authorization guards
Apply JWT validation middleware. Add role/permission checks as needed. Implement resource-level authorization.
Step 7: Write integration tests
Test every endpoint: success and error paths. Verify response structure, status codes, and database side effects.
Step 8: Write unit tests
Test business logic in services. Mock dependencies. Cover edge cases and error scenarios.
Step 9: Document the API
Add or update Swagger/OpenAPI documentation. Verify docs render correctly at /api-docs.
Step 10: Verify logging and error handling
Ensure requests are logged, errors produce structured log entries, and no sensitive data leaks in logs or responses.
Technology-Specific Patterns
The patterns above are language-agnostic. For technology-specific implementation guidance, load the appropriate reference:
- Node.js: See
./references/nodejs-patterns.md — NestJS/Express DI, Jest/Supertest testing, Pino/Winston logging, TypeORM/Prisma ORM, Swagger integration.
- PHP: See
./references/php-patterns.md — Symfony/Laravel DI, PHPUnit testing, Monolog logging, Doctrine/Eloquent ORM, Swagger integration.
- dotNET: See
./references/dotnet-patterns.md — built-in DI, xUnit testing, Serilog logging, Entity Framework ORM, Swashbuckle Swagger.
- Java: See
./references/java-spring-boot-patterns.md — Spring IoC, JUnit/REST Assured testing, SLF4J/Logback logging, Hibernate ORM, springdoc-openapi, Spring Cloud Stream async messaging.
- Go: See
./references/go-patterns.md — Wire/Fx DI, Go testing, Zap logging, GORM ORM, swaggo Swagger.
Connected Skills
tsh-sql-and-database-understanding — for database schema design, query optimization, and ORM integration
tsh-technical-context-discovering — for understanding project conventions before implementing
tsh-implementation-gap-analysing — for verifying current state before making changes
tsh-codebase-analysing — for understanding existing architecture and patterns
tsh-implementing-ci-cd — for CI/CD pipeline setup and deployment strategies
tsh-implementing-observability — for logging, monitoring, and distributed tracing
tsh-managing-secrets — for secure credential storage and rotation
tsh-e2e-testing — for end-to-end testing with Playwright
Connected Skills
technical-context-discovery — for establishing project conventions before implementing
architecture-design — for designing complex feature architectures
code-review — for validating implemented code against these standards
e2e-testing — for E2E test patterns when full-stack testing is needed
1---2name: tsh-implementing-backend3description: Backend service implementation patterns, standards, and procedures. Use for building REST/GraphQL APIs, implementing CRUD endpoints, database handling, authentication, testing strategies, external service integrations, filtering/pagination (DataGrid), logging, Docker setup, and modular architecture. Applies to Node.js, PHP, .NET, Java, and Go backends.4---56# Implementing Backend78Provides patterns for building backend API services with modular architecture, structured testing, and production-ready infrastructure following TSH best practices.910## When to Use1112- Building new REST or GraphQL API endpoints13- Implementing CRUD operations with filtering, sorting, and pagination14- Setting up authentication and authorization (JWT)15- Integrating with external/third-party services16- Writing integration tests for endpoints or unit tests for business logic17- Configuring database migrations, seeding, or repository patterns18- Setting up Docker and docker-compose for local development19- Implementing logging and observability20- Documenting APIs with Swagger/OpenAPI21- Designing modular architecture with vertical slices2223## Guiding Principles2425| Principle | Application |26|---|---|27| **SRP** | Each class/module has one reason to change. Controllers handle HTTP, services handle business logic, repositories handle data access. |28| **DRY** | Extract shared logic into reusable services or utilities. Do not duplicate validation, mapping, or query logic. |29| **KISS** | Prefer simple, readable solutions. Avoid over-engineering. Do not add abstractions until they are needed. |30| **YAGNI** | Do not build features or infrastructure "just in case". Implement what is needed now. |31| **Pragmatism** | Follow patterns when they add value. Break rules when strict adherence creates unnecessary complexity. Document the reasoning. |3233## Architecture: Vertical Slice / Modular Structure3435Organize code by **domain/feature**, not by technical layer. All artifacts related to a domain live in the same directory.3637```38src/39├── users/40│ ├── users.controller.ts # HTTP layer (routes, request/response)41│ ├── users.service.ts # Business logic42│ ├── users.repository.ts # Data access43│ ├── users.module.ts # Module registration / DI wiring44│ ├── dto/45│ │ ├── create-user.dto.ts46│ │ └── update-user.dto.ts47│ ├── entities/48│ │ └── user.entity.ts49│ ├── tests/50│ │ ├── users.integration.test.ts51│ │ └── users.service.unit.test.ts52│ └── users.swagger.yml # (if using separate swagger files)53├── orders/54│ ├── orders.controller.ts55│ ├── orders.service.ts56│ ├── orders.repository.ts57│ └── ...58├── shared/ # Cross-cutting concerns only59│ ├── middleware/60│ ├── guards/61│ ├── filters/62│ ├── interceptors/63│ └── utils/64└── config/65 ├── database.config.ts66 ├── auth.config.ts67 └── app.config.ts68```6970**Rules:**71- A module should be self-contained. Moving or removing a feature module should not break other modules.72- Cross-module communication goes through well-defined interfaces (service interfaces, events), never direct imports of internal classes.73- Shared utilities go in `shared/` only when used by 3+ modules. Otherwise keep them in the feature module.7475## REST API Design7677### Resource Naming & HTTP Methods7879| Method | Path | Purpose | Success Code |80|---|---|---|---|81| `GET` | `/resources` | List with filtering, sorting, pagination | `200` |82| `GET` | `/resources/:id` | Single resource details | `200` |83| `POST` | `/resources` | Create resource | `201` |84| `PATCH` | `/resources/:id` | Partial update | `200` |85| `PUT` | `/resources/:id` | Full replace (use sparingly) | `200` |86| `DELETE` | `/resources/:id` | Remove resource | `204` |8788**Naming conventions:**89- Use plural nouns for resource names: `/users`, `/orders`, `/products`90- Use kebab-case for multi-word resources: `/order-items`91- Nest sub-resources max 1 level deep: `/users/:id/orders` (avoid deeper nesting)92- Use query parameters for filtering, not path segments9394### Standard Error Response Codes9596| Code | Meaning |97|---|---|98| `400` | Validation errors (malformed request body, missing fields) |99| `401` | Unauthenticated (missing or invalid token) |100| `403` | Unauthorized (valid token but insufficient permissions) |101| `404` | Resource not found |102| `409` | Conflict (e.g. duplicate unique field) |103| `422` | Business logic errors (foreign key violation, state conflict) |104| `500` | Unexpected server error (never expose stack traces in production) |105106### Standard Error Response Format107108```json109{110 "statusCode": 400,111 "error": "Bad Request",112 "message": "Validation failed",113 "details": [114 { "field": "email", "message": "must be a valid email address" }115 ]116}117```118119## DataGrid: Filtering, Sorting & Pagination (TSH Standard)120121Every list endpoint returning paginated data **MUST** follow this schema.122123### Request Query Parameters124125| Parameter | Format | Description |126|---|---|---|127| `page` | `page=1` | Page number (starting from 1) |128| `limit` | `limit=10` | Max results per page |129| `sort[field]` | `sort[lastName]=ASC` | Sort by field, direction: `ASC` or `DESC` |130| `filter[field]` | `filter[firstName]=John` | Filter by field value |131| `search` | `search=john` | General text search (implementation-specific: LIKE, full-text, etc.) |132133**Example:** `GET /users?page=1&limit=10&sort[lastName]=ASC&filter[status]=active&search=john`134135### Filter Behavior136137- **Same field, multiple values** → interpreted as `OR`:138 ```139 ?filter[firstName]=Ewa&filter[firstName]=Adam140 → WHERE (firstName = 'Ewa' OR firstName = 'Adam')141 ```142- **Different fields** → interpreted as `AND`:143 ```144 ?filter[firstName]=Ewa&filter[lastName]=Kowalska145 → WHERE (firstName = 'Ewa' AND lastName = 'Kowalska')146 ```147- **LIKE search** → use URL-encoded `%25` suffix:148 ```149 ?filter[lastName]=Now%25150 → WHERE lastName LIKE 'Now%'151 ```152153### Advanced Filter Operators (when applicable)154155| Operator | SQL Equivalent | Example |156|---|---|---|157| `eq` | `=` | `filter[status][eq]=active` |158| `neq` | `<>` | `filter[status][neq]=deleted` |159| `lt`, `lte` | `<`, `<=` | `filter[age][lt]=30` |160| `gt`, `gte` | `>`, `>=` | `filter[age][gte]=18` |161| `include` | `LIKE %val%` | `filter[name][include]=john` |162| `in` | `IN (...)` | `filter[status][in]=active,pending` |163164### Response Format (Mandatory)165166```json167{168 "meta": {169 "pagination": {170 "page": 1,171 "limit": 10,172 "total": 57,173 "totalPages": 6174 },175 "filter": {176 "status": "active"177 },178 "sort": {179 "lastName": "ASC"180 },181 "search": "john"182 },183 "data": [{ "..." }]184}185```186187**Rules:**188- `meta` always reflects the actual applied parameters back to the client.189- Invalid filter/sort field names are **silently ignored** (no error thrown), but not applied.190- Default `limit` should be defined in app configuration (e.g. 20 or 50).191- Maximum `limit` should be capped to prevent abuse (e.g. 100 or 250).192193## Authentication & Authorization194195### JWT-Based Authentication196197- Use **JSON Web Tokens (JWT)** for stateless authentication.198- Token is passed in the `Authorization` header: `Bearer <token>`.199- Validate the token signature, expiration (`exp`), and issuer (`iss`) on every protected request.200- Store secrets/keys in environment variables, never in code.201- Use short-lived access tokens (15-60 minutes) with refresh token rotation where appropriate.202203### Current User Endpoint204205- Expose a `GET /me` endpoint that returns the profile of the currently authenticated user.206- This endpoint should extract the user identity from the JWT (e.g. `sub` claim) and return the full user profile.207- Do **not** accept user ID as a parameter — always derive from the token.208209```210GET /me211Authorization: Bearer <token>212→ 200 { "id": "...", "email": "...", "roles": [...] }213```214215### Authorization216217- Implement role-based access control (RBAC) or attribute-based access control (ABAC) depending on project complexity.218- Authorization checks happen via middleware/guards **before** reaching the controller action.219- Always validate that the authenticated user has permission to access/modify the specific resource (not just the endpoint).220221## Dependency Injection222223- **Always use a DI container.** Register services, repositories, and infrastructure in a central container/module.224- Inject dependencies via constructor injection.225- Depend on **interfaces/abstractions**, not concrete implementations.226- DI enables testability: in tests, swap real implementations with mocks/stubs.227228See the technology-specific references below for recommended DI frameworks per language.229230## Database Handling231232### ORM & Repository Pattern233234- Use the project's ORM for all database operations (TypeORM, MikroORM, Doctrine, Entity Framework, Hibernate, GORM, etc.).235- Implement the **Repository Pattern**: all database queries go through repository classes, never directly from services or controllers.236- Repositories return domain entities/models, not raw database rows.237- Use **transactions** for operations that modify multiple tables/records.238239### Migrations240241- **All** database schema changes go through migration files. Never modify the database manually.242- Each migration must have both `up` (apply) and `down` (revert) methods.243- Migrations run automatically on application startup (for containerized apps) or via a dedicated migration command/lambda (for serverless).244- Never modify an existing migration that has been deployed. Create a new migration instead.245- Name migrations descriptively: `2025-02-08-add-status-column-to-orders`.246247### Seeding248249- Provide seed data for **development, test, and staging** environments only.250- **Never** seed production or UAT environments with test data.251- Seeds should be idempotent — running them multiple times produces the same result.252- Separate seed files by domain (e.g. `seed-users.ts`, `seed-products.ts`).253254### Database Best Practices255256- Use `UUID` or `ULID` for primary keys where appropriate (better for distributed systems).257- Define proper indexes for foreign keys, frequently queried columns, and unique constraints.258- Use snake_case naming for tables and columns (e.g. `order_items`, `created_at`).259- Always define `created_at` and `updated_at` timestamps.260- Use soft deletes (`deleted_at`) when business rules require record retention.261262## External Service Adapters (Third-Party Clients)263264When integrating with external APIs, **always create a dedicated client/adapter class**.265266### Pattern267268```269src/270├── integrations/271│ ├── payment-gateway/272│ │ ├── payment-gateway.client.ts # HTTP calls, request/response mapping273│ │ ├── payment-gateway.types.ts # External API types/interfaces274│ │ └── payment-gateway.module.ts # DI registration275│ ├── email-provider/276│ │ ├── email-provider.client.ts277│ │ └── ...278```279280### Rules2812821. **Isolate all HTTP communication** with external services into a client class. Never call HTTP clients (Axios, fetch, HttpClient) directly from services or controllers.2832. **Map external types** to internal domain types at the adapter boundary. The rest of the application should not know about the external API's data format.2843. **Store configuration** (API URLs, keys, tokens) in environment variables and inject via config.2854. **Handle errors gracefully**: catch HTTP errors, map them to domain-specific exceptions, and log the details.2865. **Make clients testable**: depend on an interface so the client can be mocked in tests.2876. **Add retry logic and timeouts** for resilience. Use circuit breaker patterns for critical integrations.288289## Testing Strategy290291### Test Pyramid292293| Level | What to Test |294|---|---|295| **Unit Tests** | Pure business logic in services, domain models, utility functions. Mock all external dependencies. |296| **Integration Tests** | API endpoints end-to-end (HTTP request → response). Use a real test database. |297| **E2E Tests** | Critical user flows across the full stack. |298299See the technology-specific references below for recommended testing tools per language.300301### Integration Tests for Endpoints302303- Test every endpoint with valid and invalid inputs.304- Use a dedicated **test database** (same engine as production, e.g. PostgreSQL).305- Each test should set up its own data (arrange), call the endpoint (act), and verify the response (assert).306- Clean up test data after each test (use transactions or truncation).307- Verify: HTTP status code, response body structure, side effects (database state, events emitted).308309```310describe('POST /users', () => {311 it('should create a user and return 201', async () => {312 // Arrange313 const payload = { email: 'test@example.com', name: 'Test User' };314315 // Act316 const response = await request(app).post('/users').send(payload);317318 // Assert319 expect(response.status).toBe(201);320 expect(response.body.data.email).toBe('test@example.com');321 });322323 it('should return 400 for invalid email', async () => {324 const response = await request(app).post('/users').send({ email: 'invalid' });325 expect(response.status).toBe(400);326 });327});328```329330### Unit Tests for Business Logic331332- Test services and domain models in isolation.333- Mock repositories, external clients, and infrastructure.334- Focus on edge cases, error paths, and business rules.335- Keep unit tests fast — no database, no network, no filesystem.336- Use descriptive test names: `should throw InsufficientFundsError when balance is below transfer amount`.337338### Testing Rules339340- Every new endpoint or business rule **must** have tests before merging.341- Aim for meaningful coverage of critical paths, not arbitrary percentage targets.342- Integration tests are the primary quality gate for API behavior.343- Unit tests are the primary quality gate for business logic.344- Mock external services (payment gateways, email providers) — never call real external APIs in tests.345346## API Documentation347348### Swagger / OpenAPI349350- **Every API must be documented** using OpenAPI/Swagger specification.351- Prefer auto-generated docs from code annotations/decorators when the framework supports it.352- If auto-generation is not available, maintain a separate `swagger.yml` file split by domain.353- Serve documentation at `/api-docs` endpoint.354- Document: request/response schemas, query parameters, authentication requirements, error responses, and example values.355- Keep documentation in sync with the actual API — stale docs are worse than no docs.356357See the technology-specific references below for recommended Swagger tooling per language.358359## Docker & Local Development360361### Docker Setup362363- Every project **must** include a `Dockerfile` and `docker-compose.yml` for local development.364- The `docker-compose.yml` should include all required services: app, database (PostgreSQL), cache (Redis), mail catcher (Mailhog), etc.365- Use `docker-compose.override.yml` for developer-specific customizations (additional ports, volumes, debug settings).366- Application should be fully runnable with a single `docker-compose up` command.367368### Dockerfile Best Practices369370- Use multi-stage builds to keep images small.371- Pin base image versions (e.g. `node:20-alpine`, `php:8.3-fpm-alpine`, `mcr.microsoft.com/dotnet/aspnet:8.0`).372- Install only production dependencies in the final stage.373- Use `.dockerignore` to exclude build artifacts, test files, etc.374- Run as a non-root user in the container.375376## Health Check377378Every application **must** expose a `GET /health` endpoint:379- Placed **before** all middleware and auth guards.380- Publicly accessible (no authentication required).381- Returns `200` with status information.382383```json384{385 "status": "ok"386}387```388389For more thorough health checks, optionally verify database connectivity and critical service availability.390391## Logging & Observability392393### Structured Logging394395- Use a **structured logger** — never `console.log` or `print` in production.396- Log in **JSON format** for machine parseability.397- Include contextual fields in every log entry: `timestamp`, `level`, `requestId`/`correlationId`, `userId` (if authenticated), `service`.398399See the technology-specific references below for recommended logging libraries per language.400401### Log Levels402403| Level | When to Use |404|---|---|405| `error` | Unexpected failures, unhandled exceptions, critical issues |406| `warn` | Recoverable issues, deprecation notices, approaching limits |407| `info` | Significant business events: user created, order placed, payment processed |408| `debug` | Detailed diagnostic information (disabled in production) |409410### What to Log411412- **Always log**: incoming requests (method, path, status code, duration), authentication failures, authorization failures, external service calls (URL, status, duration), errors with stack traces.413- **Never log**: passwords, tokens, API keys, credit card numbers, PII (personally identifiable information) unless encrypted or masked.414415### Request Logging416417- Log every HTTP request with: method, path, status code, response time, and correlation/request ID.418- Use middleware (Morgan, express request logger, or framework equivalent) for automatic request logging.419- Propagate a `correlationId` / `requestId` header through the entire request lifecycle for tracing.420421## Scalability & Security422423### Scalability424425- Design for **horizontal scaling**: no in-memory state, no sticky sessions.426- Never store temporary data in application memory — use Redis or an external cache.427- Use message queues (SQS, RabbitMQ, Bull) for async operations (email sending, PDF generation, data processing).428- Use database connection pooling.429- Apply rate limiting on public endpoints.430- Implement pagination on all list endpoints (never return unbounded result sets).431432### Security (OWASP TOP 10)433434- **Input validation**: Validate and sanitize all user input at the API boundary (request body, query params, headers).435- **SQL Injection**: Always use parameterized queries / ORM. Never concatenate user input into SQL.436- **Authentication**: Use short-lived JWTs, validate signatures, handle token expiration.437- **Authorization**: Enforce at every endpoint. Check resource ownership, not just role membership.438- **Sensitive data**: Never expose stack traces, internal paths, or database details in error responses.439- **CORS**: Configure explicitly — never use `*` in production.440- **Security headers**: Use framework-appropriate middleware to set security headers (CSP, X-Frame-Options, etc.).441- **Dependencies**: Regularly audit and update dependencies. Use tools like `npm audit`, Snyk, or Dependabot.442- **Rate limiting**: Apply on authentication and public endpoints.443- **Secrets**: Store in environment variables or a secrets manager. Never commit to source control.444445## Configuration & Environment446447- Use `.env` files for local development with a `.env.dist` (or `.env.example`) template committed to the repo.448- **Validate all configuration** on application startup (using Joi, Zod, class-validator, or equivalent). Fail fast on missing required config.449- Group configuration by concern: `database`, `auth`, `cache`, `externalServices`.450- Never hardcode environment-specific values. Everything must come from environment variables.451452## Implementation Procedure453454When implementing a new backend feature, follow this workflow:455456```457Implementation progress:458- [ ] Step 1: Understand the requirements459- [ ] Step 2: Design the data model460- [ ] Step 3: Create migration(s)461- [ ] Step 4: Implement the domain layer (entities, services, repositories)462- [ ] Step 5: Implement the API layer (controllers, DTOs, validation)463- [ ] Step 6: Add authentication/authorization guards464- [ ] Step 7: Write integration tests for endpoints465- [ ] Step 8: Write unit tests for business logic466- [ ] Step 9: Document the API (Swagger)467- [ ] Step 10: Verify logging and error handling468```469470**Step 1: Understand the requirements**471Read the task description, acceptance criteria, and any research documents. Clarify ambiguities before starting.472473**Step 2: Design the data model**474Define entities, relationships, indexes, and constraints. Review with the team if the model is non-trivial.475476**Step 3: Create migration(s)**477Generate migration files for all schema changes. Ensure both `up` and `down` are implemented. Run and verify locally.478479**Step 4: Implement the domain layer**480Create entity classes, repository interfaces and implementations, and service classes with business logic. Follow vertical slice structure.481482**Step 5: Implement the API layer**483Create controllers with proper HTTP methods. Define DTOs for request/response. Add input validation. Follow the DataGrid standard for list endpoints.484485**Step 6: Add authentication/authorization guards**486Apply JWT validation middleware. Add role/permission checks as needed. Implement resource-level authorization.487488**Step 7: Write integration tests**489Test every endpoint: success and error paths. Verify response structure, status codes, and database side effects.490491**Step 8: Write unit tests**492Test business logic in services. Mock dependencies. Cover edge cases and error scenarios.493494**Step 9: Document the API**495Add or update Swagger/OpenAPI documentation. Verify docs render correctly at `/api-docs`.496497**Step 10: Verify logging and error handling**498Ensure requests are logged, errors produce structured log entries, and no sensitive data leaks in logs or responses.499500## Technology-Specific Patterns501502The patterns above are language-agnostic. For technology-specific implementation guidance, load the appropriate reference:503504- **Node.js**: See `./references/nodejs-patterns.md` — NestJS/Express DI, Jest/Supertest testing, Pino/Winston logging, TypeORM/Prisma ORM, Swagger integration.505- **PHP**: See `./references/php-patterns.md` — Symfony/Laravel DI, PHPUnit testing, Monolog logging, Doctrine/Eloquent ORM, Swagger integration.506- **dotNET**: See `./references/dotnet-patterns.md` — built-in DI, xUnit testing, Serilog logging, Entity Framework ORM, Swashbuckle Swagger.507- **Java**: See `./references/java-spring-boot-patterns.md` — Spring IoC, JUnit/REST Assured testing, SLF4J/Logback logging, Hibernate ORM, springdoc-openapi, Spring Cloud Stream async messaging.508- **Go**: See `./references/go-patterns.md` — Wire/Fx DI, Go testing, Zap logging, GORM ORM, swaggo Swagger.509510## Connected Skills511512- `tsh-sql-and-database-understanding` — for database schema design, query optimization, and ORM integration513- `tsh-technical-context-discovering` — for understanding project conventions before implementing514- `tsh-implementation-gap-analysing` — for verifying current state before making changes515- `tsh-codebase-analysing` — for understanding existing architecture and patterns516- `tsh-implementing-ci-cd` — for CI/CD pipeline setup and deployment strategies517- `tsh-implementing-observability` — for logging, monitoring, and distributed tracing518- `tsh-managing-secrets` — for secure credential storage and rotation519- `tsh-e2e-testing` — for end-to-end testing with Playwright520521## Connected Skills522523- `technical-context-discovery` — for establishing project conventions before implementing524- `architecture-design` — for designing complex feature architectures525- `code-review` — for validating implemented code against these standards526- `e2e-testing` — for E2E test patterns when full-stack testing is needed