<?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: Sanoy24</title>
    <description>The latest articles on DEV Community by Sanoy24 (@sanoy24).</description>
    <link>https://dev.to/sanoy24</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1081462%2Fefccefd1-8c2d-42f4-bc08-3170f137b91a.jpg</url>
      <title>DEV Community: Sanoy24</title>
      <link>https://dev.to/sanoy24</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sanoy24"/>
    <language>en</language>
    <item>
      <title>How MCP fetch servers keep getting SSRF wrong (and how I tried not to)</title>
      <dc:creator>Sanoy24</dc:creator>
      <pubDate>Tue, 11 Aug 2026 13:58:36 +0000</pubDate>
      <link>https://dev.to/sanoy24/how-mcp-fetch-servers-keep-getting-ssrf-wrong-and-how-i-tried-not-to-b27</link>
      <guid>https://dev.to/sanoy24/how-mcp-fetch-servers-keep-getting-ssrf-wrong-and-how-i-tried-not-to-b27</guid>
      <description>&lt;p&gt;If you give an AI agent a tool that fetches URLs, you've given it a tool that&lt;br&gt;
can be pointed at your own infrastructure. That's not a hypothetical — it's&lt;br&gt;
one of the most common real vulnerability classes showing up in MCP servers&lt;br&gt;
right now, and the fixes that "obviously" work keep quietly failing on the&lt;br&gt;
same handful of edge cases.&lt;/p&gt;

&lt;p&gt;I spent the last few weeks building &lt;a href="https://github.com/Sanoy24/safe-fetch-mcp-server" rel="noopener noreferrer"&gt;&lt;code&gt;safe-fetch-mcp-server&lt;/code&gt;&lt;/a&gt;,&lt;br&gt;
an MCP server whose entire job is fetching a URL and returning clean&lt;br&gt;
markdown — and whose entire &lt;em&gt;point&lt;/em&gt; is refusing to do that when the URL&lt;br&gt;
points somewhere it shouldn't. This is the story of what actually made that&lt;br&gt;
hard, a bug that only showed up once I stopped trusting my own test suite,&lt;br&gt;
and how I tried to prove the thing works instead of just asserting it.&lt;/p&gt;
&lt;h2&gt;
  
  
  The problem in one sentence
&lt;/h2&gt;

&lt;p&gt;An agent that can fetch arbitrary URLs on your behalf can be tricked into&lt;br&gt;
fetching &lt;code&gt;http://169.254.169.254/latest/meta-data/&lt;/code&gt; — the cloud metadata&lt;br&gt;
endpoint every major provider exposes — and handing your instance&lt;br&gt;
credentials to whoever wrote the page that told it to. The most widely used&lt;br&gt;
reference fetch server ships with no SSRF protection at all, by its own&lt;br&gt;
README's admission. Several of the "secure" community alternatives have&lt;br&gt;
shipped real CVEs anyway: an IPv6 check that missed the IPv4-mapped loopback&lt;br&gt;
form (&lt;code&gt;::ffff:127.0.0.1&lt;/code&gt;), a background poller that re-fetched a URL through&lt;br&gt;
a code path the original SSRF guard never touched.&lt;/p&gt;

&lt;p&gt;That second one stuck with me. It's not a hard bug to imagine — you write the&lt;br&gt;
guard, you wire it into the code path you're looking at, and eighteen months&lt;br&gt;
later someone adds a "refresh this URL periodically" feature that calls a&lt;br&gt;
slightly different function three files away. The guard was never wrong. It&lt;br&gt;
just wasn't &lt;em&gt;everywhere&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  The principle that actually holds up
&lt;/h2&gt;

