NestJS API server powering aid orchestration, verification workflows, on-chain anchoring, and operational tooling for the ChainForge platform.
The backend provides:
- Aid logic and APIs — Package management, claims processing, disbursement workflows
- Verification APIs — Inbox management with approve, reject, and resubmission flows
- On-chain anchoring — Soroban smart contract integration via Stellar RPC
- Queue processing — BullMQ-backed background jobs for async workflows
- Observability — Prometheus metrics, structured logging, Sentry error tracking
| Layer | Technology |
|---|---|
| Framework | NestJS (TypeScript) |
| Database | PostgreSQL via Prisma ORM |
| Queue | BullMQ (Redis-backed) |
| Cache | Redis |
| Auth | JWT + API keys |
| Monitoring | Prometheus, Sentry |
# From the monorepo root
pnpm install
pnpm --filter backend run start:devBy default the server listens on the port specified in your .env file (see .env.example).
cp app/backend/.env.example app/backend/.envEdit .env with your specific values. See .env.example for detailed inline comments and local development defaults.
Local development: The default .env.example values work out of the box:
- Uses local PostgreSQL with default credentials
- Points to Stellar testnet
- Client-side verification (no OpenAI key needed)
- Queues disabled (no Redis needed)
- Full logging and Swagger enabled
Production: Update these critical variables:
NODE_ENV=productionDATABASE_URL— Use secure credentials and connection poolingSTELLAR_RPC_URL— Switch to mainnet if deploying liveJWT_SECRET— Generate withopenssl rand -base64 32CORS_ORIGINS— Set to your actual frontend domain(s)METRICS_ENABLED=true— Enable for monitoringSWAGGER_ENABLED=false— Disable public API docsLOG_LEVEL=info— Reduce log verbosity
pnpm --filter backend prisma:generate
pnpm --filter backend prisma:migratecurl -s http://localhost:3001/healthpnpm --filter backend lint
pnpm --filter backend test
pnpm --filter backend run test:e2eAll environment variables are documented in .env.example with inline comments, examples, and notes on when each is required.
| Variable | Description | Default |
|---|---|---|
| Server configuration | ||
PORT |
Port the NestJS server listens on | 3001 |
NODE_ENV |
Node environment | development |
| Database | ||
DATABASE_URL |
PostgreSQL connection string for Prisma | Required |
| Blockchain (Stellar/Soroban) | ||
STELLAR_RPC_URL |
Stellar RPC endpoint | https://soroban-testnet.stellar.org |
SOROBAN_CONTRACT_ID |
Deployed AidEscrow contract ID | None |
| AI and verification | ||
OPENAI_API_KEY |
OpenAI API key for server-side verification | Empty (disabled) |
VERIFICATION_MODE |
Verification mode | client-side |
| CORS | ||
CORS_ORIGINS |
Comma-separated allowed origins | http://localhost:3000,http://localhost:3001 |
| Queue and cache | ||
REDIS_URL |
Redis connection URL | redis://localhost:6379 |
QUEUE_ENABLED |
Enable background job queues | false |
| Security | ||
JWT_SECRET |
Secret for JWT token signing | Auto-generated |
JWT_EXPIRES_IN |
JWT token expiration time | 7d |
| Rate limiting | ||
API_RATE_LIMIT |
Max requests per minute per IP | 100 |
THROTTLE_TTL |
Rate limit window (milliseconds) | 60000 |
THROTTLE_ENABLED |
Enable request throttling | true |
| Monitoring | ||
METRICS_ENABLED |
Enable Prometheus metrics at /metrics |
false |
LOG_LEVEL |
Logging level | debug |
SENTRY_DSN |
Sentry DSN for error tracking | None |
| Feature flags | ||
SWAGGER_ENABLED |
Enable API docs at /api/docs |
true |
| Problem | Solution |
|---|---|
| Database connection fails | Ensure PostgreSQL is running (pg_isready), verify credentials, check database exists |
| Stellar RPC errors | Verify network connectivity, check testnet vs mainnet, ensure testnet XLM balance |
| OpenAI verification not working | Verify OPENAI_API_KEY, check credits, ensure VERIFICATION_MODE=server-side |
| Queue/Redis errors | Ensure Redis is running (redis-cli ping), verify REDIS_URL |
View pending verifications:
curl -H "Authorization: Bearer $JWT_TOKEN" \
http://localhost:3001/api/v1/verification-inbox?status=pending_reviewApprove a verification:
curl -X POST \
-H "Authorization: Bearer $JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"nextStepMessage": "Verification approved. Proceed to disbursement."}' \
http://localhost:3001/api/v1/verification-inbox/{id}/approveReject a verification:
curl -X POST \
-H "Authorization: Bearer $JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"rejectionReason": "Document appears fraudulent"}' \
http://localhost:3001/api/v1/verification-inbox/{id}/rejectTrigger a backfill for missing ledger ranges:
curl -X POST \
-H "Authorization: Bearer $ADMIN_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"startLedger": 1000, "endLedger": 2000, "batchSize": 100}' \
http://localhost:3001/api/v1/admin/ledger/backfillTrigger reconciliation to detect discrepancies:
curl -X POST \
-H "Authorization: Bearer $ADMIN_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"startLedger": 1000, "endLedger": 2000, "thresholdPercent": 5}' \
http://localhost:3001/api/v1/admin/ledger/reconcilecurl http://localhost:3001/metricsKey metrics:
http_requests_total— Total HTTP requests by method, route, status codehttp_request_duration_seconds— Request latency distributionerror_rate_total— Error count across all systemsingestion_lag_seconds— Time between event creation and processingwebhook_retries_total— Webhook delivery retry countjobs_processed_total/jobs_failed_total— Background job success/failure ratesonchain_operations_total— On-chain operation counts by status
Log entries include:
request_id— Unique identifier for each request (from X-Request-ID header)user_id— User identifier from JWT tokenroute— HTTP method and pathduration_ms— Request processing time in millisecondscorrelationId— Tracks async operations across services
Verify security headers in production:
curl -I http://localhost:3001/api/v1/healthExpected headers:
Strict-Transport-Security: max-age=31536000; includeSubDomainsX-Content-Type-Options: nosniffX-Frame-Options: DENYContent-Security-Policywith strict directivesReferrer-Policy: strict-origin-when-cross-origin
High error rate detected:
- Check
error_rate_totalmetrics breakdown by error type - Review logs for error patterns using
request_idcorrelation - If on-chain failures: check Stellar RPC endpoint status
- If webhook failures: verify external service availability
Ingestion lag increasing:
- Monitor
ingestion_lag_secondsgauge - Check queue depth:
curl http://localhost:3001/api/v1/jobs/status - If lag exceeds 60 seconds: trigger backfill for affected ledger ranges
- Run reconciliation to identify missing data
Webhook delivery failures:
- Check
webhook_retries_totalby reason - Verify external service endpoints are accessible
- Check authentication credentials for external services
- Review webhook payload sizes (may exceed limits)
See CONTRIBUTING.md for development guidelines and coding conventions.