Skip to main content

tsh-implementing-backend

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.

소스 정보

저장소
TheSoftwareHouse/copilot-collections
최근 소스 활동
2026년 5월 5일 11:32
감지된 SKILL.md 언어
영어
스타
284
포크
19

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

파일 탐색기
6 개 파일

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
tsh-implementing-backend
description
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.
user-invocable
false
# 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 ```json { "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) ```json { "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 1. **Isolate all HTTP communication** with external services into a client class. Never call HTTP clients (Axios, fetch, HttpClient) directly from services or controllers. 2. **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. 3. **Store configuration** (API URLs, keys, tokens) in environment variables and inject via config. 4. **Handle errors gracefully**: catch HTTP errors, map them to domain-specific exceptions, and log the details. 5. **Make clients testable**: depend on an interface so the client can be mocked in tests. 6. **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.
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기