&lt;p&gt;Hostname-string checks fail for two structural reasons: IP address encodings&lt;br&gt;
are effectively infinite (&lt;code&gt;2130706433&lt;/code&gt;, &lt;code&gt;0x7f000001&lt;/code&gt;, &lt;code&gt;0177.0.0.1&lt;/code&gt;, and&lt;br&gt;
&lt;code&gt;127.0.0.1&lt;/code&gt; are the same address), and a hostname can legitimately resolve to&lt;br&gt;
something completely different on the next lookup — DNS rebinding, where an&lt;br&gt;
attacker's domain answers with a public IP the first time your validator&lt;br&gt;
checks it, then a private one the moment you actually connect.&lt;/p&gt;

&lt;p&gt;The only approach that closes both holes: &lt;strong&gt;validate the resolved IP, never&lt;br&gt;
the hostname string, and pin the connection to that exact IP.&lt;/strong&gt; Resolve&lt;br&gt;
once. Check the address you got back against explicit blocked ranges — not a&lt;br&gt;
library's opinion of what's "private," explicit ranges you wrote yourself,&lt;br&gt;
because the most popular IP-classification package on npm has itself shipped&lt;br&gt;
an SSRF-bypass CVE. Then connect to &lt;em&gt;that&lt;/em&gt; address, not to "whatever this&lt;br&gt;
hostname resolves to right now."&lt;/p&gt;

&lt;p&gt;That last part is the one people skip, because it's the one Node makes&lt;br&gt;
inconvenient.&lt;/p&gt;
&lt;h2&gt;
  
  
  The bug that only showed up in production
&lt;/h2&gt;

&lt;p&gt;Node's global &lt;code&gt;fetch&lt;/code&gt; — and the &lt;code&gt;http&lt;/code&gt;/&lt;code&gt;https&lt;/code&gt; clients by default — will&lt;br&gt;
re-resolve DNS at connection time regardless of what you validated a moment&lt;br&gt;
earlier. So the fetch server doesn't use them. It resolves the hostname&lt;br&gt;
itself, validates the result, and passes a custom DNS &lt;code&gt;lookup&lt;/code&gt; function into&lt;br&gt;
&lt;code&gt;http.request()&lt;/code&gt; that always answers with that one already-validated&lt;br&gt;
address, no matter what hostname it's asked about. The socket physically&lt;br&gt;
cannot connect anywhere else.&lt;/p&gt;

&lt;p&gt;I wrote sixty-two tests for this. Every threat-matrix row had a green check&lt;br&gt;
mark. And the very first time I drove a real HTTPS request through a real&lt;br&gt;
MCP client instead of my own test fixtures, it failed with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Invalid IP address: undefined
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...four stack frames deep inside Node's TLS socket code, nowhere near&lt;br&gt;
anything I'd written. It took a debugging session to work out why: Node&lt;br&gt;
enables Happy Eyeballs (dual-stack connection racing) by default, and when&lt;br&gt;
it's active, the HTTP client calls your custom &lt;code&gt;lookup&lt;/code&gt; function with&lt;br&gt;
&lt;code&gt;{ all: true }&lt;/code&gt; and expects an &lt;em&gt;array&lt;/em&gt; of addresses back — not the single&lt;br&gt;
string every piece of documentation I'd read implied. My function always&lt;br&gt;
returned a bare string. Node silently misread it, and the failure surfaced&lt;br&gt;
somewhere completely unrelated to the actual bug.&lt;/p&gt;

&lt;p&gt;The fix was three lines — branch on &lt;code&gt;options.all&lt;/code&gt;, return the shape the&lt;br&gt;
caller actually asked for. The lesson was bigger: a mocked test suite proves&lt;br&gt;
your logic is internally consistent. It doesn't prove Node's real networking&lt;br&gt;
stack agrees with your assumptions about its own API. I only found this by&lt;br&gt;
refusing to ship until I'd watched a real fetch succeed against a real HTTPS&lt;br&gt;
site, not just a local fixture server standing in for one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proving it, not asserting it
&lt;/h2&gt;

