Small TypeScript + Express API for a mock infrastructure provisioning workflow to demonstrate clean structure, idempotency, validation, and basic API security.
Install dependencies:
npm installStart locally:
npm run devThe app runs on http://localhost:3001.
Run tests:
npm testBuild Docker image:
docker build -t provision-api .
docker run -p 3001:3001 -e API_KEY=my-secret provision-apiCopy .env.example to .env for local development and replace placeholder values as needed.
All endpoints require x-api-key, except /api/health.
Authentication is intentionally lightweight in this repo. I used a single x-api-key header check rather than Basic Auth to keep the implementation small and focused on the core workflow. The default fallback key in code (dev-placeholder-api-key) is only a development placeholder, not a real secret. In a production setting, the key would come from environment configuration or secret-management tooling rather than a hardcoded fallback.
Creates a provisioning request.
Example body:
{
"idempotencyKey": "deploy-run-42",
"requestor": "engineer@example.com",
"environment": "prod",
"workloadName": "payments-api",
"components": ["compute", "key-vault"]
}Responses:
201created200repeated request with same idempotency key and same payload400invalid request or failed business rule401missing or invalid API key409same idempotency key used with different payload
Returns a provisioning request by ID.
Responses:
200found404not found
Returns a simple health response.
workloadNamemust be kebab-caseprodrequests must include"key-vault"incomponents
The code structure is split into intentionally lightweight layers:
domain/: entity definitions and domain rule validationapplication/: use-case logic and idempotency handlinginfrastructure/: in-memory repositoryhttp/: routes, handlers, validation middleware, auth middleware, error middlewareerrors/: common error types and parsing/validation helpers
The code is intentionally lightweight, with clear boundaries for testability and replaceable persistence.
The repository is in-memory for simplicity, but sits behind a persistence boundary so it can be swapped for a database-backed implementation later.
Internally, it uses two Maps to model two lookup paths:
- one primary map stores the actual provisioning request by
requestId - one secondary map stores
idempotencyKey -> requestId
This avoids duplicating the full request object under two different keys and mirrors the idea of a primary record store plus a secondary index.
Repeated requests are matched by idempotencyKey. If the payload is identical, the original request is returned. If the payload differs, the API returns 409.
Only caller-supplied fields are compared:
requestorenvironmentworkloadNamecomponents
Generated fields such as requestId, status, and createdAt are excluded from comparison.
Validation is intentionally split into two layers:
- HTTP boundary validation in middleware using Zod
- business rule validation in the domain layer
Request-shape validation includes:
idempotencyKey,requestor, andcomponentsmust not be emptyenvironmentmust be one ofdev,test, orprodworkloadNamemust be between 3 and 30 characters
Business rule validation includes:
workloadNamemust be kebab-caseprodrequests must include"key-vault"incomponents