<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nick Gojaman</title>
    <description>The latest articles on DEV Community by Nick Gojaman (@nick_gojaman_aab54d514a33).</description>
    <link>https://dev.to/nick_gojaman_aab54d514a33</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2950781%2F9a04d760-64b3-4c28-99e0-ef2bb1a8b83a.jpeg</url>
      <title>DEV Community: Nick Gojaman</title>
      <link>https://dev.to/nick_gojaman_aab54d514a33</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nick_gojaman_aab54d514a33"/>
    <language>en</language>
    <item>
      <title>Building a Production-Grade Serverless API on AWS: Architecture, Tradeoffs, and Lessons</title>
      <dc:creator>Nick Gojaman</dc:creator>
      <pubDate>Wed, 24 Dec 2025 19:13:48 +0000</pubDate>
      <link>https://dev.to/nick_gojaman_aab54d514a33/building-a-production-grade-serverless-api-on-aws-foa</link>
      <guid>https://dev.to/nick_gojaman_aab54d514a33/building-a-production-grade-serverless-api-on-aws-foa</guid>
      <description>&lt;p&gt;Most tutorials focus on building features. This project focused on operating a real backend system in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is IntellPulse?
&lt;/h2&gt;

&lt;p&gt;IntellPulse is a backend-first, API-only service that generates quantitative trading signals (BUY / HOLD / SELL) with explainability, quota enforcement, and safe deployments.&lt;br&gt;
There is intentionally no UI. The goal was to design and ship a system that behaves like internal fintech infrastructure — not a demo app.&lt;br&gt;
This project emphasizes:&lt;/p&gt;

&lt;p&gt;End-to-end system design (request flow + deployment flow)&lt;br&gt;
Security and access control (authentication, rate limiting)&lt;br&gt;
Operational discipline (staging environments, digest-pinned deployments)&lt;br&gt;
Production readiness (quota tracking, safe rollbacks)&lt;/p&gt;

&lt;p&gt;This article walks through the architecture, key design decisions, and tradeoffs involved in building a production-grade serverless API on AWS — without overengineering.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture Overview
&lt;/h2&gt;

&lt;p&gt;IntellPulse is implemented as a serverless, container-based API with clear separation between runtime and deployment flows.&lt;br&gt;
Request Flow (Runtime)&lt;/p&gt;

&lt;p&gt;Clients access the API over HTTPS using an AWS Lambda Function URL&lt;br&gt;
Requests are handled by a FastAPI application running inside a containerized Lambda function&lt;br&gt;
Authentication and quota enforcement are applied before any signal logic executes&lt;br&gt;
Usage and rate-limit state is stored in Amazon DynamoDB&lt;br&gt;
The API returns structured JSON responses containing signals and explainability metadata&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment Flow (CI/CD)
&lt;/h2&gt;

&lt;p&gt;Code changes trigger a CI/CD pipeline&lt;br&gt;
A container image is built and pushed to Amazon ECR&lt;br&gt;
The Lambda function is updated using a digest-pinned image reference (&lt;a class="mentioned-user" href="https://dev.to/sha256"&gt;@sha256&lt;/a&gt;)&lt;br&gt;
Deployments are promoted through staging → production environments&lt;/p&gt;

&lt;p&gt;This design keeps the runtime path minimal and predictable, while allowing deployments to be performed safely without configuration drift or accidental rollbacks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Farqxub22qvd22dqq1ywf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Farqxub22qvd22dqq1ywf.png" alt="IntellPulse serverless architecture diagram showing Lambda Function URL, containerized FastAPI app, DynamoDB for quotas, and ECR for container storage with CI/CD deployment flow" width="800" height="514"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🧠 Key Design Decisions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1️⃣ Why Lambda Function URLs instead of API Gateway?
&lt;/h3&gt;

&lt;p&gt;For this project, I intentionally used AWS Lambda Function URLs instead of Amazon API Gateway.&lt;br&gt;
Rationale:&lt;/p&gt;

&lt;p&gt;The goal was to expose a small, controlled API surface without introducing additional infrastructure overhead&lt;br&gt;
Lambda Function URLs provide native HTTPS access and integrate cleanly with Lambda-based authentication logic&lt;br&gt;
Advanced API Gateway features (custom domains, request mapping templates, usage plans) were not required for this use case&lt;/p&gt;