&lt;p&gt;"Secure by design" is a claim anyone can make. I wanted evidence, so once&lt;br&gt;
the core was built I ran &lt;a href="https://github.com/sattyamjjain/agent-audit-kit" rel="noopener noreferrer"&gt;agent-audit-kit&lt;/a&gt; —&lt;br&gt;
an independent, 276-rule static scanner for MCP servers — against the whole&lt;br&gt;
repository.&lt;/p&gt;

&lt;p&gt;First run: &lt;strong&gt;13 findings. Two critical.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's a real number, and I'm not going to pretend it wasn't a gut-check&lt;br&gt;
moment for a project whose whole pitch is "provably correct." But the&lt;br&gt;
interesting part wasn't the count — it was what the findings actually were&lt;br&gt;
once I read the scanner's own rule source instead of just its output&lt;br&gt;
message.&lt;/p&gt;

&lt;p&gt;The critical one turned out to be a false positive with a genuinely&lt;br&gt;
interesting cause: the rule pattern-matches for literal tokens like&lt;br&gt;
&lt;code&gt;allowedHosts:&lt;/code&gt; in your source to confirm you're guarding against DNS&lt;br&gt;
rebinding on the HTTP transport. I &lt;em&gt;was&lt;/em&gt; guarding against it — through the&lt;br&gt;
MCP SDK's newer &lt;code&gt;createMcpExpressApp()&lt;/code&gt; helper, which handles Host-header&lt;br&gt;
validation internally and never requires you to write that literal token&lt;br&gt;
yourself. The scanner's rule predates that API. Rather than just noting&lt;br&gt;
"false positive" and moving on, I made the code strictly better anyway:&lt;br&gt;
passed the allow-list explicitly instead of relying on the SDK's implicit&lt;br&gt;
host-based auto-detection, which closed a real edge case (the protection&lt;br&gt;
used to silently disable itself if you ever pointed the server at a&lt;br&gt;
non-default host) &lt;em&gt;and&lt;/em&gt; satisfied the scanner honestly, because now the&lt;br&gt;
protection really was explicit.&lt;/p&gt;

&lt;p&gt;Two more findings were genuinely real: the npm dependency was pinned to a&lt;br&gt;
version range wide enough to include builds from before the DNS-rebinding&lt;br&gt;
fix landed upstream, and the HTTP transport had no rate limiting at all — a&lt;br&gt;
real gap, fixed with &lt;code&gt;express-rate-limit&lt;/code&gt; and a test proving a 429 on the&lt;br&gt;
third request over a two-request limit.&lt;/p&gt;

&lt;p&gt;The rest were legitimate false positives — a scanner keyword-matching&lt;br&gt;
&lt;code&gt;169.254.169.254&lt;/code&gt; and the word "bypassed" &lt;em&gt;inside my own security&lt;br&gt;
documentation&lt;/em&gt;, which describes those exact things because that's what the&lt;br&gt;
documentation is for. I documented every exclusion with a written&lt;br&gt;
justification rather than silently suppressing them, because "trust me, it's&lt;br&gt;
fine" is exactly the failure mode a scanner exists to catch.&lt;/p&gt;

