Connecting Micro-Frontends to Serverless Backends: Patterns That Actually Work

Demi Taylor Nimmo
Software Developer

Most developers see micro-frontends and serverless as separate challenges.
Split your monolith into MFEs. Move backend to Lambda. Both sound great on paper.
Then you connect them — and realize the integration is where things break.
While working on an enterprise competence management platform, I implemented an architecture connecting StencilJS micro-frontends to an event-driven AWS serverless backend (Lambda, DynamoDB, Aurora Serverless).
The results:
- 50% reduction in frontend build times
- 60% faster data processing
- 30% improvement in API response times
- 99.9% uptime during migration
Getting there meant rethinking how frontends and backends communicate.
Here's what worked.
The Integration Problem
You gain team autonomy from micro-frontends. You get scalability from serverless.
But if each MFE makes uncoordinated calls to Lambda functions, you've built a distributed monolith.
Common mistakes:
- Duplicated auth logic across MFEs
- Inconsistent error handling between teams
- No centralized rate limiting
- Different retry implementations everywhere
- Client-side shared state management
The question isn't whether to use MFEs and serverless.
It's how to connect them without recreating the coupling you escaped.
The Platform Context
The system managed competence tracking and certification compliance for workers across multiple organizations — particularly high-risk industries requiring regular recertification. It supported both in-person training coordination and online learning.
Three distinct user experiences:
- Workers: View certification status, track compliance deadlines, book required training
- Instructors/Staff: Manage training sessions, track attendance, issue certifications
- Administrators: Compliance reporting, audit trails, certification gap analysis
Each needed different interfaces. All interacted with shared backend services: training management, certification tracking, compliance reporting, audit logging.
The challenge: enabling teams to work independently on frontend domains while maintaining consistency and performance — especially critical when certification lapses could prevent workers from performing their jobs safely.
Pattern 1: API Gateway as Unified Boundary
First architectural decision: API Gateway sits between all MFEs and all Lambda functions.
No direct Lambda invocations from frontend. Everything routes through the gateway.
Why this matters:
Centralized authentication (Cognito)
- JWT validation at gateway before hitting Lambda
- MFEs attach auth headers managed by root application
- Role-based access control enforced consistently (worker, instructor, admin)
Domain-based routing
/training/*→ Training Session Management Lambda/certifications/*→ Certification Tracking Lambda/compliance/*→ Compliance Reporting Lambda/assessments/*→ Assessment Processing Lambda
Rate limiting and throttling
- Protects backend from frontend bugs (infinite loops, retry storms)
- Per-client limits at gateway
- Quota management without Lambda code changes
- Critical during peak certification renewal periods
Unified observability
- All API calls logged through CloudWatch
- Centralized monitoring and alerting
- Request tracing: browser → API Gateway → Lambda
- Essential for debugging certification issues impacting worker compliance
The impact:
API Gateway as single entry point eliminated auth inconsistencies and gave us API control without changing backend code.
That decision alone enabled the 30% API response improvement — not because gateways are inherently faster, but because it forced intentional API design instead of reactive endpoint additions.
Pattern 2: Event-Driven Communication
The biggest performance gain — 60% reduction in data processing time — came from rethinking communication patterns.
Instead of synchronous request/response everywhere, we used event-driven architecture where it made sense.
The shift:
Before:
- MFE calls API → Lambda processes → Returns result
- User waits for entire operation
- Errors block UI
- Frontend handles retries
After:
- MFE calls API → Lambda acknowledges immediately
- Backend processes asynchronously via EventBridge/SQS
- Frontend gets updates via WebSocket or polling
- Failures retry automatically with dead letter queues
Real example:
When staff marks a worker as completing required training:
- Instructor MFE sends completion → API Gateway → Lambda
- Lambda writes to DynamoDB, publishes
training-completedevent to EventBridge - Downstream services react independently:
- Certification service checks renewal requirements, issues updated credentials
- Compliance service updates worker status, removes expiration warnings
- Notification service alerts worker of renewed certification
- Audit service logs completion for regulatory compliance
- MFEs listening for
certification-updatedevents refresh automatically
This decoupled MFEs. The instructor interface didn't need to know about compliance tracking, notifications, or audit logging. Just publish an event and move on.
Why processing time dropped 60%:
Synchronous operations forced sequential processing. One Lambda handling completion, certification checks, compliance updates, notifications, and audit logging — all in sequence.
Event-driven enabled parallel processing across specialized Lambdas.
Five focused Lambdas handling their domains independently instead of one doing everything.
Critical consideration:
Not everything should be async.
- Use events for: Certificate generation, compliance updates, audit logging, notifications
- Use synchronous for: Login, session booking validation, immediate compliance checks
If users need immediate results, don't make it async.
Certification renewals could be async (notify when processed). But checking current compliance had to be synchronous (workers need to know before arriving at job sites).
Pattern 3: Backend-for-Frontend (BFF)
Micro-frontends need shared data — certification status, training requirements, compliance deadlines, session availability.
The anti-pattern: Each MFE calling the same Lambda functions repeatedly, or managing state client-side.
The solution: Backend-for-Frontend Lambda functions optimized for specific frontend needs.
Example:
The Worker Compliance Dashboard MFE needed:
- Current certification status across required credentials
- Upcoming expiration dates and renewal deadlines
- Available training sessions for recertification
- Recent completions and issued certificates
Instead of 4 separate API calls:
// Anti-pattern: Multiple round-trips
const certs = await fetch('/certifications/status');
const sessions = await fetch('/training/available');
const completions = await fetch('/training/recent');
const deadlines = await fetch('/compliance/deadlines');
Single BFF endpoint:
// BFF pattern: One optimized call
const dashboard = await fetch('/bff/worker-compliance');
Why API response times improved:
- Fewer round-trips (reduced latency for workers checking from mobile on job sites)
- Backend aggregates efficiently (DynamoDB batch operations vs. sequential reads)
- API Gateway caching (workers check status frequently)
- Network waterfalls eliminated
For the Instructor Management MFE, separate BFF returning:
- Scheduled training sessions
- Session rosters and attendance tracking
- Pending certifications to issue
- Session capacity and waitlists
Same pattern. Different data contract.
Trade-off:
BFF endpoints couple to specific frontend needs. Dashboard UI changes? BFF changes.
That's intentional. The coupling is explicit and managed, not hidden across generic API calls.
More Lambda functions to maintain. But for a platform with distinct roles (worker, instructor, admin), the performance and developer experience gains were worth it.
Authentication: Centralized in Root MFE
With Cognito at API Gateway, we needed consistent authentication across all micro-frontends.
The architecture: Root/shell MFE manages authentication, passes user context to child MFEs.
The flow:
- Root MFE handles login → Cognito authentication
- Root receives JWT token with role claims (worker, instructor, admin)
- Root manages token storage (secure httpOnly cookie)
- Child MFEs receive user context from root via props/state
- Every API call includes token in
Authorizationheader - API Gateway validates token before invoking Lambda
- Lambda receives validated user context and role
Why this worked:
Single source of truth
- Only root handles login/logout
- Child MFEs (worker dashboard, instructor portal, admin console) never touch authentication directly
- Reduced duplication and inconsistency across teams
Simplified token management
- Root handles token refresh automatically
- Child MFEs don't worry about expired tokens
- Logout from any MFE invalidates session everywhere
Consistent user context
- Root fetches user profile once on login
- Child MFEs receive data as props/context
- No redundant API calls for user info
Implementation pattern:
// Root MFE manages auth
const RootApp = () => {
const [user, setUser] = useState(null);
const [token, setToken] = useState(null);
useEffect(() => {
const session = checkCognitoSession();
if (session) {
setUser(session.user);
setToken(session.token);
}
}, []);
return (
<AuthContext.Provider value={{ user, token }}>
<WorkerDashboardMFE />
<InstructorPortalMFE />
<AdminConsoleMFE />
</AuthContext.Provider>
);
};
// Child MFEs consume auth context
const WorkerDashboardMFE = () => {
const { user, token } = useContext(AuthContext);
const fetchCompliance = async () => {
return fetch('/api/compliance/status', {
headers: { Authorization: `Bearer ${token}` }
});
};
};
Critical principle: Frontend UX vs. Backend Security
MFEs check permissions for UX (hide admin features from workers).
Lambda functions check permissions for security (enforce authorization).
// Child MFE uses role for UX
if (user.role !== 'instructor') {
// Hide "Issue Certification" button
}
// Backend Lambda enforces security
export const handler = async (event) => {
const userRole = event.requestContext.authorizer.claims['custom:role'];
if (userRole !== 'instructor') {
return { statusCode: 403, body: 'Forbidden' };
}
// Proceed
};
Workers shouldn't see admin compliance reports in UI (root MFE hides based on role). Even if they intercept tokens and call APIs directly, Lambda rejects them (backend enforces authorization).
For a platform handling certification records, compliance status, and audit trails — this separation was non-negotiable.
Trade-off:
Root MFE becomes single point of failure for authentication.
Root crashes? Users can't authenticate.
Mitigations:
- Lightweight root MFE (just auth + routing)
- Comprehensive error handling for auth failures
- Fallback UI when root can't establish session
- Separate health monitoring for root vs. child MFEs
But the benefit — consistent, centralized authentication across teams — outweighed the risk.
Error Handling & Retry Logic
In distributed systems, things fail. Networks timeout. Lambdas cold-start. DynamoDB throttles.
The mistake: Implementing retry logic inconsistently across MFEs.
What worked:
Frontend errors:
- Network failures → Exponential backoff with jitter
- 4xx errors → User-friendly message (don't retry)
- 5xx errors → Retry up to 3 times, then fail gracefully
- Timeouts → Cancel request, show "try again" UI
Backend errors:
- Lambda failures → Automatic retries (2x default for async)
- EventBridge events → Exponential backoff retry
- Dead letter queues → Capture failures for manual review
- Circuit breakers → Stop publishing if downstream degraded
Real example:
Staff updates worker certification status. API returns 503 (service unavailable).
- Frontend waits 1s, retries
- Waits 2s, retries
- Waits 4s, retries
- Shows message: "System experiencing high load. Update saved and will process shortly."
Meanwhile, certification update event goes to queue. Backend processes asynchronously. Worker's compliance dashboard updates when complete.
Staff see degraded experience (better than "failed"). Operations get alerted to fix root cause. No certification data lost.
For a competence platform, this was critical. Certification updates can't fail silently. Staff need confidence records are saved. Workers need accurate compliance status.
Data Migration Without Downtime
Hardest challenge: Migrating high-traffic competence platform from old architecture to micro-frontends + serverless without downtime.
Thousands of workers across organizations couldn't experience disruption. Compliance audits required continuous data integrity. Workers at job sites needed real-time certification verification.
The approach: Event-driven dual-write.
Phase 1: Write to both systems
- Every write (training completion, certification issuance, attendance) goes to legacy database AND new DynamoDB
- Reads from legacy system
- Validate data consistency between systems
- Monitor for discrepancies
Phase 2: Switch reads to new system
- Gradual rollout via canary deployments (10%, then 50%, then 100% of traffic)
- Monitor error rates, latency, data accuracy
- Rollback plan (flip reads back to legacy)
- Keep dual-write active (safety net)
Phase 3: Decommission old system
- Stop dual writes once new system stable
- Archive legacy data for compliance
- Remove old infrastructure
Result: 99.9% uptime.
Event-driven architecture made this possible. Asynchronous writes let us validate both systems in parallel without blocking users.
New system failed during dual-write? Events went to dead letter queue. Users continued on legacy system. No certification records lost.
When we switched reads to new system, we did it progressively. Workers never lost access to compliance status. Staff never lost ability to manage certifications.
For a compliance-heavy platform, this was essential. No "maintenance window" that locked workers from verifying credentials or broke regulatory audit trails.
Real-World Trade-offs
These patterns worked — but they're not free.
Cost:
- API Gateway: $3.50 per million requests (adds up with frequent compliance checks)
- Lambda invocations scale with traffic (spikes during renewal periods)
- EventBridge event costs (training completions, certifications, compliance updates)
- DynamoDB on-demand pricing spikes (end-of-quarter compliance reporting)
Optimizations:
- Aggressive caching of worker compliance status
- Provisioned DynamoDB capacity for predictable workloads (certification lookups)
- Batching non-critical events (audit logging doesn't need real-time)
Complexity:
- Debugging distributed systems is hard (failed certification events touch 5+ Lambda functions)
- Tracing requests across MFEs → API Gateway → Lambda → EventBridge requires good observability (CloudWatch, X-Ray)
- Event ordering not guaranteed (designed around it with idempotency keys)
- Cold starts still happen (first certification check after inactivity: 1-2s, mitigated with provisioned concurrency)
Latency:
- API Gateway adds ~30ms per request (acceptable for most operations)
- Cold starts add 500ms-2s first invocation (noticeable during off-peak)
- Event-driven means delays (certificate generation: 5-10s, acceptable if user notified)
For a competence platform, these trade-offs were worth it. Workers could tolerate seconds for certificate generation. They couldn't tolerate inaccurate compliance data or downtime when verifying credentials.
What Actually Worked
Connecting micro-frontends to serverless backends isn't about choosing technologies.
It's about designing the boundary between them intentionally.
What worked:
- API Gateway as unified entry point (consistent auth, routing, rate limiting across MFEs)
- Event-driven communication (60% processing improvement by parallelizing workflows)
- BFF pattern (30% API response improvement by reducing round-trips)
- Root MFE authentication (single source of truth, passed to child MFEs)
- Dual-write migration strategy (99.9% uptime by validating systems in parallel)
What didn't:
- Trying to use events for everything (sync operations need immediate feedback)
- Ignoring costs until they became a problem (DynamoDB and EventBridge costs add up fast)
- Under-investing in observability early (debugging distributed systems without tracing is painful)
Lessons for compliance-critical platforms:
- Audit trails and data integrity aren't optional (when certifications impact worker safety and regulatory compliance, architecture must guarantee data consistency)
- User roles shape MFE boundaries (worker, instructor, admin experiences distinct enough for separate frontends)
- Certification updates can't fail (retry logic and dead letter queues non-negotiable)
- Peak traffic is predictable (renewal cycles, recertification deadlines, compliance reporting periods)
Start simple. Direct API calls from MFEs to Lambda are fine initially.
Add these patterns as complexity grows — not before you need them.
Monitor the boundary. It's where things break.
The goal isn't perfect architecture. It's architecture that lets teams move independently without breaking the system — while keeping critical compliance data accurate and the platform running.
Enjoyed this post?