&lt;p&gt;Tradeoff accepted:&lt;/p&gt;

&lt;p&gt;Fewer built-in features (no native throttling UI, no API key management)&lt;br&gt;
But: Simpler architecture, faster iteration, lower cost&lt;/p&gt;

&lt;p&gt;What this shows: Understanding when to use lighter-weight AWS services vs. defaulting to heavier managed solutions.&lt;/p&gt;

&lt;h3&gt;
  
  
  2️⃣ Why container-based Lambda for FastAPI?
&lt;/h3&gt;

&lt;p&gt;Rather than adapting FastAPI to a zip-based Lambda deployment, the service runs as a containerized Lambda function stored in Amazon ECR.&lt;br&gt;
Why containers?&lt;/p&gt;

&lt;p&gt;Full control over Python dependencies (no Lambda layer size limits)&lt;br&gt;
Consistent execution environments (local dev = cloud production)&lt;br&gt;
Easier iteration without Lambda packaging constraints&lt;/p&gt;

&lt;p&gt;What this enables:&lt;/p&gt;

&lt;p&gt;FastAPI runs naturally without framework-specific workarounds&lt;br&gt;
Still benefits from Lambda's serverless execution model (auto-scaling, pay-per-request)&lt;/p&gt;

&lt;p&gt;Alternative considered: Lambda Layers&lt;br&gt;
Why rejected: Dependency conflicts and size limits made iteration slower&lt;/p&gt;

&lt;h3&gt;
  
  
  3️⃣ Why DynamoDB for rate limiting and quotas?
&lt;/h3&gt;

&lt;p&gt;Rate limiting and daily usage quotas are enforced using Amazon DynamoDB rather than in-memory or middleware-based solutions.&lt;br&gt;
Why DynamoDB?&lt;/p&gt;

&lt;p&gt;Scales automatically with request volume&lt;br&gt;
Predictable performance under load (single-digit millisecond latency)&lt;br&gt;
Per-key usage tracking with TTL-based expiry (automatic cleanup)&lt;/p&gt;

&lt;p&gt;Implementation approach:&lt;/p&gt;

&lt;p&gt;Each API key has a DynamoDB item with requests_today and last_reset_timestamp&lt;br&gt;
Quota checks happen before signal logic executes&lt;br&gt;
TTL automatically deletes expired quota records&lt;/p&gt;

&lt;p&gt;What this shows: Stateful, durable, horizontally scalable quota enforcement that works at any scale.&lt;/p&gt;

&lt;h3&gt;
  
  
  4️⃣ Why digest-pinned deployments (&lt;a class="mentioned-user" href="https://dev.to/sha256"&gt;@sha256&lt;/a&gt;)?
&lt;/h3&gt;

&lt;p&gt;CI/CD deployments update Lambda functions using digest-pinned container images (&lt;a class="mentioned-user" href="https://dev.to/sha256"&gt;@sha256&lt;/a&gt;:abc123...) rather than mutable tags like latest.&lt;br&gt;
Why this matters:&lt;/p&gt;

&lt;p&gt;Each deployment is deterministic (exact image version is known)&lt;br&gt;
Rollbacks reference known artifacts (not "whatever latest points to now")&lt;br&gt;
Production never pulls unintended versions (no tag drift)&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
bash# ❌ Bad: Mutable tag&lt;br&gt;
aws lambda update-function-code --image-uri 123456789.dkr.ecr.us-east-1.amazonaws.com/intellpulse:latest&lt;/p&gt;

&lt;h2&gt;
  
  
  ✅ Good: Digest-pinned
&lt;/h2&gt;

&lt;p&gt;aws lambda update-function-code --image-uri 123456789.dkr.ecr.us-east-1.amazonaws.com/intellpulse@sha256:abc123...&lt;br&gt;
Tradeoff: Adds CI/CD complexity (pipeline must resolve digests)&lt;br&gt;
Benefit: Significantly improves deployment safety and traceability&lt;br&gt;
What this shows: Senior-level deployment discipline and understanding of immutable infrastructure.&lt;/p&gt;

&lt;h3&gt;
  
  
  5️⃣ Why separate staging and production environments?
&lt;/h3&gt;

&lt;p&gt;Even for a relatively small backend service, separate staging and production environments were maintained.&lt;br&gt;
Why staging matters:&lt;/p&gt;