&lt;p&gt;Final result: &lt;strong&gt;2 findings, zero critical or high.&lt;/strong&gt; The two that remain are&lt;br&gt;
a dependency-count threshold (normal for any TypeScript project with a real&lt;br&gt;
dev toolchain) and a generic "audit your SDK's scope" nudge that fires for&lt;br&gt;
any project depending on the MCP SDK at all. Both documented, both accepted,&lt;br&gt;
neither hideable.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd tell someone building one of these
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Don't trust hostname strings, ever&lt;/strong&gt; — not even after you've "validated"
them. The only thing worth checking is the resolved IP, and the only way
to make that check mean anything is to connect to the exact address you
checked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mocked tests prove your logic. They don't prove your assumptions about
the runtime.&lt;/strong&gt; Drive at least one real request through a real client
before you believe your own test suite.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run an external scanner and read its source, not just its output.&lt;/strong&gt; A
13-to-2 story is more credible than a 0-finding scan on day one would have
been, because it shows the process, not just the destination.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A guard that isn't in exactly one code path will eventually be
bypassed.&lt;/strong&gt; The single most common real-world SSRF regression is a second
fetch path someone adds later without realizing the first one was ever
guarded at all.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"safe-fetch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"safe-fetch-mcp-server"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;npm: &lt;a href="https://www.npmjs.com/package/safe-fetch-mcp-server" rel="noopener noreferrer"&gt;safe-fetch-mcp-server&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Source: &lt;a href="https://github.com/Sanoy24/safe-fetch-mcp-server" rel="noopener noreferrer"&gt;github.com/Sanoy24/safe-fetch-mcp-server&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;MCP Registry: &lt;code&gt;io.github.Sanoy24/safe-fetch&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Full security write-up, threat matrix, and scan evidence: &lt;a href="https://github.com/Sanoy24/safe-fetch-mcp-server/blob/main/SECURITY.md" rel="noopener noreferrer"&gt;&lt;code&gt;SECURITY.md&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MIT licensed. Issues and PRs welcome — especially if you find the next edge&lt;br&gt;
case I didn't.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>rag</category>
      <category>agents</category>
    </item>
    <item>
      <title>Building a Robust E-Commerce API with FastAPI: A Deep Dive</title>
      <dc:creator>Sanoy24</dc:creator>
      <pubDate>Thu, 20 Nov 2025 18:28:21 +0000</pubDate>
      <link>https://dev.to/sanoy24/building-a-robust-e-commerce-api-with-fastapi-a-deep-dive-f5e</link>
      <guid>https://dev.to/sanoy24/building-a-robust-e-commerce-api-with-fastapi-a-deep-dive-f5e</guid>
      <description>&lt;p&gt;In the world of modern web development, &lt;strong&gt;FastAPI&lt;/strong&gt; has emerged as a superstar framework for building high-performance APIs with Python. Its speed, ease of use, and automatic documentation make it an ideal choice for complex applications like e-commerce platforms.&lt;/p&gt;

&lt;p&gt;In this post, I'll take you through the journey of building a comprehensive &lt;strong&gt;E-Commerce RESTful API&lt;/strong&gt; using FastAPI. We'll explore the architecture, key features, and dive into some code to see how it all comes together.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why FastAPI for E-Commerce?
&lt;/h2&gt;

&lt;p&gt;E-commerce systems require reliability, speed, and scalability. FastAPI checks all these boxes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;High Performance&lt;/strong&gt;: On par with NodeJS and Go (thanks to Starlette and Pydantic).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Type Safety&lt;/strong&gt;: Built on Python 3.10+ type hints, reducing bugs and improving editor support.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Auto-Documentation&lt;/strong&gt;: Generates interactive Swagger UI and ReDoc automatically.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Async Support&lt;/strong&gt;: Native support for asynchronous programming, perfect for I/O-bound tasks like database queries.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Project Overview
&lt;/h2&gt;

&lt;p&gt;Our project is a fully functional backend for an online store. It's designed to be modular, scalable, and secure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;User Management&lt;/strong&gt;: Secure registration and login with JWT (JSON Web Tokens) and Argon2 hashing.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Product Catalog&lt;/strong&gt;: Hierarchical category structure and detailed product management.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Shopping Cart&lt;/strong&gt;: Persistent cart functionality for users.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Order Processing&lt;/strong&gt;: Complete lifecycle management from order creation to history.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Reviews &amp;amp; Ratings&lt;/strong&gt;: Allow users to leave feedback on products.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Address Management&lt;/strong&gt;: Handling shipping and billing addresses.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Tech Stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Framework&lt;/strong&gt;: FastAPI&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Database&lt;/strong&gt;: SQLAlchemy (ORM) + Alembic (Migrations)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Validation&lt;/strong&gt;: Pydantic&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Authentication&lt;/strong&gt;: PyJWT + Argon2-cffi&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Logging&lt;/strong&gt;: Loguru&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Deployment&lt;/strong&gt;: Docker support included&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Architecture &amp;amp; Structure
&lt;/h2&gt;

