.github/skills/tsh-implementing-backend/SKILL.md
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.
npx skillsauth add thesoftwarehouse/copilot-collections tsh-implementing-backendInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Provides patterns for building backend API services with modular architecture, structured testing, and production-ready infrastructure following TSH best practices.
| 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. |
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:
shared/ only when used by 3+ modules. Otherwise keep them in the feature module.| 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:
/users, /orders, /products/order-items/users/:id/orders (avoid deeper nesting)| 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) |
{
"statusCode": 400,
"error": "Bad Request",
"message": "Validation failed",
"details": [
{ "field": "email", "message": "must be a valid email address" }
]
}
Every list endpoint returning paginated data MUST follow this schema.
| 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
OR:
?filter[firstName]=Ewa&filter[firstName]=Adam
→ WHERE (firstName = 'Ewa' OR firstName = 'Adam')
AND:
?filter[firstName]=Ewa&filter[lastName]=Kowalska
→ WHERE (firstName = 'Ewa' AND lastName = 'Kowalska')
%25 suffix:
?filter[lastName]=Now%25
→ WHERE lastName LIKE 'Now%'
| 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 |
{
"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.limit should be defined in app configuration (e.g. 20 or 50).limit should be capped to prevent abuse (e.g. 100 or 250).Authorization header: Bearer <token>.exp), and issuer (iss) on every protected request.GET /me endpoint that returns the profile of the currently authenticated user.sub claim) and return the full user profile.GET /me
Authorization: Bearer <token>
→ 200 { "id": "...", "email": "...", "roles": [...] }
See the technology-specific references below for recommended DI frameworks per language.
up (apply) and down (revert) methods.2025-02-08-add-status-column-to-orders.seed-users.ts, seed-products.ts).UUID or ULID for primary keys where appropriate (better for distributed systems).order_items, created_at).created_at and updated_at timestamps.deleted_at) when business rules require record retention.When integrating with external APIs, always create a dedicated client/adapter class.
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
│ │ └── ...
| 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.
describe('POST /users', () => {
it('should create a user and return 201', async () => {
// Arrange
const payload = { email: '[email protected]', 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('[email protected]');
});
it('should return 400 for invalid email', async () => {
const response = await request(app).post('/users').send({ email: 'invalid' });
expect(response.status).toBe(400);
});
});
should throw InsufficientFundsError when balance is below transfer amount.swagger.yml file split by domain./api-docs endpoint.See the technology-specific references below for recommended Swagger tooling per language.
Dockerfile and docker-compose.yml for local development.docker-compose.yml should include all required services: app, database (PostgreSQL), cache (Redis), mail catcher (Mailhog), etc.docker-compose.override.yml for developer-specific customizations (additional ports, volumes, debug settings).docker-compose up command.node:20-alpine, php:8.3-fpm-alpine, mcr.microsoft.com/dotnet/aspnet:8.0)..dockerignore to exclude build artifacts, test files, etc.Every application must expose a GET /health endpoint:
200 with status information.{
"status": "ok"
}
For more thorough health checks, optionally verify database connectivity and critical service availability.
console.log or print in production.timestamp, level, requestId/correlationId, userId (if authenticated), service.See the technology-specific references below for recommended logging libraries per language.
| 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) |
correlationId / requestId header through the entire request lifecycle for tracing.* in production.npm audit, Snyk, or Dependabot..env files for local development with a .env.dist (or .env.example) template committed to the repo.database, auth, cache, externalServices.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.
The patterns above are language-agnostic. For technology-specific implementation guidance, load the appropriate reference:
./references/nodejs-patterns.md — NestJS/Express DI, Jest/Supertest testing, Pino/Winston logging, TypeORM/Prisma ORM, Swagger integration../references/php-patterns.md — Symfony/Laravel DI, PHPUnit testing, Monolog logging, Doctrine/Eloquent ORM, Swagger integration../references/dotnet-patterns.md — built-in DI, xUnit testing, Serilog logging, Entity Framework ORM, Swashbuckle Swagger../references/java-patterns.md — Spring IoC, JUnit/REST Assured testing, SLF4J/Logback logging, Hibernate ORM, springdoc-openapi../references/go-patterns.md — Wire/Fx DI, Go testing, Zap logging, GORM ORM, swaggo Swagger.tsh-sql-and-database-understanding — for database schema design, query optimization, and ORM integrationtsh-technical-context-discovering — for understanding project conventions before implementingtsh-implementation-gap-analysing — for verifying current state before making changestsh-codebase-analysing — for understanding existing architecture and patternstsh-implementing-ci-cd — for CI/CD pipeline setup and deployment strategiestsh-implementing-observability — for logging, monitoring, and distributed tracingtsh-managing-secrets — for secure credential storage and rotationtsh-e2e-testing — for end-to-end testing with Playwrighttechnical-context-discovery — for establishing project conventions before implementingarchitecture-design — for designing complex feature architecturescode-review — for validating implemented code against these standardse2e-testing — for E2E test patterns when full-stack testing is neededdevelopment
Custom hook and composable patterns — naming, composition, stable return shapes, lifecycle cleanup, and testing strategies. Use when writing reusable logic units (React hooks, Vue composables), refactoring logic into hooks, debugging hook behavior, or reviewing hook implementations.
testing
UI verification criteria, structure checklists, severity definitions, and tolerance rules for comparing implementations against Figma designs. Use for verifying UI matches design, understanding what to check, and determining acceptable differences.
development
Clean raw workshop or meeting transcripts from small talk, filler words, and off-topic tangents. Extract and structure business-relevant content into a standardized format with discussion topics, key decisions, action items, and open questions.
development
Discover and establish technical context before implementing any feature. Prioritize project instructions, existing codebase patterns, and external documentation in that order. Use for any task requiring understanding of project conventions, coding standards, architecture patterns, and established practices before writing code.