&lt;p&gt;Validate CI/CD changes before they reach production&lt;br&gt;
Test deployment logic safely (digest resolution, rollback procedures)&lt;br&gt;
Build confidence in changes before promotion&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation:
&lt;/h2&gt;

&lt;p&gt;Staging uses its own Lambda function, DynamoDB table, and ECR repository&lt;br&gt;
CI/CD pipeline deploys to staging first, then requires manual approval for production&lt;br&gt;
Both environments use the same codebase but different AWS accounts (or separate regions)&lt;/p&gt;

&lt;p&gt;What this shows: Operational discipline that mirrors patterns used in larger systems and scales beyond single projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚖️ Tradeoffs and Lessons Learned
&lt;/h2&gt;

&lt;p&gt;Building IntellPulse reinforced the importance of intentional tradeoffs when designing production systems.&lt;br&gt;
Backend-Only Approach&lt;br&gt;
Tradeoff: No visual demo for non-technical users&lt;br&gt;
Benefit: Full focus on correctness, security, and operational discipline&lt;br&gt;
Lesson: Acceptable tradeoff when the target audience is API consumers, not end users&lt;br&gt;
Lambda Function URLs vs API Gateway&lt;br&gt;
Tradeoff: Fewer built-in features (no native throttling, no usage plans)&lt;br&gt;
Benefit: Simpler architecture, faster iteration&lt;br&gt;
Lesson: Understand service boundaries and avoid defaulting to heavier components when they're not required&lt;br&gt;
Digest-Pinned Deployments&lt;br&gt;
Tradeoff: Added CI/CD complexity upfront&lt;br&gt;
Benefit: Eliminated risk of accidental production regressions&lt;br&gt;
Lesson: Safety mechanisms pay off even at small scale&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 What's Next?
&lt;/h2&gt;

&lt;p&gt;If I were extending this system further, the next improvements would focus on &lt;strong&gt;operational maturity&lt;/strong&gt; and &lt;strong&gt;developer ergonomics&lt;/strong&gt;, not additional features.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Planned enhancements:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Infrastructure as Code&lt;/strong&gt; (Terraform or AWS CDK) — eliminate manual AWS Console changes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structured observability&lt;/strong&gt; (CloudWatch metrics, X-Ray tracing) — understand system behavior in production&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lightweight UI&lt;/strong&gt; — internal dashboard for monitoring usage and quotas

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Update:&lt;/strong&gt; Built an interactive demo UI at &lt;a href="https://intellpulse-signals-insight.lovable.app" rel="noopener noreferrer"&gt;https://intellpulse-signals-insight.lovable.app&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Additional signal strategies&lt;/strong&gt; — expand beyond simple BUY/HOLD/SELL logic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Historical query support&lt;/strong&gt; — allow clients to retrieve past signals&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Importantly: These enhancements build on a stable foundation rather than compensating for architectural gaps.&lt;/p&gt;

&lt;h2&gt;
  
  
  📦 Source Code
&lt;/h2&gt;

&lt;p&gt;The full implementation, including the FastAPI service, Lambda container setup, DynamoDB quota logic, and CI/CD pipeline, is available here:&lt;br&gt;
GitHub: &lt;a href="https://github.com/Gojaman/intellpulse" rel="noopener noreferrer"&gt;https://github.com/Gojaman/intellpulse&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  💡 Key Takeaways for Builders
&lt;/h2&gt;

&lt;p&gt;Production-grade doesn't mean overengineered — choose the simplest solution that meets requirements&lt;br&gt;
Deployment safety scales down — digest-pinned deploys and staging environments aren't just for "big systems"&lt;br&gt;
Understand your constraints — Lambda Function URLs vs API Gateway isn't a "one is better" decision; it's context-dependent&lt;br&gt;
Build for operators, not just users — quota enforcement, rollback procedures, and observability matter from day one&lt;/p&gt;

&lt;p&gt;Want to discuss serverless architecture or trading system design? Connect with me:&lt;br&gt;
📘 &lt;a href="https://www.linkedin.com/in/nickgojamanov/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; | 💻 &lt;a href="https://github.com/Gojaman" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; | 🐦 &lt;a href="https://x.com/nijatte" rel="noopener noreferrer"&gt;X&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>cloud</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
