A serverless kitchen order and inventory system built with AWS CDK, Lambda, DynamoDB, Angular, Cognito, S3, and CloudFront.
This project manages customer orders using a limit-based availability model instead of a complex slot system. The current design is centered on:
- item-specific
lunchanddinnerorder limits - manual killswitch controls (global and per-item)
- lazy daily reset of order counts
- backend validation as the true gatekeeper
- frontend hosting via S3 + CloudFront
┌─────────────────────────────────────────────────────────────────┐
│ CloudFront CDN │
│ ┌──────────────┐ ┌──────────────────────────┐ │
│ │ Frontend App │ │ Images (S3 + CF) │ │
│ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────────┘
|
v
┌─────────────────────────────────────────────────────────────────┐
│ API Gateway (REST) │
│ + Cognito Authorizer │
└─────────────────────────────────────────────────────────────────┘
|
v
┌─────────────────────────────────────────────────────────────────┐
│ Lambda Functions │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Order Lambda │ │ Item Lambda │ │ Admin Lambda │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────────┘
|
v
┌─────────────────────────────────────────────────────────────────┐
│ DynamoDB Tables │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ Orders │ │ Items │ │ Order Limits Config │ │
│ └──────────────┘ └──────────────┘ └──────────────────────────┘ │
│ │
│ ┌──────────────────────────┐ │
│ │ Item Order Count Table │ │
│ └──────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
- Place and manage orders through API Gateway
- Enforce item-level limits for lunch and dinner
- Reject orders when limits are reached
- Support manual global killswitch to pause all orders
- Support manual per-item killswitch to pause specific items
- Initialize with fresh data; no legacy slot migration required
OrderLimitsConfigTablestores limit settings and acceptance flagsItemOrderCountTabletracks daily counts for each item and slot- Lazy reset logic resets counts on the first request for a new day
- Backend gatekeeper avoids issues from client-side bypass
- Angular app served from a private S3 bucket
- CloudFront distribution enforces HTTPS and delivers assets globally
- SPA routing returns
index.htmlfor 404/403 responses
- Image uploads are managed through presigned S3 URLs
- Images served through CloudFront for performance
Primary items:
orderIdstatusitemsslotslotDate- customer and metadata fields
Primary items:
itemIdnamedescriptionpricecategoryimageUrlavailable
Primary items:
itemIdlunchLimitdinnerLimitisAcceptingOrdersglobalKillswitch(for theGLOBALconfig record)
Primary items:
countKey(itemId-slot-date)currentCountlastResetTimestamp
POST /orders— place orderGET /orders— list ordersGET /orders/{orderId}— get order detailsPUT /orders/{orderId}— customer-safe update/cancel for own orderDELETE /orders/{orderId}— customer cancellation/deletion for own order
GET /items— list itemsGET /items/{itemId}— get item detailsPOST /admin/items— create itemPUT /admin/items/{itemId}— update itemDELETE /admin/items/{itemId}— delete itemPOST /admin/items/upload-url— generate image upload URL
GET /admin/order-limits— list configured limitsPUT /admin/order-limits— update order limit settingsPUT /admin/killswitch— toggle global or per-item order acceptance
When a new order is placed, the backend:
- checks the global killswitch
- checks item-specific acceptance state
- loads the configured lunch/dinner limit
- atomically updates the item count in
ItemOrderCountTable - rejects the order if the limit would be exceeded
This ensures concurrent orders cannot push the count above the allowed limit.
npm install
cd lambda/orders && npm install && npm run build
cd lambda/items && npm install && npm run build
cd lambda/admin && npm install && npm run buildFor custom domain setup with CloudFront certificates (which must be in us-east-1):
# First, deploy the CloudFront certificate to us-east-1
npx cdk deploy CloudFrontCertificate-test --profile your-profile --region us-east-1
# Then deploy the main stack to your region (e.g., ap-south-1)
npx cdk synth
npx cdk deploy --context environment=testFor standard deployment without custom domain:
npx cdk synth
npx cdk deploy --context environment=stagingstaging— Development / testingprod— Production
When a domain is purchased, integrate Route 53 and ACM by:
- importing or creating a Route 53 hosted zone
- creating ACM certificates for the domain
- attaching certificates to CloudFront and API Gateway
- creating Route 53 alias records for frontend and API
This hides the native API Gateway URL from customers and serves all traffic under your own domain.
OrderDatabaseItemDatabaseOrderHistoryDatabaseOrderLimitsConfigDatabaseItemOrderCountDatabase
OrderLambdasItemLambdasAdminLambdasOrderAuditLambda
FrontendHosting— S3 + CloudFront distribution
OrderApi— REST API routes for orders, items, and admin actions
- Cognito authorizer protects API routes
- S3 buckets are private and use Origin Access Control
- HTTPS enforced through CloudFront
- IAM roles follow least privilege
- The current design is intentionally simpler than the previous slot-based system
- The backend is the authoritative gatekeeper for all order acceptance
- No slot scheduler or eventbridge slot creation is required in the current architecture
- The system is built for fresh startup usage without legacy data migration
Private project for Manvi's Kitchen
#? - frontend hosting via S?? #? ??#? - frontend hosting via S?? #? ?──???────────?? ?──────────?a
┌────??vel### Sys- ### System???##??#?cu┌────??vel### S
? back?? manual killswitch cont?? back?? man? back?? manual killswitc m ? back?? manual killswitch cont?? back?? man? back?? manu `item ? back?? mars ? back?? manual killsw ? back?? manual killswitch cont?? back?? man? back?? manual ng
#? - frontend hosting via S?? #? ??#? - frontend hosting via S?? #? ?──???────────?? — place order#? - frontend hosting via S?? #? ??#? - frontend hosting via S?? #? ?──???──────── `#? ??#? - frontend hostide#? ?──???────────item
- `G### System???##??#??
- `PUT /items### Sre #? ??#? it### System???##??#???`` ┌────??vel### Sys- ### System???##??#?cu┌────??vel### S
? /┌────??vel### St # ? back?? manual killswitch cont?? back?? man? back?? manual killPU#? - frontend hosting via S??
#? ??#? - frontend hosting via S??
#? ?──???────────?? — place order#? - frontend hosting via S??
#? ??#? - frontend hosting via S??
#? ?──???──────── unt in Item#? ??#? - frontend hostif #? ?──???────────?? re#? ??#? - frontend hosting via S??
#? ?──???──────── #? ??# i#? ?──???──────── #? n - `G### System???##??#??
- `PUT /items### Sre #? ??
┌────??vel### Sys- ### System???##??#?cu┌── ? ? /┌────??vel### St # ? back?? manual killswitch contut#? ??#? - frontend hosting via S??
#? ?──???────────?? — place order#? - frontend hdFront and API Gateway
-#? ?──???────────?? d #? ??#? - frontend hosting via S??
#? ?──???──────── unt in It
#? ?──???──────── unt is #? ?──???──────── #? ??# i#? ?──???──────── #? n - G### System???##??#??
- inLambdas`
- `### Sre #? ??#? -t item details
- `PUT /items### Sre #? ??
┌────??vel### Sys- ### System???##??#?cu┌── ? ? /┌────??on#? ?──???────────?? — place order#? - frontend hdFront and API Gateway
-#? ?──???────────?? d #? ??#? - frontendnc-#? ?──???────────?? d #? ??#? - frontend hosting via S??
#? ?─t #? ?──???──────── unt in It
#? ?──???──────── `uhen
The stack now synthesizes separate Cognito user pools for administrators and customers. Admin users remain in the admin pool and must belong to the configured Admin group for admin-only Lambda operations. Customer users belong to the customer pool; customer order routes never trust caller-supplied ownership fields and scope order history to the authenticated Cognito sub.
Order routes are split by audience:
- Customer:
POST /orders,GET /orders,GET /orders/{orderId},PUT /orders/{orderId},DELETE /orders/{orderId}. - Admin:
POST /admin/orders,GET /admin/orders,GET /admin/orders/{orderId},PUT /admin/orders/{orderId},DELETE /admin/orders/{orderId},PATCH /admin/orders/bulk,GET /admin/orders/{orderId}/history. - Admin configuration:
GET /admin/order-limits,PUT /admin/order-limits,PUT /admin/killswitch. The stalePUT /orders/order-limitsroute is removed.
Item reads are customer/default routes (GET /items, GET /items/{itemId}), while item writes and image upload URL creation are admin routes (POST /admin/items, PUT /admin/items/{itemId}, DELETE /admin/items/{itemId}, POST /admin/items/upload-url).