Enterprise-grade serverless backend for clinical document annotation, built with Hono and deployed on AWS.
🚀 Live Demo: https://d1pijuvgczqoi4.cloudfront.net/
Unlike traditional servers (EC2, Render, Railway, etc.) that cap their financial damage by crashing under high load, AWS serverless applications scale infinitely.
Under high virality or a DDoS attack, an unprotected serverless stack will spin up thousands of concurrent containers instantly. This can lead to runaway AWS bills of hundreds of thousands of dollars overnight.
To mitigate this, this repository implements a custom DDoS/DoW Circuit Breaker:
- Zero-Base-Cost Function URLs bypass API Gateway completely, eliminating gateway request charges ($3.50/M) for blocked requests.
- CloudWatch Alarm Metric Math monitors total Lambda traffic (
Invocations + Throttles) in a 1-minute window. - Lambda Concurrency Kill Switch automatically triggers during an anomaly, programmatically throttling the API's reserved concurrency to
0to drop subsequent request costs to exactly $0.00.
- Backend: https://github.com/harsh-vardhhan/EHR-backend
- Frontend: https://github.com/harsh-vardhhan/EHR-frontend
The backend follows a highly scalable, serverless architecture designed for clinical data residency and high availability.
graph TD
User((Clinician)) -->|API Request with API Key| LambdaURL[AWS Lambda Function URL]
LambdaURL -->|Hono Router & Auth Middleware| LambdaAPI[AWS Lambda - API]
LambdaAPI -->|Read/Write| DynamoDB[(Amazon DynamoDB)]
LambdaAPI -->|Read| S3[(Amazon S3 - Medical Notes)]
LambdaAPI -->|Manual Trigger| SQS[AWS SQS - Annotation Queue]
S3 -->|Emit Event| EB[Amazon EventBridge - Bus]
EB -->|Route Rule| SQS[AWS SQS - Annotation Queue]
SQS -->|Trigger| LambdaWorker[AWS Lambda - NLP Worker]
SQS -.->|Failures| DLQ[AWS SQS - Dead Letter Queue]
LambdaWorker -->|Inference| Groq[Groq AI - LLM Inference]
LambdaWorker -->|Save Annotations| DynamoDB
%% DDoS Protection
LambdaAPI -.->|Invocations & Throttles| CWAlarm[CloudWatch Traffic Alarm]
CWAlarm -->|Trigger if >200 req/1m| SNS[SNS Topic]
SNS -->|Invoke| LambdaKillSwitch[AWS Lambda - Kill Switch]
LambdaKillSwitch -->|Set Reserved Concurrency to 0| LambdaAPI
| Component | Role in Architecture |
|---|---|
| AWS Lambda (API) | Executes the Hono application, handling UI interactions and document metadata orchestration. |
| AWS Lambda (NLP Worker) | Dedicated asynchronous worker triggered by SQS to perform LLM clinical entity extraction. |
| AWS Lambda (Kill Switch) | Administrative helper triggered by SNS to throttle the API Lambda reserved concurrency to 0. |
| Amazon SQS & DLQ | Decouples document ingestion from analysis, buffers surges, and quarantines failed tasks in a Dead Letter Queue. |
| Amazon DynamoDB | Managed NoSQL storage for ultra-low latency storage of clinical annotation metadata and document status. |
| Amazon S3 | Encrypted object storage for raw clinical document text, acting as the event source for the ingestion pipeline. |
| Lambda Function URL | Public HTTPS endpoint routing requests directly to the Hono backend. |
| CloudWatch Alarm & SNS | Monitors total request volume (Invocations + Throttles) in real-time, acting as the circuit breaker sensor. |
| Groq AI Integration | High-performance inference engine running clinical entity recognition models. |
To maximize performance, cut database costs, and eliminate cross-table JOIN latency, this application uses a consolidated Single-Table Design layout (EhrTable) instead of traditional relational multi-table structures.
| PK (Partition Key) | SK (Sort Key) | Entity Type | Attributes & Schema |
|---|---|---|---|
DOCUMENT#<docId> |
METADATA |
Document | id, title, category, s3Key, status, createdAt |
DOCUMENT#<docId> |
ANNOTATION#<annotationId> |
Annotation | annotationId, documentId, text, label, startOffset, endOffset, createdAt, source, status, confidence |
- Unified Read (Document + Annotations):
When opening a patient note, the backend executes a single DynamoDB query where
PK = DOCUMENT#<docId>. This retrieves the document metadata and all its annotations in a single physical database operation, reducing network roundtrips and latency by 50%. - Inverted Index (
SKIndex): To update or delete an annotation by itsannotationIdalone (without knowing the parentdocumentId), we use a Global Secondary Index (GSI) calledSKIndex(whereHashKey = SKandRangeKey = PK). This resolves the parentPKin milliseconds, allowing targeted, isolated edits on specific rows.
The project uses GitHub Actions for an automated, zero-downtime deployment workflow.
| Pipeline Stage | Processes | Actions & Best Practices | Trigger Event |
|---|---|---|---|
| Continuous Integration (CI) | • Linting • Type Checking |
Runs automated TypeScript linting and strict compilation checks. | All Pull Requests targeting main |
| Continuous Deployment (CD) | • Build & Bundle • AWS SAM Deploy • OIDC Authentication • Env Variable Sync |
Bundles files using esbuild, provisions CloudFormation stacks, signs in passwordlessly using OpenID Connect (OIDC), and syncs secrets. |
Every commit/merge push to main |
This backend incorporates a robust, multi-layered security architecture designed to prevent volumetric DDoS abuse and cloud-native Denial of Wallet (DoW) attacks, guaranteeing predictable operational billing.
| Defense Vector | Implementation & Controls | Purpose & Billing Safety Impact |
|---|---|---|
| Auth Gatekeeper | Valid x-api-key header verified in Hono middleware. |
Rejects unauthenticated requests in ~2ms before executing database operations. |
| Zero-Routing Cost Gateway | Direct Lambda Function URL (no API Gateway request fees). | Eliminates API Gateway per-request charges ($3.50/million), ensuring throttled requests cost exactly $0.00. |
| Automated Circuit Breaker | CloudWatch Alarm (>200 req/1m) |
Automatically updates backend Lambda reserved concurrency to 0 on breach, dropping resource billing to absolute zero. |
| Compute Scaling Caps |
ReservedConcurrentExecutions limits (50 for API Lambda, 2 for SQS NLP Worker). |
Caps the maximum number of concurrent running containers AWS can spin up under a flood. |
| Asynchronous Decoupling | SQS-backed queue hand-off (EhrAnnotationQueue) with BatchSize: 5. |
Prevents container runtime crashes; processes spikes in document uploads sequentially rather than in parallel. |
| Infinite Retry Defense | SQS Dead Letter Queue (EhrAnnotationDLQ) with maxReceiveCount: 3. |
Quarantines failing payloads (poison pills) to prevent endless execution retry loops. |
| Partial Batch Isolation | SQS batch response processing with ReportBatchItemFailures. |
Prevents successfully processed records in a batch from being re-executed when a sibling record in the same batch fails, saving redundant LLM API costs. |
| External API Timeouts | Groq NLP call AbortController (strictly capped at 8 seconds). |
Prevents hung external LLM endpoints from keeping the worker Lambda running up to its 30-second cap. |
| Database Cost Ceiling | TIP DynamoDB table configured with provisioned capacity (5 RCU / 5 WCU). | Acts as a budget boundary, preventing database scaling costs from skyrocketing during attacks. |
| Compute Efficiency | Parallel database writes via Promise.all instead of sequential writes. |
Grouped DB actions run concurrently, reducing billable Lambda active execution time by over 80%. |
| S3 Read-Only Worker | SQS Lambda worker has read-only S3 permissions (S3ReadPolicy) and never writes back to S3. |
There is zero risk of an infinite S3 write-event loop. |
| Agentic Role Scoping | Dev policy (developer-policy.json) restricts agent actions to data-plane only (S3/DynamoDB item actions) and read-only infra visibility. |
Prevents AI agent hallucinations or runaway CLI scripts from deleting infrastructure or provisioning expensive, untracked resources. |
Tip
Manual Recovery after Circuit Breaker Activation:
To bring the system back online after a kill-switch trigger, reset the backend Lambda's reserved concurrency back to your desired capacity (e.g., 5 or delete the limit) via the AWS Console, AWS SDK/CLI, or by redeploying the SAM template.
- Node.js 20+
- AWS CLI (configured via AWS IAM Identity Center/SSO profile e.g.,
ehr-dev) - SAM CLI (optional, for local Lambda emulation)
$ npm install# development
$ npm run start:devnpm run build: Compiles the application.npm run bundle: Creates a production-ready esbuild bundle for AWS Lambda.npm run lint: Runs the linter.npm run test: Executes unit tests.npm run cleanup: Wipes all DynamoDB table items and S3 objects to reset the environment.npm run seed: Seeds the S3 bucket with sample document notes.
Built for the Modern Clinical Workflow.