NestJS REST API backend for the Document Intelligence Platform. Provides comprehensive document processing capabilities including OCR, workflow execution, document labeling, model training, and human-in-the-loop review.
The backend services provide a modular, scalable API for:
- Document upload, storage, and metadata management
- Azure Document Intelligence OCR integration
- Graph-based workflow orchestration via Temporal.io
- Document labeling projects with custom field schemas
- Azure Document Intelligence custom model training
- Human-in-the-loop (HITL) review queue and correction tracking
- Multi-mode authentication (Keycloak SSO + API keys)
- Blob storage abstraction (MinIO/S3 or Azure Blob Storage, switchable via environment variable)
- Framework: NestJS with Express HTTP server
- Database: PostgreSQL with Prisma ORM
- Workflow Engine: Temporal.io for durable, distributed workflows
- OCR: Azure Document Intelligence (formerly Form Recognizer)
- Storage: Pluggable blob storage (MinIO/S3 or Azure Blob Storage, selected via
BLOB_STORAGE_PROVIDERenv var) - Authentication: Keycloak OIDC/SSO + API Key authentication
- API Documentation: Swagger/OpenAPI at
/api
- CRUD operations for documents
- Document status tracking (pre_ocr, ongoing_ocr, completed_ocr, failed)
- OCR result retrieval and key-value pair extraction
- Document approval workflow
- File download endpoints
- Integration with Temporal workflows and blob storage
Key Endpoints:
GET /api/documents- List all documents with optional filtersGET /api/documents/:id- Get document detailsPOST /api/documents/:id/approve- Approve OCR resultsGET /api/documents/:id/file- Download original fileGET /api/documents/:id/ocr-result- Get OCR results with key-value pairs
- File upload via base64-encoded or multipart/form-data
- Automatic OCR workflow triggering
- File type validation
- Metadata handling
Key Endpoints:
POST /api/upload- Upload document and start OCR processing
- Azure Document Intelligence integration
- Model listing (prebuilt and custom models)
- OCR processing coordination
Key Endpoints:
GET /api/models- List available OCR models
- CRUD operations for graph-based workflow definitions
- Workflow versioning
- User-scoped workflow management
- Workflow execution via Temporal
Key Endpoints:
GET /api/workflows- List user workflowsPOST /api/workflows- Create workflowGET /api/workflows/:id- Get workflow detailsPUT /api/workflows/:id- Update workflowDELETE /api/workflows/:id- Delete workflow
- Labeling project management
- Custom field schema definition (string, number, date, signature, selectionMark)
- Document-to-project assignment
- Bounding box label saving
- Label export for training
Key Endpoints:
GET /api/labeling/projects- List projectsPOST /api/labeling/projects- Create projectGET /api/labeling/projects/:id- Get project detailsPOST /api/labeling/projects/:id/fields- Add field definitionPOST /api/labeling/projects/:projectId/documents- Add document to projectPOST /api/labeling/projects/:projectId/documents/:docId/labels- Save labelsGET /api/labeling/projects/:projectId/export- Export labels for training
- Azure Document Intelligence custom model training
- Training data validation
- Training job management and monitoring
- Blob container creation and SAS URL generation
- Label file formatting (.labels.json)
Key Endpoints:
GET /api/training/projects/:projectId/validate- Validate training readinessPOST /api/training/projects/:projectId/train- Start training jobGET /api/training/projects/:projectId/jobs- List training jobsGET /api/training/jobs/:jobId- Get job statusDELETE /api/training/jobs/:jobId- Delete training job and resources
- Document classifier management (create, train, classify)
- Wraps Azure Document Intelligence classifier APIs
- BlobService for container/blob lifecycle and SAS URL generation
- Group-scoped classifiers with PRETRAINING → TRAINING → READY lifecycle
Key Endpoints:
POST /api/azure/classifier— Create classifier recordPOST /api/azure/classifier/documents— Upload training documentsDELETE /api/azure/classifier/documents— Delete training documents (204)POST /api/azure/classifier/train— Start training jobGET /api/azure/classifier/train— Poll training resultPOST /api/azure/classifier/classify— Classify a documentGET /api/azure/classifier/classify— Poll classification result
- Review queue management with filtering
- Review session tracking
- Field-level correction recording
- Confidence score tracking
- Escalation workflow
- Analytics and statistics
Key Endpoints:
GET /api/hitl/queue- Get review queue with filtersGET /api/hitl/queue/stats- Queue statisticsPOST /api/hitl/sessions- Start review sessionPOST /api/hitl/sessions/:id/corrections- Submit correctionsPOST /api/hitl/sessions/:id/approve- Approve documentPOST /api/hitl/sessions/:id/escalate- Escalate for further reviewGET /api/hitl/analytics- Analytics data with filters
- Keycloak OIDC/SSO integration
- JWT token validation
- User context extraction
- Protected route decorators
- API key generation and storage
- bcrypt-based key hashing
- API key authentication guard
- Last-used timestamp tracking
Key Endpoints:
GET /api/api-key- Get user's API key infoPOST /api/api-key- Generate new API keyDELETE /api/api-key- Revoke API key
- Temporal workflow client initialization
- Workflow execution (OCR, Graph workflows)
- Workflow status querying
- Search attribute management
- Workflow cancellation
- Pluggable storage interface (
BlobStorageInterface) - MinIO/S3 implementation (
MinioBlobStorageService) - Azure Blob Storage implementation (
AzureBlobProviderService) - Azure storage — always Azure, for DI model training (
AzureStorageService) - Dynamic module with runtime provider selection via
BLOB_STORAGE_PROVIDER - Operations: write, read, exists, delete, list, deleteByPrefix
- See docs-md/BLOB_STORAGE.md for full architecture docs
- Prisma client wrapper
- Database connection management
- Shared across all modules
- Message queue integration interface
- Ready for RabbitMQ/SQS/Azure Service Bus integration
- Node.js 24+ and npm 10+
- Docker and Docker Compose (for PostgreSQL, MinIO, and Temporal)
- Temporal Server (local or cloud)
- Azure Subscription (for Document Intelligence and Blob Storage)
- Keycloak (optional, for SSO authentication)
npm installCreate a .env file in the apps/backend-services/ directory:
# Server Configuration
PORT=3002
NODE_ENV=development
FRONTEND_URL=http://localhost:3000
# Database
DATABASE_URL=postgresql://user:password@localhost:5432/docintell
# Azure Document Intelligence (OCR)
AZURE_DOCUMENT_INTELLIGENCE_ENDPOINT=https://<your-resource>.cognitiveservices.azure.com/
AZURE_DOCUMENT_INTELLIGENCE_API_KEY=<your-api-key>
AZURE_DOC_INTELLIGENCE_MODELS=prebuilt-layout,prebuilt-document,prebuilt-invoice
# Azure Blob Storage (for primary storage when BLOB_STORAGE_PROVIDER=azure, and always for training)
AZURE_STORAGE_CONNECTION_STRING=DefaultEndpointsProtocol=https;AccountName=...
AZURE_STORAGE_ACCOUNT_NAME=<your-account-name>
AZURE_STORAGE_ACCOUNT_KEY=<your-account-key>
AZURE_STORAGE_CONTAINER=documents
AZURE_STORAGE_TRAINING_CONTAINER=training-data
# Blob Storage Provider Selection (minio or azure, default: minio)
BLOB_STORAGE_PROVIDER=minio
# MinIO Configuration (when BLOB_STORAGE_PROVIDER=minio)
MINIO_ENDPOINT=http://localhost:19000
MINIO_ACCESS_KEY=minioadmin
MINIO_SECRET_KEY=minioadmin
MINIO_DOCUMENT_BUCKET=document-blobs
# Temporal Workflow Engine
TEMPORAL_ADDRESS=localhost:7233
TEMPORAL_NAMESPACE=default
TEMPORAL_TASK_QUEUE=ocr-processing
# Keycloak SSO (Optional)
SSO_AUTH_SERVER_URL=https://keycloak.example.com/auth/realms/standard/protocol/openid-connect
SSO_REALM=standard
SSO_CLIENT_ID=your-client-id
SSO_CLIENT_SECRET=your-client-secret
# Request Limits
BODY_LIMIT=50mb
# Rate Limiting — Global Default (all endpoints, via @nestjs/throttler)
THROTTLE_GLOBAL_TTL_MS=60000 # Time window in milliseconds (default: 60 000 = 1 minute)
THROTTLE_GLOBAL_LIMIT=100 # Max requests per IP per window (default: 100)
# Rate Limiting — Auth Endpoints (login, callback, logout)
THROTTLE_AUTH_TTL_MS=60000 # Time window in milliseconds (default: 60 000 = 1 minute)
THROTTLE_AUTH_LIMIT=10 # Max requests per IP per window (default: 10)
# Rate Limiting — Token Refresh Endpoint
THROTTLE_AUTH_REFRESH_TTL_MS=60000 # Time window in milliseconds (default: 60 000 = 1 minute)
THROTTLE_AUTH_REFRESH_LIMIT=5 # Max requests per IP per window (default: 5)
# API Key Failed-Attempt Throttling
API_KEY_MAX_FAILED_ATTEMPTS=20 # Max failed API key validations per IP before 429 (default: 20)
API_KEY_FAILED_WINDOW_MS=60000 # Tracking window in milliseconds (default: 60 000 = 1 minute)
API_KEY_SWEEP_INTERVAL_MS=60000 # Cleanup interval for stale records in milliseconds (default: 60 000)The project includes a docker-compose.yml for local development services (PostgreSQL and MinIO).
- Docker and Docker Compose installed and running
- Ports
5432(PostgreSQL),19000(MinIO API), and19001(MinIO Console) available
# From repo root
docker compose --profile infra up -dThis starts:
- PostgreSQL on port
5432 - MinIO (S3-compatible blob storage) on port
19000(API) and19001(web console) - minio-init sidecar that auto-creates the
document-blobsandbenchmark-outputsbuckets
- Web Console: http://localhost:19001 — login with
minioadmin/minioadmin - API Endpoint: http://localhost:19000
# Check container health
docker ps | grep minio
# Check bucket initialization logs
docker compose logs minio-initIf minio-init failed (e.g., MinIO wasn't healthy in time), re-run it:
docker compose --profile infra up minio-init- Port conflict: The compose file maps MinIO's internal ports 9000/9001 to host ports 19000/19001. If those are taken, adjust the port mappings in
docker-compose.ymlat the repo root. - Buckets missing: Check
docker compose logs minio-init— the init container depends on MinIO's healthcheck and will retry until ready. - Connection refused from app: Ensure your
.envhasMINIO_ENDPOINT=http://localhost:19000(not port 9000).
This project uses Prisma with a shared schema located at apps/shared/prisma/schema.prisma.
# Generate Prisma client (writes to apps/backend-services/src/generated/)
npm run db:generate
# Run migrations
npm run db:migrate
# (Optional) Seed database
npm run db:seed
# (Optional) Open Prisma Studio for database GUI
npm run db:studioImportant: Migrations are stored in apps/shared/prisma/migrations/ and are shared between backend-services and temporal apps.
# Using Docker Compose (recommended for local development)
# From repo root
docker compose --profile temporal up -d
# Verify Temporal is running
temporal server status# Development mode (with hot reload)
npm run start:dev
# Production mode
npm run build
npm run start:prodThe API will be available at http://localhost:3002.
Interactive Swagger/OpenAPI documentation is available once the server is running:
Swagger UI: http://localhost:3002/api
The API documentation includes:
- All endpoint definitions with request/response schemas
- Authentication requirements (Bearer token or API key)
- Example requests and responses
- Schema definitions for all DTOs
The API supports two authentication modes:
Protected endpoints accept JWT bearer tokens from Keycloak:
curl -X GET http://localhost:3002/api/documents \
-H "Authorization: Bearer <your-jwt-token>"Use the @KeycloakSSOAuth() decorator on protected endpoints.
Protected endpoints accept API keys in the x-api-key header:
curl -X GET http://localhost:3002/api/documents \
-H "x-api-key: <your-api-key>"Use the @ApiKeyAuth() decorator on protected endpoints.
Most endpoints support both authentication methods for flexibility.
npm testIntegration tests validate end-to-end API flows including database and Temporal interactions.
# Run all integration tests
npm run test:int
# Run specific test suite
npm run test:int -- document.spec.ts
# Run graph workflow integration tests
npm run test:int:workflow
# Run with Temporal worker in same process
npm run test:int:workflow:with-workerSee TESTING.md for comprehensive testing documentation.
# Generate Prisma client from shared schema
npm run db:generate
# Create a new migration
npm run db:migrate
# Check migration status
npm run db:status
# Reset database (WARNING: deletes all data)
npm run db:reset
# Open Prisma Studio (database GUI)
npm run db:studio
# Run seed script
npm run db:seedapps/backend-services/
├── src/
│ ├── api-key/ # API key authentication
│ │ ├── api-key.controller.ts
│ │ ├── api-key.service.ts
│ │ └── guards/ # API key guard
│ │
│ ├── azure/ # Azure Document Intelligence classifier
│ │ ├── azure.module.ts
│ │ ├── azure.controller.ts
│ │ ├── azure.service.ts
│ │ ├── blob.service.ts
│ │ ├── classifier.service.ts
│ │ └── dto/
│ │
│ ├── auth/ # Keycloak SSO authentication
│ │ ├── auth.controller.ts
│ │ ├── auth.service.ts
│ │ └── guards/ # JWT guard
│ │
│ ├── blob-storage/ # Storage abstraction
│ │ ├── blob-storage.interface.ts # Interface & injection token
│ │ ├── blob-storage.module.ts # Dynamic provider module
│ │ ├── minio-blob-storage.service.ts # MinIO/S3 implementation
│ │ ├── azure-blob-provider.service.ts # Azure Blob provider (BlobStorageInterface)
│ │ └── azure-storage.service.ts # Azure storage (containers, SAS)
│ │
│ ├── database/ # Prisma database module
│ │ └── database.service.ts
│ │
│ ├── document/ # Document CRUD
│ │ ├── document.controller.ts
│ │ ├── document.service.ts
│ │ └── dto/
│ │
│ ├── hitl/ # Human-in-the-loop
│ │ ├── hitl.controller.ts
│ │ ├── hitl.service.ts
│ │ └── dto/
│ │
│ ├── labeling/ # Document labeling
│ │ ├── labeling.controller.ts
│ │ ├── labeling.service.ts
│ │ └── dto/
│ │
│ ├── ocr/ # Azure Document Intelligence
│ │ ├── ocr.controller.ts
│ │ └── ocr.service.ts
│ │
│ ├── queue/ # Message queue (stub)
│ │ ├── queue.module.ts
│ │ └── queue.service.ts
│ │
│ ├── temporal/ # Temporal client
│ │ ├── temporal-client.service.ts
│ │ └── workflow-types.ts
│ │
│ ├── training/ # Model training
│ │ ├── training.controller.ts
│ │ ├── training.service.ts
│ │ └── dto/
│ │
│ ├── upload/ # Document upload
│ │ ├── upload.controller.ts
│ │ └── dto/
│ │
│ ├── workflow/ # Workflow configuration
│ │ ├── workflow.controller.ts
│ │ ├── workflow.service.ts
│ │ ├── graph-workflow-types.ts
│ │ └── dto/
│ │
│ ├── decorators/ # Custom decorators
│ │ └── custom-auth-decorators.ts
│ │
│ ├── utils/ # Shared utilities
│ ├── testUtils/ # Test utilities
│ │
│ ├── app.module.ts # Root module
│ └── main.ts # Application entrypoint
│
├── integration-tests/ # Integration test suites
│ ├── document.spec.ts
│ ├── upload.spec.ts
│ ├── graph-workflow-tests/
│ └── helpers/
│
├── prisma.config.ts # Prisma configuration
├── nest-cli.json # NestJS CLI config
├── tsconfig.json # TypeScript config
└── package.json # Dependencies
-
Generate module using NestJS CLI:
nest generate module my-feature nest generate controller my-feature nest generate service my-feature
-
Add module to
app.module.ts -
Create DTOs in
my-feature/dto/ -
Add Swagger decorators for API documentation
-
Write tests in
my-feature/*.spec.ts
-
Edit
apps/shared/prisma/schema.prisma -
Create migration:
npm run db:migrate
-
Regenerate Prisma clients:
npm run db:generate
-
Update services to use new models
-
Define workflow in
apps/temporal/src/ -
Register activity in activity registry
-
Use
TemporalClientServiceto start workflows from backend -
Query workflow status via
queryWorkflow()method
Docker support:
# Build image
docker build -t backend-services -f Dockerfile .
# Run container
docker run -p 3002:3002 \
-e DATABASE_URL="postgresql://..." \
-e TEMPORAL_ADDRESS="temporal:7233" \
backend-servicesSee /deployments/openshift/kustomize/ for Kubernetes/OpenShift manifests.
If you see "Cannot find module '@generated/client'":
npm run db:generateEnsure Temporal server is running:
# From repo root
docker compose --profile temporal psVerify environment variables:
AZURE_DOCUMENT_INTELLIGENCE_ENDPOINTAZURE_DOCUMENT_INTELLIGENCE_API_KEY
Test connectivity:
curl -X GET "$AZURE_DOCUMENT_INTELLIGENCE_ENDPOINT/documentintelligence/documentModels?api-version=2023-10-31-preview" \
-H "Ocp-Apim-Subscription-Key: $AZURE_DOCUMENT_INTELLIGENCE_API_KEY"Apache License 2.0