&lt;p&gt;I've adopted a clean, modular structure to keep the codebase maintainable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app/
├── api/          # Route handlers (v1/)
├── core/         # Config and security
├── crud/         # Database operations
├── db/           # Session management
├── models/       # SQLAlchemy models
├── schema/       # Pydantic schemas
├── services/     # Business logic
└── main.py       # Entry point
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This separation of concerns ensures that our business logic is decoupled from the API layer, and database models are distinct from the API schemas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Deep Dive
&lt;/h2&gt;

&lt;p&gt;Let's look at some interesting parts of the implementation.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Product Model (&lt;code&gt;app/models/product.py&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;We use SQLAlchemy 2.0 style with &lt;code&gt;Mapped&lt;/code&gt; and &lt;code&gt;mapped_column&lt;/code&gt; for a modern, type-hinted definition.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;__tablename__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;products&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;mapped_column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;primary_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;autoincrement&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;mapped_column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;nullable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;mapped_column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;unique&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nullable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;mapped_column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Numeric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;nullable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;stock_quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;mapped_column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Relationships
&lt;/span&gt;    &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Category&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;relationship&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Category&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;back_populates&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;products&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;reviews&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Review&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;relationship&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Review&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;back_populates&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;product&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cascade&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;all, delete-orphan&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Clean Routing (&lt;code&gt;app/api/v1/init_routes.py&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Instead of cluttering &lt;code&gt;main.py&lt;/code&gt;, we centralize our route registration. This makes it easy to add new modules or version our API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;init_routes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include_router&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;healthcheck&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/healthcheck&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include_router&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/users&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include_router&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/category&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include_router&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/product&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include_router&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/cart&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include_router&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/order&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Robust Application Setup (&lt;code&gt;app/main.py&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Our &lt;code&gt;main.py&lt;/code&gt; handles middleware, exception handlers, and metadata configuration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;E-Commerce Backend API&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RESTful API for managing the product catalog...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;docs_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/docs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;redoc_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/redoc&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_middleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LoggingMiddleware&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.exception_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RequestValidationError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;validation_exception_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;RequestValidationError&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Custom error formatting
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;JSONResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTP_422_UNPROCESSABLE_ENTITY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ValidationError&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fields&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Best Practices Implemented
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Dependency Injection&lt;/strong&gt;: FastAPI's dependency injection system is used for database sessions and current user retrieval, making testing easier.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Middleware&lt;/strong&gt;: Custom logging middleware ensures we have visibility into every request.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Global Exception Handling&lt;/strong&gt;: We catch &lt;code&gt;SQLAlchemyError&lt;/code&gt; and generic &lt;code&gt;Exception&lt;/code&gt; to prevent leaking internal server details to the client.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;This project serves as a solid foundation. Future enhancements could include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Redis Caching&lt;/strong&gt;: To speed up product catalog reads.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Celery Tasks&lt;/strong&gt;: For sending emails and processing payments asynchronously.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Elasticsearch&lt;/strong&gt;: For advanced product search capabilities.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building an e-commerce API with FastAPI is a rewarding experience. The framework's design encourages best practices while giving you the speed you need.&lt;/p&gt;

&lt;p&gt;Check out the full code on GitHub &lt;a href="https://github.com/Sanoy24/fastapi-ecommerce" rel="noopener noreferrer"&gt;github repo link&lt;/a&gt; and feel free to contribute!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy Coding✌️!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>fastapi</category>
      <category>python</category>
      <category>api</category>
      <category>postgres</category>
    </item>
  </channel>
</rss>
