<?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: Deepika Pusala</title>
    <description>The latest articles on DEV Community by Deepika Pusala (@deepika_pusala).</description>
    <link>https://dev.to/deepika_pusala</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%2F4060374%2F1930f0d0-ed1a-4008-a5b1-cb07341e8dec.png</url>
      <title>DEV Community: Deepika Pusala</title>
      <link>https://dev.to/deepika_pusala</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/deepika_pusala"/>
    <language>en</language>
    <item>
      <title>Transactions, Isolation Levels, MVCC, Locking, Triggers, Audit Logs &amp; CAP Theorem in PostgreSQL 🔒</title>
      <dc:creator>Deepika Pusala</dc:creator>
      <pubDate>Thu, 12 Nov 2026 06:36:01 +0000</pubDate>
      <link>https://dev.to/deepika_pusala/transactions-isolation-levels-mvcc-locking-triggers-audit-logs-cap-theorem-in-postgresql-3mld</link>
      <guid>https://dev.to/deepika_pusala/transactions-isolation-levels-mvcc-locking-triggers-audit-logs-cap-theorem-in-postgresql-3mld</guid>
      <description>&lt;h1&gt;
  
  
  🔒 Isolation, MVCC, Locking, Triggers, Audit Logging &amp;amp; CAP Theorem
&lt;/h1&gt;

&lt;p&gt;This is one of the &lt;strong&gt;MOST important&lt;/strong&gt; database topics for a Full Stack Developer. 💪&lt;/p&gt;

&lt;p&gt;Imagine you're building an &lt;strong&gt;Instagram-like Social Feed&lt;/strong&gt;. Thousands of users might like posts, comment, follow people and update profiles &lt;strong&gt;at the same time&lt;/strong&gt;. Now imagine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🛒 Two users try to buy the &lt;strong&gt;last&lt;/strong&gt; available product&lt;/li&gt;
&lt;li&gt;🏦 Two requests update the &lt;strong&gt;same&lt;/strong&gt; bank account&lt;/li&gt;
&lt;li&gt;❤️ Someone likes a post while another person deletes it&lt;/li&gt;
&lt;li&gt;💥 A transaction fails halfway through&lt;/li&gt;
&lt;li&gt;👀 Two people read the same data while a third is modifying it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;How does the database prevent incorrect data, conflicts and inconsistent results? That's exactly what we'll learn, from absolute zero, with PostgreSQL examples and connections to &lt;strong&gt;FastAPI&lt;/strong&gt; and &lt;strong&gt;SQLAlchemy&lt;/strong&gt;. 🚀&lt;/p&gt;




&lt;h2&gt;
  
  
  📚 Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;What is a transaction?&lt;/li&gt;
&lt;li&gt;ACID: Isolation and Durability&lt;/li&gt;
&lt;li&gt;What is concurrency?&lt;/li&gt;
&lt;li&gt;Dirty read&lt;/li&gt;
&lt;li&gt;Non-repeatable read&lt;/li&gt;
&lt;li&gt;Phantom read&lt;/li&gt;
&lt;li&gt;Isolation levels&lt;/li&gt;
&lt;li&gt;MVCC&lt;/li&gt;
&lt;li&gt;Row-level locking and deadlocks&lt;/li&gt;
&lt;li&gt;Database triggers&lt;/li&gt;
&lt;li&gt;Audit logging&lt;/li&gt;
&lt;li&gt;CAP theorem&lt;/li&gt;
&lt;li&gt;ACID vs CAP&lt;/li&gt;
&lt;li&gt;Connecting it all to FastAPI&lt;/li&gt;
&lt;li&gt;Revision sheet&lt;/li&gt;
&lt;li&gt;Interview questions&lt;/li&gt;
&lt;li&gt;Quiz&lt;/li&gt;
&lt;li&gt;Practice checklist (two-session lab included!)&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1️⃣ What is a transaction?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;📦 A &lt;strong&gt;transaction&lt;/strong&gt; is a group of database operations treated as &lt;strong&gt;one logical unit of work&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Transfer ₹1,000 from Account A to Account B:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Account&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;₹5,000&lt;/td&gt;
&lt;td&gt;₹4,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B&lt;/td&gt;
&lt;td&gt;₹2,000&lt;/td&gt;
&lt;td&gt;₹3,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two actions are needed: subtract from A, add to B. But what if ₹1,000 is deducted from A, the server crashes, and B never receives it? 😱 Money has &lt;strong&gt;disappeared&lt;/strong&gt;. Transactions prevent this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;                  → &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="k"&gt;transaction&lt;/span&gt;
  &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="nc"&gt;Account&lt;/span&gt; &lt;span class="no"&gt;A&lt;/span&gt;     → &lt;span class="n"&gt;deduct&lt;/span&gt; ₹&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;
  &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="nc"&gt;Account&lt;/span&gt; &lt;span class="no"&gt;B&lt;/span&gt;     → &lt;span class="k"&gt;add&lt;/span&gt; ₹&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;
&lt;span class="k"&gt;COMMIT&lt;/span&gt;                 → &lt;span class="n"&gt;save&lt;/span&gt; &lt;span class="n"&gt;changes&lt;/span&gt; ✅
&lt;span class="k"&gt;ROLLBACK&lt;/span&gt;               → &lt;span class="n"&gt;undo&lt;/span&gt; &lt;span class="n"&gt;changes&lt;/span&gt; ❌
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;accounts&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;accounts&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;COMMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If an error occurs before committing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ROLLBACK&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BEGIN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Starts a transaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;COMMIT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Permanently saves the transaction's changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ROLLBACK&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cancels uncommitted changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SAVEPOINT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Creates a point inside a transaction you can partially roll back to&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  📍 Bonus: SAVEPOINT
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&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="p"&gt;;&lt;/span&gt;

&lt;span class="no"&gt;SAVEPOINT&lt;/span&gt; &lt;span class="n"&gt;after_order&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;order_items&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;999&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;-- suppose this fails&lt;/span&gt;

&lt;span class="k"&gt;ROLLBACK&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="no"&gt;SAVEPOINT&lt;/span&gt; &lt;span class="n"&gt;after_order&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;-- undo only the item insert&lt;/span&gt;

&lt;span class="k"&gt;COMMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                               &lt;span class="c1"&gt;-- the order itself is still saved&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🧠 A transaction is &lt;strong&gt;not&lt;/strong&gt; necessarily one SQL query. It can contain many.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  2️⃣ ACID: Isolation and Durability 🧪
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;ACID&lt;/strong&gt; is a set of four properties that keep transactions reliable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⚛️ &lt;strong&gt;A: Atomicity.&lt;/strong&gt; All operations succeed or none do.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;C: Consistency.&lt;/strong&gt; Data follows database rules before and after.&lt;/li&gt;
&lt;li&gt;🚦 &lt;strong&gt;I: Isolation.&lt;/strong&gt; Concurrent transactions don't interfere incorrectly.&lt;/li&gt;
&lt;li&gt;💾 &lt;strong&gt;D: Durability.&lt;/strong&gt; Once committed, changes survive crashes (under the database's durability guarantees).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You likely know Atomicity and Consistency already. Today we go deep on &lt;strong&gt;Isolation&lt;/strong&gt; and &lt;strong&gt;Durability&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.1 🚦 Isolation: what does it mean?
&lt;/h3&gt;

&lt;p&gt;Two people use the app at the same time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User A wants to &lt;strong&gt;update&lt;/strong&gt; a product price&lt;/li&gt;
&lt;li&gt;User B wants to &lt;strong&gt;read&lt;/strong&gt; that price&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Isolation&lt;/strong&gt; decides how transactions interact when they run concurrently. The goal: never show users incorrect or incomplete intermediate data.&lt;/p&gt;

&lt;p&gt;Product 101 costs ₹500.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Transaction A:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;700&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- not committed yet!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Meanwhile, Transaction B:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Should B see ₹500 or ₹700? 🤔 It depends on the &lt;strong&gt;isolation level&lt;/strong&gt;, which is the most important part of today's lesson.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.2 💾 Durability: what does it mean?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Once a transaction is successfully &lt;strong&gt;committed&lt;/strong&gt;, its changes should survive a crash or restart.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;User updates their profile name&lt;/li&gt;
&lt;li&gt;Database executes &lt;code&gt;COMMIT&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Database server crashes 💥&lt;/li&gt;
&lt;li&gt;Server restarts&lt;/li&gt;
&lt;li&gt;The updated name is &lt;strong&gt;still there&lt;/strong&gt; ✅&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;How? PostgreSQL uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📓 &lt;strong&gt;Write-Ahead Logging (WAL):&lt;/strong&gt; records changes in a log &lt;em&gt;before&lt;/em&gt; the data pages need to be written&lt;/li&gt;
&lt;li&gt;🔁 &lt;strong&gt;Recovery:&lt;/strong&gt; replays the log after a crash&lt;/li&gt;
&lt;li&gt;🏁 &lt;strong&gt;Checkpoints:&lt;/strong&gt; limit how much work recovery must replay&lt;/li&gt;
&lt;li&gt;🔄 &lt;strong&gt;Storage synchronization:&lt;/strong&gt; persists required data per configured durability settings&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  📓 What is WAL?
&lt;/h3&gt;

&lt;p&gt;Think of WAL as a &lt;strong&gt;diary&lt;/strong&gt; kept by the database. Before changes are safely reflected in the main data files, PostgreSQL writes the needed information to its log.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Application runs an UPDATE
2. PostgreSQL records the change in WAL
3. COMMIT is acknowledged once required WAL durability conditions are met
4. Data pages are written to their main files LATER
5. If a crash happens → PostgreSQL replays WAL during recovery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🎤 &lt;strong&gt;Interview point:&lt;/strong&gt; &lt;code&gt;COMMIT&lt;/code&gt; does &lt;strong&gt;not&lt;/strong&gt; necessarily mean every modified table page has already reached its final location. &lt;strong&gt;WAL is what makes crash recovery possible.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;⚠️ Durability also depends on correct configuration and reliable storage. For example, weakening &lt;code&gt;synchronous_commit&lt;/code&gt; weakens the guarantee.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  3️⃣ What is concurrency? 🚗🚕🚙
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Concurrency&lt;/strong&gt; means multiple transactions execute during &lt;strong&gt;overlapping periods of time&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Your Instagram-like app at one moment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;👤 User A likes a post&lt;/li&gt;
&lt;li&gt;💬 User B comments on the same post&lt;/li&gt;
&lt;li&gt;✏️ User C updates their profile&lt;/li&gt;
&lt;li&gt;📰 User D views the home feed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All these requests hit the backend at nearly the same time, and your backend sends queries for all of them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why is it difficult?
&lt;/h3&gt;

&lt;p&gt;Because transactions may:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read the &lt;strong&gt;same row&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Update the &lt;strong&gt;same row&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Insert new rows&lt;/li&gt;
&lt;li&gt;Delete existing rows&lt;/li&gt;
&lt;li&gt;Depend on each other's data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without proper concurrency control, problems occur. Let's meet the three classic read problems. 👇&lt;/p&gt;




&lt;h2&gt;
  
  
  4️⃣ Dirty Read 🧟
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;A &lt;strong&gt;dirty read&lt;/strong&gt; happens when a transaction reads data modified by another transaction that has &lt;strong&gt;NOT committed yet&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In simple words: you read someone else's &lt;strong&gt;temporary, unconfirmed&lt;/strong&gt; changes.&lt;/p&gt;

&lt;p&gt;Price starts at ₹500.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Transaction A:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;900&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- not committed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Transaction B:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- imagine it reads 900&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Transaction A then:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ROLLBACK&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- price is back to 500&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But B already used ₹900, a value that was &lt;strong&gt;never permanently accepted&lt;/strong&gt;. That's a dirty read.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. A: changes price 500 → 900 (not committed)
2. B: reads 900
3. A: ROLLBACK (price back to 500)
4. ❌ B used data that never officially existed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🐘 &lt;strong&gt;PostgreSQL note:&lt;/strong&gt; dirty reads &lt;strong&gt;never happen&lt;/strong&gt; in PostgreSQL. Even if you request &lt;code&gt;READ UNCOMMITTED&lt;/code&gt;, it behaves like &lt;code&gt;READ COMMITTED&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  5️⃣ Non-repeatable Read 🔄
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;A &lt;strong&gt;non-repeatable read&lt;/strong&gt; happens when the &lt;strong&gt;same transaction reads the same row twice&lt;/strong&gt; and gets &lt;strong&gt;different values&lt;/strong&gt;, because another transaction &lt;strong&gt;committed&lt;/strong&gt; an update in between.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Price starts at ₹500.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Transaction A:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- 500&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Transaction B:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;700&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;COMMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Transaction A reads again:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- 700 😮&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same transaction, same SQL, same row, &lt;strong&gt;different result&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Transaction A:  first read  → ₹500
Transaction B:  updates to ₹700 and COMMITS
Transaction A:  second read → ₹700   ← non-repeatable read
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This matters when your app expects data to stay &lt;strong&gt;stable&lt;/strong&gt; throughout a transaction.&lt;/p&gt;




&lt;h2&gt;
  
  
  6️⃣ Phantom Read 👻
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;A &lt;strong&gt;phantom read&lt;/strong&gt; happens when a transaction runs the &lt;strong&gt;same query twice&lt;/strong&gt; and gets a &lt;strong&gt;different set of rows&lt;/strong&gt;, because another transaction inserted, deleted or changed rows matching the condition.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;employees&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;department&lt;/th&gt;
&lt;th&gt;salary&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;IT&lt;/td&gt;
&lt;td&gt;₹50,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;IT&lt;/td&gt;
&lt;td&gt;₹60,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;HR&lt;/td&gt;
&lt;td&gt;₹40,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Transaction A:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;department&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'IT'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- Employee 1, Employee 2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Transaction B:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;department&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;salary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'IT'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;55000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;COMMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Transaction A runs the same query again:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;department&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'IT'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- Employee 1, Employee 2, Employee 4  👻&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Employee 4 is the &lt;strong&gt;phantom row&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  ⚖️ Non-repeatable read vs phantom read
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;What changes?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Non-repeatable read&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A previously read &lt;strong&gt;row's values&lt;/strong&gt; change&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Phantom read&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The &lt;strong&gt;set of rows&lt;/strong&gt; returned by a query changes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 Non-repeatable read = &lt;strong&gt;same row, different values&lt;/strong&gt;. Phantom read = &lt;strong&gt;different collection of matching rows&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;🐘 &lt;strong&gt;PostgreSQL detail:&lt;/strong&gt; Repeatable Read prevents phantom reads thanks to its snapshot-based MVCC implementation (even though the SQL standard would allow them at that level). Serializable also prevents them, and additionally prevents &lt;em&gt;serialization anomalies&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  7️⃣ Isolation Levels 🎚️ (the most important section!)
&lt;/h2&gt;

&lt;p&gt;An &lt;strong&gt;isolation level&lt;/strong&gt; decides how much protection a transaction gets from changes made by other concurrent transactions. The SQL standard defines four:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Read Uncommitted&lt;/li&gt;
&lt;li&gt;Read Committed&lt;/li&gt;
&lt;li&gt;Repeatable Read&lt;/li&gt;
&lt;li&gt;Serializable&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  7.1 🟥 Read Uncommitted (weakest)
&lt;/h3&gt;

&lt;p&gt;In theory it allows &lt;strong&gt;dirty reads&lt;/strong&gt;. But &lt;strong&gt;PostgreSQL treats it as Read Committed&lt;/strong&gt;, so dirty reads can't occur.&lt;/p&gt;

&lt;h3&gt;
  
  
  7.2 🟨 Read Committed (PostgreSQL default)
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;A transaction only sees data &lt;strong&gt;committed before the start of each individual SQL statement&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Notice: &lt;strong&gt;each individual statement&lt;/strong&gt;. Every statement gets a &lt;strong&gt;fresh snapshot&lt;/strong&gt; of committed data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Transaction A&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;-- ₹500&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Transaction B&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;700&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;COMMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Transaction A again&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;-- ₹700 (fresh snapshot!)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Therefore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Dirty reads: prevented&lt;/li&gt;
&lt;li&gt;⚠️ Non-repeatable reads: possible&lt;/li&gt;
&lt;li&gt;⚠️ Phantom reads: possible&lt;/li&gt;
&lt;li&gt;⚠️ Serialization anomalies: possible&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Great for: normal web apps, social feeds, ordinary CRUD.&lt;/p&gt;

&lt;h3&gt;
  
  
  7.3 🟧 Repeatable Read (stronger)
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;The transaction keeps seeing &lt;strong&gt;the same snapshot&lt;/strong&gt; from its first query onward.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Transaction A&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt; &lt;span class="no"&gt;ISOLATION&lt;/span&gt; &lt;span class="no"&gt;LEVEL&lt;/span&gt; &lt;span class="no"&gt;REPEATABLE&lt;/span&gt; &lt;span class="no"&gt;READ&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;-- 500&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Transaction B&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;700&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;COMMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Transaction A again&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;-- still 500 ✅&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A continues reading its &lt;strong&gt;original snapshot&lt;/strong&gt;. In PostgreSQL this uses &lt;strong&gt;MVCC&lt;/strong&gt; (coming up in section 8).&lt;/p&gt;

&lt;p&gt;Repeatable Read prevents dirty reads, non-repeatable reads and (in PostgreSQL) phantom reads. But it does &lt;strong&gt;not&lt;/strong&gt; prevent every anomaly:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🩺 &lt;strong&gt;Write skew example:&lt;/strong&gt; two transactions each read that &lt;em&gt;at least two doctors are on duty&lt;/em&gt;, then each marks a &lt;strong&gt;different&lt;/strong&gt; doctor off duty. Both commit, and now &lt;strong&gt;nobody&lt;/strong&gt; is on duty. Only &lt;strong&gt;Serializable&lt;/strong&gt; catches this.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Also, if a Repeatable Read transaction tries to update a row that another transaction changed after its snapshot, PostgreSQL raises:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ERROR: could not serialize access due to concurrent update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and your app must &lt;strong&gt;retry&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  7.4 🟩 Serializable (strongest)
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Successfully committed concurrent transactions produce an outcome &lt;strong&gt;equivalent to some serial (one-after-another) execution&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Two users try to buy the last product. Both check stock, both proceed, and boom, wrong result. Serializable &lt;strong&gt;detects dangerous interactions&lt;/strong&gt; and may &lt;strong&gt;abort one transaction&lt;/strong&gt;. The application must &lt;strong&gt;retry&lt;/strong&gt; it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt; &lt;span class="no"&gt;ISOLATION&lt;/span&gt; &lt;span class="no"&gt;LEVEL&lt;/span&gt; &lt;span class="no"&gt;SERIALIZABLE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;COMMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On a conflict, PostgreSQL may return:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ERROR: could not serialize access
due to read/write dependencies among transactions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You must &lt;strong&gt;retry the complete transaction&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 Serializable does &lt;strong&gt;not&lt;/strong&gt; mean transactions literally run one by one. PostgreSQL runs them concurrently and uses &lt;strong&gt;Serializable Snapshot Isolation (SSI)&lt;/strong&gt; to detect dangerous conflicts.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  7.5 📊 Comparison table (interview favorite!)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Isolation level&lt;/th&gt;
&lt;th&gt;Dirty read&lt;/th&gt;
&lt;th&gt;Non-repeatable read&lt;/th&gt;
&lt;th&gt;Phantom read&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Read Uncommitted*&lt;/td&gt;
&lt;td&gt;Possible by the standard&lt;/td&gt;
&lt;td&gt;Possible&lt;/td&gt;
&lt;td&gt;Possible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Read Committed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Repeatable Read&lt;/strong&gt; (PostgreSQL)&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Serializable&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;* PostgreSQL treats Read Uncommitted as Read Committed.&lt;/p&gt;

&lt;p&gt;The table covers these three read anomalies. &lt;strong&gt;Serializable&lt;/strong&gt; also prevents the broader class of &lt;em&gt;serialization anomalies&lt;/em&gt; (like write skew).&lt;/p&gt;

&lt;h3&gt;
  
  
  🪜 Hierarchy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Read Uncommitted  → weakest protection
Read Committed    → prevents dirty reads
Repeatable Read   → stable transaction snapshot
Serializable      → strongest isolation guarantees
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚖️ Higher isolation generally means more conflict handling, possible retries and extra overhead. It doesn't automatically mean every query is slower.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  🛠️ Setting the isolation level
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- per transaction&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt; &lt;span class="no"&gt;ISOLATION&lt;/span&gt; &lt;span class="no"&gt;LEVEL&lt;/span&gt; &lt;span class="no"&gt;REPEATABLE&lt;/span&gt; &lt;span class="no"&gt;READ&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- or right after BEGIN&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="k"&gt;TRANSACTION&lt;/span&gt; &lt;span class="no"&gt;ISOLATION&lt;/span&gt; &lt;span class="no"&gt;LEVEL&lt;/span&gt; &lt;span class="no"&gt;SERIALIZABLE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In SQLAlchemy:&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;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;asyncio&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;create_async_engine&lt;/span&gt;

&lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_async_engine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;"postgresql+asyncpg://user:pass@localhost/db"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;isolation_level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"REPEATABLE READ"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# applies to the whole engine&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# or per connection&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execution_options&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isolation_level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"SERIALIZABLE"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  8️⃣ MVCC: Multi-Version Concurrency Control 🧬
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;MVCC&lt;/strong&gt; lets multiple transactions work with &lt;strong&gt;different versions of the same data&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Product 1 costs ₹500. Transaction A updates it to ₹700. Transaction B wants to read it. How can PostgreSQL let both happen &lt;strong&gt;without unnecessarily blocking each other&lt;/strong&gt;? Through MVCC.&lt;/p&gt;

&lt;p&gt;Conceptually, PostgreSQL keeps different &lt;strong&gt;row versions&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Old row version  (visible to older snapshots)   → Product 1, ₹500
        │  Transaction A updates the price
        ▼
New row version  (visible to newer snapshots)   → Product 1, ₹700
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;📝 This is a simplified picture. PostgreSQL actually stores tuple versions in table storage along with transaction visibility information.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The key ideas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;👴 An older transaction can keep seeing the old committed version&lt;/li&gt;
&lt;li&gt;👶 A newer transaction sees the newly committed version&lt;/li&gt;
&lt;li&gt;📖 &lt;strong&gt;Readers generally don't block writers&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;✍️ &lt;strong&gt;Writers generally don't block ordinary readers&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  📸 What is a snapshot?
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;snapshot&lt;/strong&gt; is the database's view of &lt;strong&gt;which transactions and row versions are visible&lt;/strong&gt; to a particular query or transaction.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A starts at 10:00&lt;/li&gt;
&lt;li&gt;B updates a row at 10:01 and commits&lt;/li&gt;
&lt;li&gt;A (under Repeatable Read) &lt;strong&gt;keeps seeing its original snapshot&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;A new transaction starting at 10:02 &lt;strong&gt;sees the updated value&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🧹 Does PostgreSQL keep old versions forever?
&lt;/h3&gt;

&lt;p&gt;No. A process called &lt;strong&gt;VACUUM&lt;/strong&gt; cleans up obsolete row versions once they're no longer needed. Old versions consume storage, and a &lt;strong&gt;long-running transaction&lt;/strong&gt; can stop PostgreSQL from cleaning some of them, causing &lt;strong&gt;table bloat&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Developer takeaway:&lt;/strong&gt; MVCC improves concurrency, but &lt;strong&gt;avoid long-running transactions&lt;/strong&gt;. Never keep a transaction open while waiting on a user or an external API.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  9️⃣ Row-Level Locking 🔐
&lt;/h2&gt;

&lt;p&gt;Two people update the &lt;strong&gt;same product&lt;/strong&gt; at once:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A wants 9, B wants 8. How does the database coordinate? Through &lt;strong&gt;locks&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🔒 A &lt;strong&gt;lock&lt;/strong&gt; controls access to database resources while transactions operate on them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  9.1 🧱 What is a row-level lock?
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;row-level lock&lt;/strong&gt; protects a particular row from conflicting operations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This locks the selected row. Other transactions trying for conflicting locks on that row may have to &lt;strong&gt;wait&lt;/strong&gt;, but they can still work on &lt;strong&gt;other rows&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ID&lt;/th&gt;
&lt;th&gt;Product&lt;/th&gt;
&lt;th&gt;Lock status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Laptop&lt;/td&gt;
&lt;td&gt;🔒 Locked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Mouse&lt;/td&gt;
&lt;td&gt;🟢 Available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Keyboard&lt;/td&gt;
&lt;td&gt;🟢 Available&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  9.2 ⭐ &lt;code&gt;SELECT ... FOR UPDATE&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;One of the most important commands for backend developers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;COMMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What happens:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;PostgreSQL finds the row&lt;/li&gt;
&lt;li&gt;It acquires a &lt;strong&gt;row-level lock&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Other transactions attempting conflicting updates or row locks &lt;strong&gt;wait&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Your transaction updates the quantity&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;COMMIT&lt;/code&gt; &lt;strong&gt;releases&lt;/strong&gt; the lock&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Use it for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🏦 Banking transactions&lt;/li&gt;
&lt;li&gt;📦 Inventory management&lt;/li&gt;
&lt;li&gt;🎟️ Booking and ticket reservation&lt;/li&gt;
&lt;li&gt;👛 Wallet balance updates&lt;/li&gt;
&lt;li&gt;🧠 Any "read a row, then decide based on its value" logic&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  9.3 🎛️ Row-lock modes
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FOR UPDATE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Strong lock; blocks conflicting modifications and row-locking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FOR NO KEY UPDATE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Weaker; useful when key columns aren't changing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FOR SHARE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Blocks updates/deletes while allowing other shared locks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FOR KEY SHARE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Protects key values against changes that affect foreign keys&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;As a beginner, focus on &lt;strong&gt;&lt;code&gt;FOR UPDATE&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  🎁 Bonus: &lt;code&gt;NOWAIT&lt;/code&gt; and &lt;code&gt;SKIP LOCKED&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Fail immediately instead of waiting&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="no"&gt;NOWAIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Skip rows that are already locked (great for job queues!)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;jobs&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'pending'&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="no"&gt;SKIP&lt;/span&gt; &lt;span class="no"&gt;LOCKED&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  9.4 💀 What is a deadlock?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;A &lt;strong&gt;deadlock&lt;/strong&gt; happens when two or more transactions &lt;strong&gt;wait for each other forever&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;A locks Row 1&lt;/li&gt;
&lt;li&gt;B locks Row 2&lt;/li&gt;
&lt;li&gt;A now wants Row 2&lt;/li&gt;
&lt;li&gt;B now wants Row 1
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Transaction A: owns Row 1, waiting for Row 2
                  ▲                │
                  │                ▼
Transaction B: owns Row 2, waiting for Row 1

        🔁 circular waiting
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PostgreSQL &lt;strong&gt;detects&lt;/strong&gt; the deadlock and &lt;strong&gt;aborts one transaction&lt;/strong&gt; with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ERROR: deadlock detected   (SQLSTATE 40P01)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🛡️ How to prevent deadlocks
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Always acquire locks in a consistent order&lt;/strong&gt; (for example, lower account ID first)&lt;/li&gt;
&lt;li&gt;Keep transactions &lt;strong&gt;short&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Avoid unnecessary locks&lt;/li&gt;
&lt;li&gt;Never wait for external API calls while holding database locks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retry&lt;/strong&gt; on appropriate transient errors&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If every transaction locks Account A first and Account B second, you reduce the risk of opposite lock ordering.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔟 Database Triggers ⚡
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;A &lt;strong&gt;trigger&lt;/strong&gt; is a database mechanism that &lt;strong&gt;automatically executes a function&lt;/strong&gt; when a particular event happens.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Think of it as an &lt;strong&gt;automatic reaction&lt;/strong&gt;. Common events:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;INSERT&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UPDATE&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DELETE&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TRUNCATE&lt;/code&gt; (statement-level triggers)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Real-world example
&lt;/h3&gt;

&lt;p&gt;Whenever a user changes their username, you want to record the &lt;strong&gt;old and new&lt;/strong&gt; username. You &lt;em&gt;could&lt;/em&gt; write that in every endpoint. But what if another developer updates the table directly with SQL? That code gets skipped. A &lt;strong&gt;trigger enforces it at the database level&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  10.1 🔄 How a trigger works
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Database event:   UPDATE users
        ▼
Trigger fires:    PostgreSQL detects the UPDATE
        ▼
Trigger function: records old and new values
        ▼
Audit table:      change history is stored
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  10.2 🧭 Types of triggers
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;By timing:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⏪ &lt;code&gt;BEFORE&lt;/code&gt;: runs before the operation&lt;/li&gt;
&lt;li&gt;⏩ &lt;code&gt;AFTER&lt;/code&gt;: runs after the operation&lt;/li&gt;
&lt;li&gt;🔀 &lt;code&gt;INSTEAD OF&lt;/code&gt;: runs instead of the operation (commonly on views)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;By execution level:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🧍 &lt;code&gt;ROW&lt;/code&gt;-level: once for &lt;strong&gt;every affected row&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;📣 &lt;code&gt;STATEMENT&lt;/code&gt;-level: once for the &lt;strong&gt;entire statement&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Bengaluru'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If 100 users match: a ROW trigger fires &lt;strong&gt;100 times&lt;/strong&gt;, a STATEMENT trigger fires &lt;strong&gt;once&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  10.3 🆕🆚 &lt;code&gt;NEW&lt;/code&gt; and &lt;code&gt;OLD&lt;/code&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;OLD&lt;/code&gt; → the previous version of a row&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NEW&lt;/code&gt; → the new version of a row
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OLD.username = deepika
NEW.username = deepika_p
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;th&gt;Available&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;INSERT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;NEW&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UPDATE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;OLD&lt;/code&gt; and &lt;code&gt;NEW&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DELETE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;OLD&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  10.4 🧪 Hands-on: build an audit trigger
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 1: the users table&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt;       &lt;span class="kt"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="kt"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;    &lt;span class="kt"&gt;VARCHAR&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="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: the audit table&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;user_audit&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt;           &lt;span class="kt"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt;      &lt;span class="kt"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;old_username&lt;/span&gt; &lt;span class="kt"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;new_username&lt;/span&gt; &lt;span class="kt"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;changed_at&lt;/span&gt;   &lt;span class="kt"&gt;TIMESTAMPTZ&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="nb"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&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;p&gt;&lt;strong&gt;Step 3: the trigger function&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="nf"&gt;log_username_changes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="k"&gt;TRIGGER&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; $$
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
    &lt;span class="k"&gt;IF&lt;/span&gt; &lt;span class="no"&gt;OLD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;DISTINCT&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="no"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt;
        &lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;user_audit&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;old_username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;OLD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;OLD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="no"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
$$ &lt;span class="no"&gt;LANGUAGE&lt;/span&gt; &lt;span class="n"&gt;plpgsql&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;IS DISTINCT FROM&lt;/code&gt; is a NULL-safe "not equal" comparison&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;RETURN NEW;&lt;/code&gt; lets the update proceed (required in this kind of trigger)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 4: attach the trigger&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TRIGGER&lt;/span&gt; &lt;span class="n"&gt;username_audit_trigger&lt;/span&gt;
&lt;span class="no"&gt;AFTER&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="no"&gt;EACH&lt;/span&gt; &lt;span class="k"&gt;ROW&lt;/span&gt;
&lt;span class="no"&gt;EXECUTE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="nf"&gt;log_username_changes&lt;/span&gt;&lt;span class="p"&gt;(&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;p&gt;&lt;strong&gt;Step 5: test it&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'deepika'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'deepika@example.com'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'deepika_p'&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;user_audit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;user_id&lt;/th&gt;
&lt;th&gt;old_username&lt;/th&gt;
&lt;th&gt;new_username&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;deepika&lt;/td&gt;
&lt;td&gt;deepika_p&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;🎉 The backend developer &lt;strong&gt;never manually inserted&lt;/strong&gt; an audit record in the &lt;code&gt;UPDATE&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Important:&lt;/strong&gt; trigger actions generally run in the &lt;strong&gt;same transaction&lt;/strong&gt; as the statement that fired them. If that transaction rolls back, the audit row rolls back too. Triggers are great for tracking changes, but they are &lt;strong&gt;not a substitute for tamper-proof security auditing&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To remove a trigger:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;TRIGGER&lt;/span&gt; &lt;span class="n"&gt;username_audit_trigger&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  1️⃣1️⃣ Audit Logging 📝
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Audit logging&lt;/strong&gt; means keeping a record of important actions and changes in a system.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In your social app, you may want to know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Who changed a username?&lt;/li&gt;
&lt;li&gt;Who deleted a post?&lt;/li&gt;
&lt;li&gt;Who changed account privacy?&lt;/li&gt;
&lt;li&gt;When was an email address changed?&lt;/li&gt;
&lt;li&gt;What was the previous value, and the new one?&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;User&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Old value&lt;/th&gt;
&lt;th&gt;New value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;Username update&lt;/td&gt;
&lt;td&gt;deepika&lt;/td&gt;
&lt;td&gt;deepika_p&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;Privacy update&lt;/td&gt;
&lt;td&gt;Public&lt;/td&gt;
&lt;td&gt;Private&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;Profile update&lt;/td&gt;
&lt;td&gt;Old bio&lt;/td&gt;
&lt;td&gt;New bio&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  What should an audit log contain?
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;audit_id
user_id
action
entity_type
entity_id
old_value
new_value
timestamp
request_id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;audit_id&lt;/code&gt; → unique audit record ID&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;user_id&lt;/code&gt; → &lt;strong&gt;who&lt;/strong&gt; performed the action&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;action&lt;/code&gt; → &lt;strong&gt;what&lt;/strong&gt; happened&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;entity_type&lt;/code&gt; → which kind of resource changed&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;entity_id&lt;/code&gt; → which specific resource changed&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;old_value&lt;/code&gt; / &lt;code&gt;new_value&lt;/code&gt; → before and after&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;timestamp&lt;/code&gt; → &lt;strong&gt;when&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;request_id&lt;/code&gt; → which API request caused it&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ⚖️ Application-level vs trigger-based logging
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Application-level logging&lt;/th&gt;
&lt;th&gt;Database trigger logging&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Written in Python/FastAPI&lt;/td&gt;
&lt;td&gt;Written in database functions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Captures &lt;strong&gt;business context&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Captures &lt;strong&gt;row changes&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flexible for complex workflows&lt;/td&gt;
&lt;td&gt;Runs automatically on configured DB events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can be skipped if a code path misses it&lt;/td&gt;
&lt;td&gt;Runs for applicable changes &lt;strong&gt;regardless of entry point&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Easy to include API request info&lt;/td&gt;
&lt;td&gt;Needs extra design to capture request context&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In real systems, &lt;strong&gt;both&lt;/strong&gt; are often used together.&lt;/p&gt;

&lt;h3&gt;
  
  
  🎁 Bonus: giving triggers request context
&lt;/h3&gt;

&lt;p&gt;A trigger doesn't know &lt;em&gt;which app user&lt;/em&gt; made the change. One approach: set a transaction-local setting from your backend, then read it inside the trigger.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- In your app, at the start of the transaction:&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="k"&gt;LOCAL&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;current_user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'101'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Inside the trigger function:&lt;/span&gt;
&lt;span class="c1"&gt;-- current_setting('app.current_user_id', true)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🔐 &lt;strong&gt;Security point:&lt;/strong&gt; don't casually store passwords, raw auth tokens or other secrets in audit logs. Restrict access and protect sensitive logged data.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  1️⃣2️⃣ CAP Theorem 🌐
&lt;/h2&gt;

&lt;p&gt;Now we enter &lt;strong&gt;distributed systems&lt;/strong&gt;. Don't worry, we'll start from zero.&lt;/p&gt;

&lt;h3&gt;
  
  
  12.1 🖥️ What is a distributed system?
&lt;/h3&gt;

&lt;p&gt;One server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Frontend (React) → Backend (FastAPI) → Database (PostgreSQL)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now millions of users arrive. One server isn't enough, so you add more:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Users
  ▼
Load Balancer
  ├─ Server 1
  ├─ Server 2
  └─ Server 3
        ▼
Distributed database / replicas
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your application now runs across &lt;strong&gt;multiple machines communicating over a network&lt;/strong&gt;. But networks fail! 💥&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Server 1 can't talk to Server 2&lt;/li&gt;
&lt;li&gt;A cable fails&lt;/li&gt;
&lt;li&gt;A data center loses connectivity&lt;/li&gt;
&lt;li&gt;A connection becomes extremely slow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are &lt;strong&gt;network partitions&lt;/strong&gt;, and this is where CAP becomes important.&lt;/p&gt;

&lt;h3&gt;
  
  
  12.2 📐 What is CAP theorem?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;In a distributed data system, &lt;strong&gt;when a network partition occurs&lt;/strong&gt;, you cannot guarantee &lt;strong&gt;both&lt;/strong&gt; strong Consistency &lt;strong&gt;and&lt;/strong&gt; Availability for every request.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;CAP&lt;/strong&gt; = &lt;strong&gt;C&lt;/strong&gt;onsistency, &lt;strong&gt;A&lt;/strong&gt;vailability, &lt;strong&gt;P&lt;/strong&gt;artition tolerance.&lt;/p&gt;

&lt;h4&gt;
  
  
  🅲 Consistency (in CAP)
&lt;/h4&gt;

&lt;p&gt;Every successful read sees the &lt;strong&gt;latest successfully committed write&lt;/strong&gt;, or an appropriate failure is returned.&lt;/p&gt;

&lt;p&gt;A user changes their username from &lt;code&gt;deepika&lt;/code&gt; to &lt;code&gt;deepika_p&lt;/code&gt;. Your database has Nodes A, B and C. Under strong consistency, once the update is acknowledged, any successful read from &lt;strong&gt;any&lt;/strong&gt; node returns &lt;code&gt;deepika_p&lt;/code&gt;, never the old name.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🚨 &lt;strong&gt;CAP consistency ≠ ACID consistency.&lt;/strong&gt; ACID consistency is about &lt;strong&gt;constraints and valid states&lt;/strong&gt;. CAP consistency is about &lt;strong&gt;visibility and ordering of data across distributed nodes&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  🅰 Availability
&lt;/h4&gt;

&lt;p&gt;Every request received by a &lt;strong&gt;non-failing node&lt;/strong&gt; gets a &lt;strong&gt;non-error response&lt;/strong&gt;, even if that response doesn't contain the latest data.&lt;/p&gt;

&lt;p&gt;If Node A is unreachable but Node B can still serve the profile request, the system is available. But an available response isn't necessarily the &lt;strong&gt;newest&lt;/strong&gt; value.&lt;/p&gt;

&lt;h4&gt;
  
  
  🅿 Partition Tolerance
&lt;/h4&gt;

&lt;p&gt;The system &lt;strong&gt;keeps operating despite communication failures&lt;/strong&gt; between groups of nodes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Network group A: Node A, Node B
        ✂️  communication broken  ✂️
Network group B: Node C, Node D
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The nodes are all running, but they can't talk to each other. That's a &lt;strong&gt;partition&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  12.3 ⚖️ The CAP trade-off
&lt;/h3&gt;

&lt;p&gt;When a partition occurs, you have two broad choices:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option 1: prioritize Consistency (CP)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Don't allow potentially stale or conflicting reads&lt;/li&gt;
&lt;li&gt;Some requests may &lt;strong&gt;wait or fail&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Consistency over availability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Example:&lt;/em&gt; a strongly consistent banking operation may refuse to proceed without quorum or a valid leader.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option 2: prioritize Availability (AP)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep responding to requests&lt;/li&gt;
&lt;li&gt;Some responses may be &lt;strong&gt;stale&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Replicas reconcile later&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Example:&lt;/em&gt; a social feed shows a slightly older like count for a moment.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;❓ &lt;strong&gt;"Pick any two of three?"&lt;/strong&gt; That's a simplification. The accurate version: &lt;strong&gt;when a network partition occurs, a distributed system must trade off strong consistency against availability.&lt;/strong&gt; Partition tolerance isn't something you can simply switch off, since communication failures are possible. And when there's &lt;em&gt;no&lt;/em&gt; partition, the trade-off looks different.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  12.4 🌍 Real-world examples
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use case&lt;/th&gt;
&lt;th&gt;Typical priority&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🏦 Bank balance transfers&lt;/td&gt;
&lt;td&gt;Strong consistency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;📦 Inventory reservations&lt;/td&gt;
&lt;td&gt;Strong consistency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;📰 Social media feed&lt;/td&gt;
&lt;td&gt;Availability (staleness OK)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;❤️ Like counts&lt;/td&gt;
&lt;td&gt;Often eventual consistency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🎯 Product recommendations&lt;/td&gt;
&lt;td&gt;Availability&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🧭 Distributed coordination&lt;/td&gt;
&lt;td&gt;Strong consistency&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;These are &lt;strong&gt;tendencies, not absolute labels&lt;/strong&gt; for whole products. One application can use different consistency strategies for different features.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  12.5 ⏳ What is eventual consistency?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;If no new updates happen and communication is restored, &lt;strong&gt;all replicas eventually converge&lt;/strong&gt; to the same value.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You like a post:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Primary:  Likes = 101
Replica:  Likes = 100     ← hasn't received the update yet

...a moment later...

Primary:  Likes = 101
Replica:  Likes = 101     ← converged ✅
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Strong consistency&lt;/th&gt;
&lt;th&gt;Eventual consistency&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Reads reflect the latest committed write&lt;/td&gt;
&lt;td&gt;Reads may temporarily be stale&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;More coordination required&lt;/td&gt;
&lt;td&gt;Allows more independent operation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Good for financial operations&lt;/td&gt;
&lt;td&gt;Good for feeds, analytics, some counters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;May reject/delay requests during partitions&lt;/td&gt;
&lt;td&gt;Can keep serving during partitions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In your social feed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📝 Creating a post must &lt;strong&gt;reliably save&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🔒 Privacy settings need &lt;strong&gt;careful consistency&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;❤️ Like counts can tolerate a &lt;strong&gt;small delay&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🎯 Recommendations can update &lt;strong&gt;asynchronously&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Backend developers choose guarantees based on &lt;strong&gt;business requirements&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  1️⃣3️⃣ ACID vs CAP: don't confuse them! ⚔️
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ACID&lt;/th&gt;
&lt;th&gt;CAP&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;About &lt;strong&gt;transaction reliability&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;About &lt;strong&gt;distributed system trade-offs&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Atomicity&lt;/td&gt;
&lt;td&gt;Consistency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consistency&lt;/td&gt;
&lt;td&gt;Availability&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Isolation&lt;/td&gt;
&lt;td&gt;Partition tolerance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Durability&lt;/td&gt;
&lt;td&gt;(no direct equivalent)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ACID Isolation&lt;/strong&gt; → transactions don't interfere incorrectly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CAP Consistency&lt;/strong&gt; → latest values observed across distributed nodes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ACID Durability&lt;/strong&gt; → committed transactions survive failures&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CAP Partition Tolerance&lt;/strong&gt; → handling network communication failures&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They are &lt;strong&gt;not interchangeable&lt;/strong&gt; concepts.&lt;/p&gt;




&lt;h2&gt;
  
  
  1️⃣4️⃣ Connecting it all to your FastAPI project 🔌
&lt;/h2&gt;

&lt;p&gt;Imagine users can reserve &lt;strong&gt;limited promotional slots&lt;/strong&gt;. Only &lt;strong&gt;one slot&lt;/strong&gt; is left, and two users try to reserve it simultaneously.&lt;/p&gt;

&lt;p&gt;Without concurrency control:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User A reads &lt;code&gt;available_slots = 1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;User B reads &lt;code&gt;available_slots = 1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;User A reserves it&lt;/li&gt;
&lt;li&gt;User B &lt;strong&gt;also&lt;/strong&gt; reserves it&lt;/li&gt;
&lt;li&gt;💥 &lt;strong&gt;Overbooked!&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  ✅ Solution 1: row-level locking
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;available_slots&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;promotions&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;promotions&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;available_slots&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;available_slots&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;available_slots&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;COMMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The lock makes competing transactions &lt;strong&gt;wait&lt;/strong&gt;. Also check whether the &lt;code&gt;UPDATE&lt;/code&gt; actually affected a row. If it didn't, no slot was available.&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ Solution 2: a single conditional UPDATE (often simplest!)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;promotions&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;available_slots&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;available_slots&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;available_slots&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;RETURNING&lt;/span&gt; &lt;span class="n"&gt;available_slots&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is &lt;strong&gt;atomic&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Row returned → reservation &lt;strong&gt;succeeded&lt;/strong&gt; ✅&lt;/li&gt;
&lt;li&gt;No row returned → &lt;strong&gt;no slot available&lt;/strong&gt; ❌&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A very useful backend technique. 🧠&lt;/p&gt;

&lt;h3&gt;
  
  
  🐍 FastAPI + SQLAlchemy version
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;select&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;reserve_slot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;promotion_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&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;with&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;begin&lt;/span&gt;&lt;span class="p"&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Promotion&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Promotion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;promotion_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with_for_update&lt;/span&gt;&lt;span class="p"&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;promotion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scalar_one_or_none&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;promotion&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Promotion not found"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;promotion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;available_slots&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"No slots available"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;promotion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;available_slots&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;promotion&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Code&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;session.begin()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Starts a transaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;select(Promotion)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Builds a SELECT query&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.where(...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Filters the requested promotion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.with_for_update()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Requests a &lt;strong&gt;row-level lock&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;scalar_one_or_none()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Gets one result or &lt;code&gt;None&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;available_slots -= 1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Updates the value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;End of &lt;code&gt;with&lt;/code&gt; block&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Commits&lt;/strong&gt; on success, &lt;strong&gt;rolls back&lt;/strong&gt; on exception&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;code&gt;session.begin()&lt;/code&gt; raises an error if a transaction has already started on that session (for example, a dependency already ran a query). If your project's session dependency auto-begins, use the explicit &lt;code&gt;await session.commit()&lt;/code&gt; / &lt;code&gt;await session.rollback()&lt;/code&gt; style instead. Stay consistent with your project's pattern.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  🔁 Retrying on serialization failures and deadlocks
&lt;/h3&gt;

&lt;p&gt;Serializable (and Repeatable Read) can abort a transaction by design, so your code should retry:&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;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exc&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;DBAPIError&lt;/span&gt;

&lt;span class="no"&gt;MAX_RETRIES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&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;run_with_retry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session_factory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;MAX_RETRIES&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&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;with&lt;/span&gt; &lt;span class="nf"&gt;session_factory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;try&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;with&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nc"&gt;DBAPIError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="c1"&gt;# 40001 = serialization_failure, 40P01 = deadlock_detected&lt;/span&gt;
                &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;getattr&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;orig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sqlstate"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nb"&gt;getattr&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;orig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"pgcode"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"40001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"40P01"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;MAX_RETRIES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="k"&gt;continue&lt;/span&gt;
                &lt;span class="k"&gt;raise&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 Retry the &lt;strong&gt;entire&lt;/strong&gt; transaction, never just the failed statement.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  1️⃣5️⃣ Complete revision sheet 🧾
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;Simple meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Transaction&lt;/td&gt;
&lt;td&gt;Group of operations treated as one unit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BEGIN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start a transaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;COMMIT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Save changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ROLLBACK&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Undo uncommitted changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Isolation&lt;/td&gt;
&lt;td&gt;Controls how concurrent transactions interact&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dirty read&lt;/td&gt;
&lt;td&gt;Reading &lt;strong&gt;uncommitted&lt;/strong&gt; changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Non-repeatable read&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Same row&lt;/strong&gt; returns different values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Phantom read&lt;/td&gt;
&lt;td&gt;Same query returns a &lt;strong&gt;different set of rows&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read Committed&lt;/td&gt;
&lt;td&gt;Each &lt;strong&gt;statement&lt;/strong&gt; sees a fresh committed snapshot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Repeatable Read&lt;/td&gt;
&lt;td&gt;The &lt;strong&gt;transaction&lt;/strong&gt; sees a stable snapshot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Serializable&lt;/td&gt;
&lt;td&gt;Concurrent execution behaves like serial execution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MVCC&lt;/td&gt;
&lt;td&gt;Maintains multiple row versions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Row-level locking&lt;/td&gt;
&lt;td&gt;Locks individual rows against conflicts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deadlock&lt;/td&gt;
&lt;td&gt;Transactions wait on each other's locks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trigger&lt;/td&gt;
&lt;td&gt;Auto-runs database logic on an event&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Audit logging&lt;/td&gt;
&lt;td&gt;Records who changed what and when&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WAL&lt;/td&gt;
&lt;td&gt;Log used for crash recovery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Durability&lt;/td&gt;
&lt;td&gt;Committed data survives crashes (under configured guarantees)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Distributed system&lt;/td&gt;
&lt;td&gt;Multiple machines working together&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CAP theorem&lt;/td&gt;
&lt;td&gt;Consistency vs Availability during partitions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Eventual consistency&lt;/td&gt;
&lt;td&gt;Replicas eventually converge&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  1️⃣6️⃣ Important interview questions 🎤
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q1. Read Committed vs Repeatable Read?&lt;/strong&gt;&lt;br&gt;
Read Committed takes a new snapshot for &lt;strong&gt;every statement&lt;/strong&gt;. Repeatable Read (in PostgreSQL) uses a &lt;strong&gt;stable snapshot for the whole transaction&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q2. What is a dirty read?&lt;/strong&gt;&lt;br&gt;
Reading data modified by another transaction &lt;strong&gt;before it commits&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q3. Non-repeatable vs phantom reads?&lt;/strong&gt;&lt;br&gt;
Non-repeatable reads involve &lt;strong&gt;changed values in an existing row&lt;/strong&gt;. Phantom reads involve &lt;strong&gt;changes in the set of rows&lt;/strong&gt; matching a query.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q4. What is MVCC?&lt;/strong&gt;&lt;br&gt;
Multi-Version Concurrency Control. It lets transactions access different versions of rows, improving concurrency between readers and writers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q5. What is &lt;code&gt;SELECT FOR UPDATE&lt;/code&gt;?&lt;/strong&gt;&lt;br&gt;
It acquires row-level locks on selected rows so competing transactions can't perform conflicting operations on them until the lock is released.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q6. What is a deadlock?&lt;/strong&gt;&lt;br&gt;
Transactions waiting for locks held by each other, so none can progress. The database detects it and aborts one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q7. What is a database trigger?&lt;/strong&gt;&lt;br&gt;
A mechanism that automatically executes a trigger function when a specified database event occurs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q8. What is audit logging?&lt;/strong&gt;&lt;br&gt;
Recording important changes and actions: who did them, when, and what changed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q9. What is WAL?&lt;/strong&gt;&lt;br&gt;
Write-Ahead Logging. PostgreSQL records changes in a log to support crash recovery and durability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q10. Explain CAP theorem.&lt;/strong&gt;&lt;br&gt;
During a network partition, a distributed system must choose between guaranteeing strong consistency for successful requests and guaranteeing availability for every request.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q11. What is eventual consistency?&lt;/strong&gt;&lt;br&gt;
Replicas may temporarily differ but converge once updates stop and synchronization succeeds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q12. ACID consistency vs CAP consistency?&lt;/strong&gt;&lt;br&gt;
ACID consistency means transactions preserve database rules and constraints. CAP consistency means reads across distributed nodes respect the latest successful write under the chosen guarantee.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q13. What is write skew, and which isolation level prevents it?&lt;/strong&gt;&lt;br&gt;
Two transactions read overlapping data and then update &lt;em&gt;different&lt;/em&gt; rows based on what they read, producing an invalid combined result. Only &lt;strong&gt;Serializable&lt;/strong&gt; prevents it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q14. Should you retry a failed Serializable transaction?&lt;/strong&gt;&lt;br&gt;
Yes. Retry the &lt;strong&gt;entire transaction&lt;/strong&gt;, since serialization failures are an expected part of the model.&lt;/p&gt;




&lt;h2&gt;
  
  
  1️⃣7️⃣ Test your understanding! 🧪
&lt;/h2&gt;

&lt;p&gt;Answer without scrolling up. (Answers are at the bottom.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Transaction A updates a price but hasn't committed. Transaction B reads that value. What is this?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Dirty read&lt;/li&gt;
&lt;li&gt;B) Phantom read&lt;/li&gt;
&lt;li&gt;C) Deadlock&lt;/li&gt;
&lt;li&gt;D) Durability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Transaction A reads the same row twice but sees ₹500 and then ₹700 after another transaction commits. What is this?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Dirty read&lt;/li&gt;
&lt;li&gt;B) Non-repeatable read&lt;/li&gt;
&lt;li&gt;C) Phantom read&lt;/li&gt;
&lt;li&gt;D) Deadlock&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Which isolation level provides a stable transaction snapshot in PostgreSQL?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Read Uncommitted&lt;/li&gt;
&lt;li&gt;B) Read Committed&lt;/li&gt;
&lt;li&gt;C) Repeatable Read&lt;/li&gt;
&lt;li&gt;D) None&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Which SQL command locks selected rows against conflicting modifications?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) &lt;code&gt;SELECT DISTINCT&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;B) &lt;code&gt;SELECT FOR UPDATE&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;C) &lt;code&gt;SELECT COUNT&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;D) &lt;code&gt;SELECT LIMIT&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;5. What happens to trigger-generated audit rows if the triggering transaction rolls back?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) They remain permanently&lt;/li&gt;
&lt;li&gt;B) They are automatically emailed&lt;/li&gt;
&lt;li&gt;C) They are rolled back too&lt;/li&gt;
&lt;li&gt;D) Nothing happens&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;6. Which PostgreSQL mechanism maintains multiple row versions?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) WAL&lt;/li&gt;
&lt;li&gt;B) MVCC&lt;/li&gt;
&lt;li&gt;C) CAP&lt;/li&gt;
&lt;li&gt;D) JSON&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;7. During a network partition, what trade-off does CAP describe?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Atomicity vs Durability&lt;/li&gt;
&lt;li&gt;B) Consistency vs Availability&lt;/li&gt;
&lt;li&gt;C) Speed vs Storage&lt;/li&gt;
&lt;li&gt;D) SQL vs NoSQL&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;8. What does WAL primarily help PostgreSQL achieve?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Frontend rendering&lt;/li&gt;
&lt;li&gt;B) Password hashing&lt;/li&gt;
&lt;li&gt;C) Crash recovery and durability&lt;/li&gt;
&lt;li&gt;D) API routing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👀 Click to reveal answers&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A&lt;/strong&gt;: dirty read&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;B&lt;/strong&gt;: non-repeatable read&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C&lt;/strong&gt;: Repeatable Read&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;B&lt;/strong&gt;: &lt;code&gt;SELECT FOR UPDATE&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C&lt;/strong&gt;: they roll back too (same transaction)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;B&lt;/strong&gt;: MVCC&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;B&lt;/strong&gt;: Consistency vs Availability&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C&lt;/strong&gt;: crash recovery and durability&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1️⃣8️⃣ What to practice as a Full Stack Developer 🛠️
&lt;/h2&gt;

&lt;p&gt;Practice in PostgreSQL using &lt;strong&gt;pgAdmin&lt;/strong&gt; or &lt;strong&gt;psql&lt;/strong&gt;, with &lt;strong&gt;two query windows&lt;/strong&gt; open side by side.&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ Practical checklist
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Create two transactions and demonstrate &lt;code&gt;COMMIT&lt;/code&gt; and &lt;code&gt;ROLLBACK&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Reproduce a non-repeatable read using two sessions&lt;/li&gt;
&lt;li&gt;[ ] Compare Read Committed and Repeatable Read using two sessions&lt;/li&gt;
&lt;li&gt;[ ] Try &lt;code&gt;SELECT FOR UPDATE&lt;/code&gt; with two simultaneous transactions&lt;/li&gt;
&lt;li&gt;[ ] Create a deadlock intentionally and observe PostgreSQL's error&lt;/li&gt;
&lt;li&gt;[ ] Create a PostgreSQL trigger for username changes&lt;/li&gt;
&lt;li&gt;[ ] Create an audit table and verify old/new values&lt;/li&gt;
&lt;li&gt;[ ] Observe WAL and understand how PostgreSQL performs recovery&lt;/li&gt;
&lt;li&gt;[ ] Explain CP and AP with a real-world example&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🧪 Mini lab: non-repeatable read (Read Committed)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Setup (run once)&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="kt"&gt;INT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&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;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Session 1&lt;/th&gt;
&lt;th&gt;Session 2&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BEGIN;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;SELECT price FROM products WHERE id = 1;&lt;/code&gt; → &lt;strong&gt;500&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;UPDATE products SET price = 700 WHERE id = 1;&lt;/code&gt; (autocommits)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;SELECT price FROM products WHERE id = 1;&lt;/code&gt; → &lt;strong&gt;700&lt;/strong&gt; 😮&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;code&gt;COMMIT;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  🧪 Mini lab: same test under Repeatable Read
&lt;/h3&gt;

&lt;p&gt;Reset the price to 500, then:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Session 1&lt;/th&gt;
&lt;th&gt;Session 2&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BEGIN ISOLATION LEVEL REPEATABLE READ;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;SELECT price FROM products WHERE id = 1;&lt;/code&gt; → &lt;strong&gt;500&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;UPDATE products SET price = 700 WHERE id = 1;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;SELECT price FROM products WHERE id = 1;&lt;/code&gt; → &lt;strong&gt;still 500&lt;/strong&gt; ✅&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;COMMIT;&lt;/code&gt; then &lt;code&gt;SELECT&lt;/code&gt; again → &lt;strong&gt;700&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  🧪 Mini lab: &lt;code&gt;FOR UPDATE&lt;/code&gt; blocking
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Session 1&lt;/th&gt;
&lt;th&gt;Session 2&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BEGIN;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SELECT * FROM products WHERE id = 1 FOR UPDATE;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BEGIN;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;UPDATE products SET price = 999 WHERE id = 1;&lt;/code&gt; → &lt;strong&gt;hangs, waiting&lt;/strong&gt; ⏳&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;code&gt;COMMIT;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Session 2's update now proceeds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;COMMIT;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  🧪 Mini lab: create a deadlock 💀
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="k"&gt;VALUES&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="mi"&gt;800&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;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Session 1&lt;/th&gt;
&lt;th&gt;Session 2&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BEGIN;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BEGIN;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;code&gt;UPDATE products SET price = 1 WHERE id = 1;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;UPDATE products SET price = 2 WHERE id = 2;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;UPDATE products SET price = 3 WHERE id = 2;&lt;/code&gt; → waits ⏳&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;UPDATE products SET price = 4 WHERE id = 1;&lt;/code&gt; → 💥 &lt;strong&gt;deadlock detected&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;PostgreSQL aborts one of them. Run &lt;code&gt;ROLLBACK;&lt;/code&gt; in the aborted session. Then think: how would &lt;strong&gt;consistent lock ordering&lt;/strong&gt; have avoided this?&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 Final thoughts
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;My biggest advice:&lt;/strong&gt; don't just memorize the four isolation levels. Run the &lt;strong&gt;same scenario in two query windows&lt;/strong&gt;. Watching Transaction A and Transaction B happen at the same time makes isolation, locking and concurrency click.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Connect it to your stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⚡ &lt;strong&gt;FastAPI&lt;/strong&gt; handles incoming concurrent requests&lt;/li&gt;
&lt;li&gt;🧪 &lt;strong&gt;SQLAlchemy&lt;/strong&gt; manages database transactions&lt;/li&gt;
&lt;li&gt;🐘 &lt;strong&gt;PostgreSQL&lt;/strong&gt; handles isolation, MVCC, locks and triggers&lt;/li&gt;
&lt;li&gt;📓 &lt;strong&gt;WAL&lt;/strong&gt; helps with durability&lt;/li&gt;
&lt;li&gt;🌐 &lt;strong&gt;Distributed architecture&lt;/strong&gt; introduces CAP trade-offs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Together, these concepts form a major part of backend engineering. 🏆&lt;/p&gt;




&lt;h3&gt;
  
  
  🙌 Enjoyed this post?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;❤️ Drop a reaction&lt;/li&gt;
&lt;li&gt;🦄 Save it for revision&lt;/li&gt;
&lt;li&gt;💬 Comment with questions, and I'll answer them!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy coding! 👩‍💻✨&lt;/p&gt;

&lt;h1&gt;
  
  
  postgres #sql #database #backend #concurrency
&lt;/h1&gt;

</description>
      <category>backend</category>
      <category>database</category>
      <category>postgres</category>
      <category>sql</category>
    </item>
    <item>
      <title>CQRS &amp; Event-Driven Thinking: Commands, Queries, Domain Events &amp; the Outbox Pattern Explained 📨</title>
      <dc:creator>Deepika Pusala</dc:creator>
      <pubDate>Thu, 12 Nov 2026 06:32:07 +0000</pubDate>
      <link>https://dev.to/deepika_pusala/cqrs-event-driven-thinking-commands-queries-domain-events-the-outbox-pattern-explained-45lg</link>
      <guid>https://dev.to/deepika_pusala/cqrs-event-driven-thinking-commands-queries-domain-events-the-outbox-pattern-explained-45lg</guid>
      <description>&lt;h1&gt;
  
  
  📨 CQRS &amp;amp; Event-Driven Thinking: Let's Learn from ZERO!
&lt;/h1&gt;

&lt;p&gt;These are important backend architecture concepts, especially with &lt;strong&gt;FastAPI, PostgreSQL, SQLAlchemy, repositories, services&lt;/strong&gt; and a social media app. We'll cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔀 What &lt;strong&gt;Command-Query Separation (CQS)&lt;/strong&gt; means&lt;/li&gt;
&lt;li&gt;🧩 What &lt;strong&gt;CQRS&lt;/strong&gt; is and how it differs from CQS&lt;/li&gt;
&lt;li&gt;⚔️ Commands vs queries&lt;/li&gt;
&lt;li&gt;📣 What &lt;strong&gt;domain events&lt;/strong&gt; are&lt;/li&gt;
&lt;li&gt;🌐 How &lt;strong&gt;event-driven architecture&lt;/strong&gt; works&lt;/li&gt;
&lt;li&gt;🏢 Real examples using an Instagram-like app&lt;/li&gt;
&lt;li&gt;✅ Advantages, disadvantages, use cases and interview questions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We'll start simple and gradually move towards real backend code. 🚀&lt;/p&gt;




&lt;h2&gt;
  
  
  1. 🤔 First, Understand the Problem
&lt;/h2&gt;

&lt;p&gt;You're building an Instagram-like app. A user can:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a post&lt;/li&gt;
&lt;li&gt;Like a post&lt;/li&gt;
&lt;li&gt;Comment on a post&lt;/li&gt;
&lt;li&gt;Follow another user&lt;/li&gt;
&lt;li&gt;View their home feed&lt;/li&gt;
&lt;li&gt;Search for posts&lt;/li&gt;
&lt;li&gt;View someone's profile&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All of these touch the database. But are they all doing the same &lt;em&gt;kind&lt;/em&gt; of work? &lt;strong&gt;No!&lt;/strong&gt; There are two fundamentally different kinds of operations:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Kind&lt;/th&gt;
&lt;th&gt;Examples&lt;/th&gt;
&lt;th&gt;Effect&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;✍️ &lt;strong&gt;Writing / changing data&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Create a post, like a post, delete a comment, follow a user&lt;/td&gt;
&lt;td&gt;Changes the database&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👀 &lt;strong&gt;Reading / retrieving data&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Get all posts, view comments, get a profile, search posts&lt;/td&gt;
&lt;td&gt;Retrieves information without changing it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This distinction is the foundation of Command-Query Separation.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. 🔀 Command-Query Separation (CQS)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Command:&lt;/strong&gt; an instruction to perform an action&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Query:&lt;/strong&gt; a request to retrieve information&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Separation:&lt;/strong&gt; keeping these two responsibilities apart&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;Command-Query Separation&lt;/strong&gt; means separating operations that &lt;strong&gt;change data&lt;/strong&gt; from operations that &lt;strong&gt;only read data&lt;/strong&gt;. A command changes the state of the system. A query returns information but does &lt;strong&gt;not&lt;/strong&gt; change the state.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;State&lt;/em&gt; means the current stored condition of your application. For example, a post:&lt;br&gt;
&lt;/p&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="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"caption"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"My first post"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"likes_count"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Its current state: the post has 10 likes.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.1 ✍️ Command example: like a post
&lt;/h3&gt;

&lt;p&gt;The frontend sends &lt;code&gt;POST /posts/101/like&lt;/code&gt;. The backend:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Identifies the logged-in user&lt;/li&gt;
&lt;li&gt;Checks the post exists&lt;/li&gt;
&lt;li&gt;Checks the user hasn't already liked it&lt;/li&gt;
&lt;li&gt;Inserts a like record&lt;/li&gt;
&lt;li&gt;Updates the like count, if your design stores one
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before:  { "id": 101, "likes_count": 10 }
After:   { "id": 101, "likes_count": 11 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The database state changed, so this is a &lt;strong&gt;Command&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.2 👀 Query example: get post details
&lt;/h3&gt;

&lt;p&gt;The frontend sends &lt;code&gt;GET /posts/101&lt;/code&gt;. The backend finds the post, retrieves its caption, author and like count, and returns:&lt;br&gt;
&lt;/p&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="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"caption"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"My first post"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"likes_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Did we change anything? &lt;strong&gt;No.&lt;/strong&gt; So this is a &lt;strong&gt;Query&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.3 📊 Visual comparison
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;✍️ COMMAND (write)&lt;/th&gt;
&lt;th&gt;👀 QUERY (read)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Changes application state&lt;/td&gt;
&lt;td&gt;Does not change application state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Create, Update, Delete, Like&lt;/td&gt;
&lt;td&gt;Get, List, Search, Retrieve&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 &lt;strong&gt;Command = change something. Query = ask for something.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2.4 📋 More examples
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Reason&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Create post&lt;/td&gt;
&lt;td&gt;Command&lt;/td&gt;
&lt;td&gt;Inserts data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Edit caption&lt;/td&gt;
&lt;td&gt;Command&lt;/td&gt;
&lt;td&gt;Updates data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delete post&lt;/td&gt;
&lt;td&gt;Command&lt;/td&gt;
&lt;td&gt;Removes data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Like post&lt;/td&gt;
&lt;td&gt;Command&lt;/td&gt;
&lt;td&gt;Changes like state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Follow user&lt;/td&gt;
&lt;td&gt;Command&lt;/td&gt;
&lt;td&gt;Changes follow relationship&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Get profile&lt;/td&gt;
&lt;td&gt;Query&lt;/td&gt;
&lt;td&gt;Reads data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Get feed&lt;/td&gt;
&lt;td&gt;Query&lt;/td&gt;
&lt;td&gt;Retrieves posts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Search users&lt;/td&gt;
&lt;td&gt;Query&lt;/td&gt;
&lt;td&gt;Reads matching records&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Get comments&lt;/td&gt;
&lt;td&gt;Query&lt;/td&gt;
&lt;td&gt;Retrieves comments&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;📝 A query can have internal work such as logging or caching, but its &lt;strong&gt;purpose&lt;/strong&gt; must not be to change the business data being queried.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  3. 🤷 Why Do We Need CQS?
&lt;/h2&gt;

&lt;p&gt;Imagine this function:&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;get_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;likes_count&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="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;likes_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What's wrong? It's named &lt;code&gt;get_post()&lt;/code&gt;, so we expect it to &lt;em&gt;retrieve&lt;/em&gt; a post. But it also &lt;strong&gt;changes the database&lt;/strong&gt;! Someone calling &lt;code&gt;get_post()&lt;/code&gt; might accidentally modify application data. A better design:&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;get_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;like_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;likes_count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;get_post()&lt;/code&gt; → &lt;strong&gt;Query&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;like_post()&lt;/code&gt; → &lt;strong&gt;Command&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;✅ &lt;strong&gt;Main benefit:&lt;/strong&gt; your code becomes easier to understand, maintain, test and debug.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  4. 🧩 What is CQRS?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;CQRS&lt;/strong&gt; = &lt;strong&gt;C&lt;/strong&gt;ommand &lt;strong&gt;Q&lt;/strong&gt;uery &lt;strong&gt;R&lt;/strong&gt;esponsibility &lt;strong&gt;S&lt;/strong&gt;egregation. It's an architectural pattern based on the same idea as CQS.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CQS&lt;/strong&gt; is a principle about separating read and write &lt;em&gt;operations&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CQRS&lt;/strong&gt; takes that further by letting the read and write sides have &lt;strong&gt;different models, responsibilities, and sometimes different data stores&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4.1 🏛️ Traditional architecture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Frontend (React)
     ▼
Backend API (FastAPI)
     ▼
PostgreSQL Database   ← same database for reading and writing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Writing means insert/update/delete, reading means select. This is completely valid and works very well for many apps. But if your app becomes extremely popular (1 million users, 10 million posts, millions of feed requests, thousands of new posts and likes every minute) the database must handle heavy reads and writes together. That's where CQRS can become useful.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.2 🧱 CQRS architecture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                    Frontend (React)
                 ┌────────┴────────┐
              Command            Query
          (write request)    (read request)
                 ▼                  ▼
         Command Handler      Query Handler
   (validates and processes   (processes read
            writes)              requests)
                 ▼                  ▼
          Write Model          Read Model
          (PostgreSQL)    (optimized for retrieval)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Conceptual. Read and write models may use the same database or different ones.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Write side:&lt;/strong&gt; handles commands, validates business rules, changes application state, saves data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Read side:&lt;/strong&gt; handles queries, retrieves information, returns data in a format suitable for the frontend, and can be optimized for fast reads.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.3 🗄️ Does CQRS require two databases?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;No!&lt;/strong&gt; A very important interview question. There are two common implementations:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option A: same database, separate models.&lt;/strong&gt; Command and query logic are separate, but they share the same underlying database. For example, the command handler uses the SQLAlchemy ORM to create a post, while the query handler uses optimized SQL to retrieve posts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option B: separate read and write databases.&lt;/strong&gt; Write model: PostgreSQL. Read model: Redis, Elasticsearch, or a separate PostgreSQL database. The read database may hold precomputed or denormalized information for faster retrieval. Use this when you need independent scaling or specialized read storage.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.4 📖 What is a Read Model?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 A &lt;strong&gt;read model&lt;/strong&gt; is a representation of data designed specifically for &lt;strong&gt;reading&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Suppose your database has:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;users&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;username&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;deepika&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;arjun&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;posts&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;user_id&lt;/th&gt;
&lt;th&gt;caption&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;My first post&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Weekend vibes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;likes&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;post_id&lt;/th&gt;
&lt;th&gt;user_id&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;To display a feed you may need to join several tables and count likes. Your frontend wants:&lt;br&gt;
&lt;/p&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="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;"post_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"deepika"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;"caption"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"My first post"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;"likes_count"&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="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This combined structure is a &lt;strong&gt;read model&lt;/strong&gt;. It's shaped for the frontend's needs rather than matching your original tables. Instead of repeating expensive joins and calculations on every feed request, you can maintain a read-optimized representation, for example a precomputed like count or a denormalized feed projection.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.5 ✍️ What is a Write Model?
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;write model&lt;/strong&gt; is the representation used to perform and &lt;strong&gt;enforce changes&lt;/strong&gt;. When a user likes a post, the write side must ensure the post exists, the user exists, the user hasn't already liked it, and the like is stored correctly. It maintains business rules and data integrity.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Write Model&lt;/th&gt;
&lt;th&gt;Read Model&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Handles changes&lt;/td&gt;
&lt;td&gt;Handles retrieval&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Processes commands&lt;/td&gt;
&lt;td&gt;Processes queries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enforces business rules&lt;/td&gt;
&lt;td&gt;Optimizes presentation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Focuses on consistency&lt;/td&gt;
&lt;td&gt;Focuses on efficient reads&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  4.6 📍 When should we use CQRS?
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Read-heavy apps:&lt;/strong&gt; millions of users frequently retrieve information&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complex queries:&lt;/strong&gt; many joins, aggregations and calculations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Different scaling needs:&lt;/strong&gt; read and write traffic scale independently&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Different data representations:&lt;/strong&gt; the frontend needs data structured differently from the normalized write database&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complex business workflows:&lt;/strong&gt; many operations and rules affect the same domain&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;When should you avoid it?&lt;/strong&gt; For a small CRUD app, CQRS adds unnecessary complexity. You'd maintain two models, command handlers, query handlers, event processing, synchronization, and error handling for failed projections. A normal layered FastAPI app is often enough.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ CQRS is an &lt;strong&gt;architectural choice&lt;/strong&gt;, not a requirement for every backend.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  5. 📣 What is Event-Driven Thinking?
&lt;/h2&gt;

&lt;p&gt;A user creates a post and the backend saves it. After that, several other things may need to happen:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The post should appear in the user's feed&lt;/li&gt;
&lt;li&gt;Followers may receive notifications&lt;/li&gt;
&lt;li&gt;The profile's post count may increase&lt;/li&gt;
&lt;li&gt;Analytics may record the new post&lt;/li&gt;
&lt;li&gt;An image-processing service may generate thumbnails&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Should the post-creation function directly perform all of these? It could, but if the notification service is slow or temporarily unavailable, should the user wait for it before their request finishes? Probably not. That's where &lt;strong&gt;event-driven thinking&lt;/strong&gt; helps.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.1 📌 What is an Event?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 An &lt;strong&gt;event&lt;/strong&gt; represents something that &lt;strong&gt;has already happened&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Examples: &lt;code&gt;UserRegistered&lt;/code&gt;, &lt;code&gt;PostCreated&lt;/code&gt;, &lt;code&gt;PostLiked&lt;/code&gt;, &lt;code&gt;CommentAdded&lt;/code&gt;, &lt;code&gt;UserFollowed&lt;/code&gt;, &lt;code&gt;OrderPlaced&lt;/code&gt;, &lt;code&gt;PaymentCompleted&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Notice the &lt;strong&gt;past tense&lt;/strong&gt;. An event is a completed fact. &lt;code&gt;PostCreated&lt;/code&gt; means a post &lt;em&gt;has already been&lt;/em&gt; created. It doesn't mean someone is &lt;em&gt;requesting&lt;/em&gt; creation. That distinction is extremely important.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.2 ⚔️ Command vs Event
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;✍️ Command&lt;/th&gt;
&lt;th&gt;📣 Event&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;CreatePost&lt;/code&gt;: &lt;em&gt;"Please create a new post."&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;PostCreated&lt;/code&gt;: &lt;em&gt;"A new post has been successfully created."&lt;/em&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;An &lt;strong&gt;instruction&lt;/strong&gt; that requests something happen&lt;/td&gt;
&lt;td&gt;A &lt;strong&gt;fact&lt;/strong&gt; that announces something happened&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RegisterUser&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;UserRegistered&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CreatePost&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PostCreated&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LikePost&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PostLiked&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AddComment&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CommentAdded&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FollowUser&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;UserFollowed&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PlaceOrder&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;OrderPlaced&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MakePayment&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PaymentCompleted&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 &lt;strong&gt;Command = request to do something. Event = notification that something has happened.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  5.3 🌐 How does event-driven architecture work?
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. User creates a post          → React frontend
        ▼
2. CreatePost command           → backend receives the instruction
        ▼
3. Command Handler              → validates and saves the post
        ▼
4. PostCreated event            → a fact is published after successful creation
        ▼
5. Event Bus / Event Broker     → distributes the event to interested consumers
        ├── 🔔 Notifications
        ├── 🖼️ Media
        └── 📊 Analytics
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One event can trigger &lt;strong&gt;several independent reactions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A. Event Producer:&lt;/strong&gt; the component that creates and publishes an event. Here, the post creation service saves the post and publishes &lt;code&gt;PostCreated&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;B. Event:&lt;/strong&gt; carries information about what happened:&lt;br&gt;
&lt;/p&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="nl"&gt;"event_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"evt_001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"event_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"PostCreated"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"post_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"2026-11-12T10:30:00Z"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(An illustrative payload.)&lt;/em&gt; It tells other components which event occurred, which post was created, which user created it and when. An event should contain enough information for consumers to do their work, without unnecessarily exposing private data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C. Event Bus / Event Broker:&lt;/strong&gt; a communication system that receives events from producers and delivers them to interested consumers. Technologies include &lt;strong&gt;RabbitMQ, Apache Kafka, Redis Streams, AWS EventBridge&lt;/strong&gt;. For a small app you can even use a simple &lt;strong&gt;in-process event dispatcher&lt;/strong&gt; with no broker at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;D. Event Consumer:&lt;/strong&gt; a component that receives and reacts to an event. When &lt;code&gt;PostCreated&lt;/code&gt; occurs, the notification consumer sends notifications, the analytics consumer records the activity, and the media consumer processes images. Each has its own responsibility.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🔗 The event producer doesn't need to know the internal implementation of every consumer, which &lt;strong&gt;reduces coupling&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  6. 🏷️ What are Domain Events?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 A &lt;strong&gt;domain event&lt;/strong&gt; is an event that represents something &lt;strong&gt;meaningful that happened in the application's business domain&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A &lt;strong&gt;domain&lt;/strong&gt; is the business area or problem your app is built to handle:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Application&lt;/th&gt;
&lt;th&gt;Domain&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Instagram-like app&lt;/td&gt;
&lt;td&gt;Social networking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Banking app&lt;/td&gt;
&lt;td&gt;Banking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;E-commerce app&lt;/td&gt;
&lt;td&gt;Online shopping&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hospital app&lt;/td&gt;
&lt;td&gt;Healthcare management&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Food delivery app&lt;/td&gt;
&lt;td&gt;Food ordering and delivery&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In a social media app the domain includes users, posts, comments, likes, follows and notifications. When a user follows another user and the operation succeeds, we can represent that important business fact as the domain event &lt;code&gt;UserFollowed&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  6.1 📋 Examples of domain events
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Social media:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Domain Event&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UserRegistered&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A new account was registered&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ProfileUpdated&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A profile was updated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PostCreated&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A new post was created&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PostDeleted&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A post was deleted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PostLiked&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A user liked a post&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CommentAdded&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A comment was added&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UserFollowed&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A user followed another user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UserUnfollowed&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A user unfollowed another user&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;E-commerce:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Domain Event&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ProductAddedToCart&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A product was added&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OrderPlaced&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;An order was placed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PaymentCompleted&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Payment succeeded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OrderCancelled&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;An order was cancelled&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ItemShipped&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;An item was shipped&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  6.2 ⚖️ Domain event vs command vs query
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Command&lt;/td&gt;
&lt;td&gt;Request to perform an action&lt;/td&gt;
&lt;td&gt;&lt;code&gt;LikePost&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Query&lt;/td&gt;
&lt;td&gt;Request to retrieve data&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GetPost&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Domain Event&lt;/td&gt;
&lt;td&gt;Something meaningful happened&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PostLiked&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A complete sequence:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A user clicks Like&lt;/li&gt;
&lt;li&gt;The backend receives &lt;code&gt;LikePost&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The backend checks the rules&lt;/li&gt;
&lt;li&gt;The backend saves the like&lt;/li&gt;
&lt;li&gt;The backend generates &lt;code&gt;PostLiked&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Other components react to &lt;code&gt;PostLiked&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LikePost (Command)
     ▼
Business logic + database write
     ▼
PostLiked (Domain Event)
     ▼
Event consumers: 🔔 Notification · 📊 Analytics
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command &lt;strong&gt;asks&lt;/strong&gt; the system to do something. The event &lt;strong&gt;records&lt;/strong&gt; that the system successfully did it.&lt;/p&gt;

&lt;h3&gt;
  
  
  6.3 🤔 Are domain events always published to Kafka or RabbitMQ?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;No.&lt;/strong&gt; A domain event is a &lt;strong&gt;concept&lt;/strong&gt;. It can exist entirely inside your application. For example, your Python app can create a &lt;code&gt;PostCreated&lt;/code&gt; event object and pass it to an internal event dispatcher. You only need Kafka, RabbitMQ or another broker when your architecture requires the capabilities they provide.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. ⏳ Synchronous vs Asynchronous Event Processing
&lt;/h2&gt;

&lt;h3&gt;
  
  
  7.1 🐢 Synchronous processing
&lt;/h3&gt;

&lt;p&gt;The caller &lt;strong&gt;waits&lt;/strong&gt; for the operation to finish before continuing:&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;create_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;save_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;send_notification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;update_analytics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Save post&lt;/li&gt;
&lt;li&gt;Wait for notification&lt;/li&gt;
&lt;li&gt;Wait for analytics&lt;/li&gt;
&lt;li&gt;Return response&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If notification takes 3 seconds and analytics takes 2, the request may take about &lt;strong&gt;5 seconds longer&lt;/strong&gt; than the database save itself. The user waits for all of it.&lt;/p&gt;

&lt;h3&gt;
  
  
  7.2 🐇 Asynchronous event processing
&lt;/h3&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;create_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;save_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;publish_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;"PostCreated"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"post_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Event consumers process their tasks independently (notifications, analytics, image processing), and the request doesn't have to wait for them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Synchronous:   Save post → Send notification → Update analytics → Return response (after all)

Asynchronous:  Save post → Publish event → Return response
                              ├── Notification
                              └── Analytics   (processed independently)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;📝 Asynchronous doesn't automatically mean a separate server or message broker. Python background tasks, queues and distributed brokers are different implementation options. For reliable processing across services, a &lt;strong&gt;durable message broker&lt;/strong&gt; is often preferable to an in-memory background task.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  7.3 🕐 What is Eventual Consistency?
&lt;/h3&gt;

&lt;p&gt;A user creates a post. The write database saves it immediately, but your separate read database or feed projection may take a moment to receive and process &lt;code&gt;PostCreated&lt;/code&gt;. For a brief moment the write database says the post exists, while the read database hasn't caught up. After the event is processed, both agree.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Time T1 (write completed)   Write DB: Post exists (ID 101)   Read DB: Post not yet visible
Time T2 (event processed)   Write DB: Post exists (ID 101)   Read DB: Post exists (ID 101)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;Eventual consistency:&lt;/strong&gt; the system may temporarily show slightly outdated information, but if processing succeeds and continues, the read side &lt;strong&gt;eventually catches up&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For analytics, recommendation updates and notification delivery, a small delay is often acceptable. But for critical operations such as checking an account balance before a withdrawal, you generally need &lt;strong&gt;strong consistency&lt;/strong&gt; at the point of decision.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. 💥 What Happens If Event Processing Fails?
&lt;/h2&gt;

&lt;p&gt;Your app saves a post in PostgreSQL, then tries to publish &lt;code&gt;PostCreated&lt;/code&gt; to RabbitMQ, but RabbitMQ is temporarily down. The post exists, yet the event was never published, so the notification service may never learn about it. That's a &lt;strong&gt;reliability problem&lt;/strong&gt;. One important solution is the &lt;strong&gt;Transactional Outbox Pattern&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  8.1 📤 Transactional Outbox Pattern
&lt;/h3&gt;

&lt;p&gt;Instead of saving business data and publishing the event as two unrelated operations, save &lt;strong&gt;both the business data and an event record in the same database transaction&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;posts&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;caption&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;My first post&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;outbox_events&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;event_type&lt;/th&gt;
&lt;th&gt;payload&lt;/th&gt;
&lt;th&gt;published&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PostCreated&lt;/td&gt;
&lt;td&gt;&lt;code&gt;post_id:101&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;false&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start a database transaction&lt;/li&gt;
&lt;li&gt;Insert the post&lt;/li&gt;
&lt;li&gt;Insert the &lt;code&gt;PostCreated&lt;/code&gt; event into the outbox table&lt;/li&gt;
&lt;li&gt;Commit the transaction&lt;/li&gt;
&lt;li&gt;A background worker reads unpublished events&lt;/li&gt;
&lt;li&gt;The worker publishes them to the broker&lt;/li&gt;
&lt;li&gt;The worker marks them as published after successful delivery
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BEGIN TRANSACTION
  Insert post
  Insert event
COMMIT              ← both records saved together
     ▼
Background Worker
     ▼
Message Broker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even if the broker is down, the event remains safely stored in PostgreSQL and can be published later.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Caveat:&lt;/strong&gt; a worker can publish an event and crash before marking it as published, so the event may be delivered &lt;strong&gt;again&lt;/strong&gt;. That's why consumers should be &lt;strong&gt;idempotent&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  8.2 🔁 What is Idempotency?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;Idempotency&lt;/strong&gt; means performing the same operation multiple times produces the same intended result as performing it once.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Suppose &lt;code&gt;PostLiked&lt;/code&gt; is delivered twice. If your consumer blindly increments the like count each time, the count goes up twice. That's wrong. A safer design:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Store processed event IDs&lt;/li&gt;
&lt;li&gt;Check whether an event was already processed&lt;/li&gt;
&lt;li&gt;If yes, skip it&lt;/li&gt;
&lt;li&gt;If not, process it and record its ID&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  9. ⚡ How Does This Fit into FastAPI Architecture?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  9.1 🥞 Traditional layered architecture
&lt;/h3&gt;

&lt;p&gt;For &lt;code&gt;POST /posts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;frontend → FastAPI Router → Post Service → Post Repository → PostgreSQL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The router receives the request, the service implements business logic, and the repository handles database operations.&lt;/p&gt;

&lt;h3&gt;
  
  
  9.2 🧩 Introducing CQRS
&lt;/h3&gt;

&lt;p&gt;Separate the write and read responsibilities:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                     FastAPI
              ┌─────────┴─────────┐
        Command Router       Query Router
              ▼                   ▼
       Command Handler       Query Handler
              ▼                   ▼
       Write Repository      Read Repository
              ▼                   ▼
          Database            Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Command handler:&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;CreatePostCommand&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;caption&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;caption&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_create_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;caption&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Query handler:&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;handle_get_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_by_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Simplified examples, not a complete production implementation.)&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;CreatePostCommand&lt;/code&gt; represents the instruction&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;handle_create_post()&lt;/code&gt; processes the write&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;handle_get_post()&lt;/code&gt; retrieves information&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can have separate command and query handlers even when both use the same SQLAlchemy session and PostgreSQL database.&lt;/p&gt;

&lt;h3&gt;
  
  
  9.3 📣 Introducing domain events
&lt;/h3&gt;

&lt;p&gt;Extend the command handler:&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;handle_create_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event_publisher&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;caption&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"event_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"PostCreated"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"post_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;event_publisher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CreatePost Command
     ▼
Command Handler
     ▼
Post Repository
     ▼
PostgreSQL
     ▼
PostCreated Event
     ▼
Event Publisher
     ▼
Event Consumers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ For production reliability, you'd normally use the &lt;strong&gt;transactional outbox pattern&lt;/strong&gt; instead of relying on a direct database-write-then-publish sequence.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  10. 🆚 Domain Events vs Integration Events
&lt;/h2&gt;

&lt;p&gt;Related, but not identical.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Domain event:&lt;/strong&gt; represents an important business fact &lt;strong&gt;inside a particular domain&lt;/strong&gt;. It might be used by your app's internal business logic.&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="nc"&gt;PostCreated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&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;p&gt;&lt;strong&gt;Integration event:&lt;/strong&gt; communicates something that happened to &lt;strong&gt;another system or independent service&lt;/strong&gt;. It might be published through RabbitMQ or Kafka so another service can consume it.&lt;br&gt;
&lt;/p&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="nl"&gt;"event_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"PostCreated"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"post_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Domain Event&lt;/th&gt;
&lt;th&gt;Integration Event&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Internal to a domain&lt;/td&gt;
&lt;td&gt;Shared across service boundaries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Represents business facts&lt;/td&gt;
&lt;td&gt;Communicates business facts to other systems&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can be an internal Python object&lt;/td&gt;
&lt;td&gt;Usually has a serialized message format&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;May never leave the application&lt;/td&gt;
&lt;td&gt;Intended for external consumers&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A domain event can lead to an integration event, but not every domain event needs to be published externally.&lt;/p&gt;




&lt;h2&gt;
  
  
  11. ⚔️ CQRS vs Event-Driven Architecture vs Event Sourcing
&lt;/h2&gt;

&lt;p&gt;These three are often discussed together but solve different problems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🅰️ CQRS.&lt;/strong&gt; &lt;em&gt;Separate reading and writing responsibilities.&lt;/em&gt; &lt;code&gt;CreatePost&lt;/code&gt; → command, &lt;code&gt;GetPost&lt;/code&gt; → query. Focus: how your app handles reads and writes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🅱️ Event-Driven Architecture (EDA).&lt;/strong&gt; &lt;em&gt;Components communicate and react to events.&lt;/em&gt; &lt;code&gt;PostCreated&lt;/code&gt; triggers notifications, analytics and image processing. Focus: how components communicate and respond to changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🅲 Event Sourcing.&lt;/strong&gt; A &lt;em&gt;different&lt;/em&gt; concept. Don't confuse it with EDA. Normally your database stores the &lt;strong&gt;current state&lt;/strong&gt;:&lt;br&gt;
&lt;/p&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="nl"&gt;"account_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"balance"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;700&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With event sourcing, instead of treating current state as the primary record, you store the &lt;strong&gt;sequence of events that produced it&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;th&gt;Amount&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AccountCreated&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MoneyDeposited&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;+1000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MoneyWithdrawn&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;-200&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MoneyWithdrawn&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;-100&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The balance is calculated by replaying the events: 1000 − 200 − 100 = &lt;strong&gt;700&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Traditional database:  Current balance = ₹700
Event sourcing:        Deposited ₹1,000 → Withdrawn ₹200 → Withdrawn ₹100 → Current balance = ₹700
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why use it?&lt;/strong&gt; Complete history of changes, ability to reconstruct previous states, audit trails, and understanding exactly how a value changed over time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But it adds complexity:&lt;/strong&gt; event versioning, event replay, data migration, handling duplicate events and maintaining projections. You don't need event sourcing just because you're using events.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;Main responsibility&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CQS&lt;/td&gt;
&lt;td&gt;Separate reads and writes at the operation level&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CQRS&lt;/td&gt;
&lt;td&gt;Separate read and write models/responsibilities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Event-Driven Architecture&lt;/td&gt;
&lt;td&gt;Communicate through events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Domain Events&lt;/td&gt;
&lt;td&gt;Represent important business facts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Event Sourcing&lt;/td&gt;
&lt;td&gt;Store state changes as an event history&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;🎯 &lt;strong&gt;Very important:&lt;/strong&gt; CQRS and EDA can be used together, but &lt;strong&gt;neither requires Event Sourcing&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  12. 🧵 Practical Example: A Social Media App
&lt;/h2&gt;

&lt;p&gt;Imagine Deepika likes Arjun's post.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: the frontend sends a command&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /posts/101/like
Authorization: Bearer access_token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: FastAPI receives the request&lt;/strong&gt;&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="nd"&gt;@router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/posts/{post_id}/like"&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;like_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_current_user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;like_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&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;p&gt;&lt;strong&gt;Step 3: the service processes the command.&lt;/strong&gt; It checks the post exists, checks whether the user already liked it, applies business rules, and creates the like record.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: the database saves the change&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;likes
id | post_id | user_id
1  | 101     | 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 5: a domain event is generated&lt;/strong&gt;&lt;br&gt;
&lt;/p&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="nl"&gt;"event_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"PostLiked"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"post_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 6: event consumers react.&lt;/strong&gt; The notification service informs the post owner, analytics records the interaction, and a read projection updates the like count, if the app maintains one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 7: the user retrieves the post&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /posts/101
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a &lt;strong&gt;Query&lt;/strong&gt;. The query handler returns:&lt;br&gt;
&lt;/p&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="nl"&gt;"post_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"caption"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"My first post"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"likes_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"liked_by_me"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This one example demonstrates CQS, CQRS, domain events, event-driven architecture, event consumers and eventual consistency.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📝 If &lt;code&gt;liked_by_me&lt;/code&gt; must update &lt;strong&gt;immediately&lt;/strong&gt;, read it from the authoritative write-side data, or otherwise ensure the response reflects the successful write. Don't assume an asynchronously updated read model is immediately current.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  13. 📖 Important Terms
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Term&lt;/th&gt;
&lt;th&gt;Beginner-friendly meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Command Handler&lt;/td&gt;
&lt;td&gt;Processes a command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Query Handler&lt;/td&gt;
&lt;td&gt;Processes a query&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write Model&lt;/td&gt;
&lt;td&gt;Handles data modifications&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read Model&lt;/td&gt;
&lt;td&gt;Represents data for retrieval&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Event Producer&lt;/td&gt;
&lt;td&gt;Creates and publishes events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Event Consumer&lt;/td&gt;
&lt;td&gt;Receives and processes events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Event Bus&lt;/td&gt;
&lt;td&gt;Distributes events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Event Broker&lt;/td&gt;
&lt;td&gt;Messaging infrastructure for event delivery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Domain Event&lt;/td&gt;
&lt;td&gt;Important business occurrence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Event Payload&lt;/td&gt;
&lt;td&gt;Data contained in an event&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Eventual Consistency&lt;/td&gt;
&lt;td&gt;Different data representations become consistent over time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Idempotency&lt;/td&gt;
&lt;td&gt;Repeating an operation doesn't produce duplicate effects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Event Sourcing&lt;/td&gt;
&lt;td&gt;Stores events as the primary history of state changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transactional Outbox&lt;/td&gt;
&lt;td&gt;Reliably stores events alongside database changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Message Queue&lt;/td&gt;
&lt;td&gt;Holds messages until consumers process them&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  14. 🧭 When Do These Concepts Work Together?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Useful concept&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Separating &lt;code&gt;get_post()&lt;/code&gt; and &lt;code&gt;create_post()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;CQS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Different read and write models&lt;/td&gt;
&lt;td&gt;CQRS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sending notifications after a post is created&lt;/td&gt;
&lt;td&gt;Event-driven architecture&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Representing a successful follow action&lt;/td&gt;
&lt;td&gt;Domain events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Updating a read database asynchronously&lt;/td&gt;
&lt;td&gt;CQRS + events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Keeping event publication reliable&lt;/td&gt;
&lt;td&gt;Transactional outbox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Preventing duplicate message processing&lt;/td&gt;
&lt;td&gt;Idempotency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Keeping complete historical state changes&lt;/td&gt;
&lt;td&gt;Event sourcing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  15. 🎤 Interview Questions and Answers
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q1. What is Command-Query Separation?&lt;/strong&gt;&lt;br&gt;
A design principle that separates operations into Commands, which modify application state, and Queries, which retrieve information without modifying business state.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q2. What is CQRS?&lt;/strong&gt;&lt;br&gt;
Command Query Responsibility Segregation: an architectural pattern that separates read and write responsibilities, potentially using different models and data stores.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q3. What's the difference between CQS and CQRS?&lt;/strong&gt;&lt;br&gt;
CQS is a principle applied at the operation or method level. CQRS is an architectural pattern that applies the separation more broadly to the application's read and write models.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q4. What is a Domain Event?&lt;/strong&gt;&lt;br&gt;
Something important that has already happened in the business domain, such as &lt;code&gt;PostCreated&lt;/code&gt;, &lt;code&gt;PaymentCompleted&lt;/code&gt; or &lt;code&gt;UserRegistered&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q5. What's the difference between a Command and an Event?&lt;/strong&gt;&lt;br&gt;
A Command requests an action. An Event represents a fact that the action has happened.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q6. What is Event-Driven Architecture?&lt;/strong&gt;&lt;br&gt;
An approach in which components communicate by publishing and consuming events. It helps decouple components and allows independent processing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q7. What is an Event Broker?&lt;/strong&gt;&lt;br&gt;
Infrastructure that receives and distributes messages between producers and consumers, such as RabbitMQ and Kafka.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q8. What is Eventual Consistency?&lt;/strong&gt;&lt;br&gt;
Different data representations may temporarily contain different information but eventually become consistent after updates are processed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q9. What is the Transactional Outbox Pattern?&lt;/strong&gt;&lt;br&gt;
A reliability pattern that stores business data and the corresponding event record in the same database transaction. A background worker later publishes the event.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q10. Does CQRS require two databases?&lt;/strong&gt;&lt;br&gt;
No. CQRS can use the same database with separate read and write models. Separate databases are optional.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q11. Is Event-Driven Architecture the same as Event Sourcing?&lt;/strong&gt;&lt;br&gt;
No. EDA uses events for communication between components. Event sourcing stores events as the primary record of state changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q12. What are the disadvantages of CQRS?&lt;/strong&gt;&lt;br&gt;
Architectural complexity, additional code, synchronization challenges, potential eventual consistency, and more difficult debugging.&lt;/p&gt;


&lt;h2&gt;
  
  
  16. 🧪 Test Your Understanding!
&lt;/h2&gt;

&lt;p&gt;Try these before peeking at the answers! ❤️&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;A user requests their profile details. What is this?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Command&lt;/li&gt;
&lt;li&gt;B) Query&lt;/li&gt;
&lt;li&gt;C) Domain Event&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;A user clicks the Like button. What is the incoming instruction?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) &lt;code&gt;PostLiked&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;B) &lt;code&gt;LikePost&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;C) &lt;code&gt;GetPost&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The backend successfully saves a post. What event can represent this?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) &lt;code&gt;CreatePost&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;B) &lt;code&gt;GetPost&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;C) &lt;code&gt;PostCreated&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;A post creation event triggers notifications and analytics independently. Which architecture is this?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Event-Driven Architecture&lt;/li&gt;
&lt;li&gt;B) Event Sourcing&lt;/li&gt;
&lt;li&gt;C) Normalization&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Does CQRS always require two databases?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Yes&lt;/li&gt;
&lt;li&gt;B) No&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;What helps reliably publish events after a database transaction?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) B-tree index&lt;/li&gt;
&lt;li&gt;B) Transactional Outbox&lt;/li&gt;
&lt;li&gt;C) JWT&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;What does Eventual Consistency mean?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Data never changes&lt;/li&gt;
&lt;li&gt;B) All systems update at exactly the same time&lt;/li&gt;
&lt;li&gt;C) Data representations may temporarily differ but eventually catch up&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;/p&gt;
  👀 Click to reveal answers
  &lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;B&lt;/strong&gt;: Query. It reads data without changing it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;B&lt;/strong&gt;: &lt;code&gt;LikePost&lt;/code&gt; is the command. &lt;code&gt;PostLiked&lt;/code&gt; is the event that follows&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C&lt;/strong&gt;: &lt;code&gt;PostCreated&lt;/code&gt;. Events are past-tense facts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A&lt;/strong&gt;: Event-Driven Architecture&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;B&lt;/strong&gt;: No. The same database with separate models is a valid CQRS implementation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;B&lt;/strong&gt;: Transactional Outbox&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C&lt;/strong&gt;: Representations may temporarily differ but eventually catch up&lt;/li&gt;
&lt;/ol&gt;



&lt;p&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  17. 🏁 Remember These Five Things
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;🔀 &lt;strong&gt;CQS&lt;/strong&gt; = separate reading and writing operations&lt;/li&gt;
&lt;li&gt;🧩 &lt;strong&gt;CQRS&lt;/strong&gt; = separate read and write responsibilities/models&lt;/li&gt;
&lt;li&gt;✍️ &lt;strong&gt;Command&lt;/strong&gt; = an instruction to perform an action&lt;/li&gt;
&lt;li&gt;📣 &lt;strong&gt;Domain Event&lt;/strong&gt; = something meaningful that happened&lt;/li&gt;
&lt;li&gt;🌐 &lt;strong&gt;EDA&lt;/strong&gt; = components react to events and communicate through them&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;If this post helped you, drop a ❤️ or 🦄 and share it with a friend learning backend architecture. Happy coding! 👩‍💻✨&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>backend</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>PostgreSQL Query Optimization: Indexes, EXPLAIN ANALYZE, B-tree vs GIN vs GiST &amp; the N+1 Problem 🚀</title>
      <dc:creator>Deepika Pusala</dc:creator>
      <pubDate>Thu, 12 Nov 2026 06:26:09 +0000</pubDate>
      <link>https://dev.to/deepika_pusala/postgresql-query-optimization-indexes-explain-analyze-b-tree-vs-gin-vs-gist-the-n1-problem-3jg5</link>
      <guid>https://dev.to/deepika_pusala/postgresql-query-optimization-indexes-explain-analyze-b-tree-vs-gin-vs-gist-the-n1-problem-3jg5</guid>
      <description>&lt;h1&gt;
  
  
  🚀 PostgreSQL Query Optimization: Let's Learn from ZERO!
&lt;/h1&gt;

&lt;p&gt;Imagine your social media app has only &lt;strong&gt;30 posts&lt;/strong&gt; today. Everything is fast. Now imagine it grows to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;👥 10,000 users&lt;/li&gt;
&lt;li&gt;📝 1 million posts&lt;/li&gt;
&lt;li&gt;❤️ 10 million likes&lt;/li&gt;
&lt;li&gt;💬 Millions of comments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Suddenly some API requests become slow, the database takes longer to retrieve data, and your server does more work than necessary.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;Query optimization&lt;/strong&gt; is about making database operations &lt;strong&gt;faster and more efficient without changing the correctness of the results&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We'll learn everything from zero, using PostgreSQL and examples from a social-feed project. 🎯&lt;/p&gt;




&lt;h2&gt;
  
  
  1. 🤔 What Does Query Optimization Mean?
&lt;/h2&gt;

&lt;p&gt;Take a &lt;code&gt;posts&lt;/code&gt; table:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;user_id&lt;/th&gt;
&lt;th&gt;caption&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;My first post&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;Beautiful day&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;Weekend vibes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;Coding time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;Hello world&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You want all posts by user 101. But &lt;strong&gt;how&lt;/strong&gt; does the database find them?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It can inspect &lt;strong&gt;every row&lt;/strong&gt; one by one&lt;/li&gt;
&lt;li&gt;It can use an &lt;strong&gt;index&lt;/strong&gt; to find matching rows more directly&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The second approach can be much faster for suitable queries. That's where indexing and query optimization come in.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.1 📨 What is a database query?
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;query&lt;/strong&gt; is simply a request sent to the database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                      &lt;span class="c1"&gt;-- get all users&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;        &lt;span class="c1"&gt;-- get post 10&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;likes&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;post_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;-- get all likes of post 25&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every time your FastAPI backend needs data, it may execute queries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User opens the app
      ▼
React frontend requests /feed
      ▼
FastAPI receives the request
      ▼
Service layer executes a database query
      ▼
PostgreSQL retrieves matching posts
      ▼
FastAPI returns a JSON response
      ▼
React displays posts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the database query is slow, the API response may also become slow.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Query optimization isn't only about writing shorter SQL. It's about &lt;strong&gt;reducing unnecessary database work&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  2. 📚 What is a Database Index?
&lt;/h2&gt;

&lt;p&gt;This is the foundation of the whole topic. Imagine a &lt;strong&gt;1,000-page textbook&lt;/strong&gt; and you want the topic "Normalization".&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Without an index:&lt;/strong&gt; you start at page 1, then page 2, page 3... until you find it on page 650. That's like a &lt;strong&gt;Sequential Scan&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;With an index:&lt;/strong&gt; the back of the book says &lt;code&gt;Normalization → Page 650&lt;/code&gt;. You go straight there. That's like an &lt;strong&gt;indexed lookup&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Normalization  → Page 650
Transactions   → Page 720
Primary Key    → Page 100
SQL Joins      → Page 250
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;📌 A database &lt;strong&gt;index&lt;/strong&gt; is an additional data structure that helps PostgreSQL locate rows more efficiently.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_posts_user_id&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="nf"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&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;p&gt;This creates an index on &lt;code&gt;user_id&lt;/code&gt;. Now this query can potentially use it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Without an index:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Check row 1 → Check row 2 → Check row 3 → Check row 4 → ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;With an index:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Search the index
   ▼
Find user_id = 101
   ▼
Locate matching row positions
   ▼
Retrieve matching rows
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🧠 Important things to remember
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;An index is &lt;strong&gt;separate&lt;/strong&gt; from the actual table data&lt;/li&gt;
&lt;li&gt;An index helps speed up certain &lt;code&gt;SELECT&lt;/code&gt; queries&lt;/li&gt;
&lt;li&gt;An index consumes &lt;strong&gt;additional storage&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;INSERT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt; and &lt;code&gt;DELETE&lt;/code&gt; may become &lt;strong&gt;slower&lt;/strong&gt; because indexes also need maintenance&lt;/li&gt;
&lt;li&gt;Not every query benefits from an index&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;🏆 &lt;strong&gt;Golden rule:&lt;/strong&gt; index columns based on &lt;strong&gt;actual query patterns&lt;/strong&gt;, not randomly.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  3. 🔍 EXPLAIN: Understanding How PostgreSQL Executes Queries
&lt;/h2&gt;

&lt;p&gt;You write a query, but how do you know PostgreSQL runs it efficiently? You can't assume a query is fast just because it looks simple. PostgreSQL provides &lt;code&gt;EXPLAIN&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.1 🤔 What is EXPLAIN?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;code&gt;EXPLAIN&lt;/code&gt; shows the &lt;strong&gt;query execution plan&lt;/strong&gt;: how PostgreSQL &lt;em&gt;plans&lt;/em&gt; to retrieve the requested data.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;EXPLAIN&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Possible output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Seq Scan on posts
  Filter: (user_id = 101)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;A. Seq Scan.&lt;/strong&gt; &lt;em&gt;Seq&lt;/em&gt; means sequential. PostgreSQL reads through the table's rows to find matching records.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;posts table
Row 1 → Check
Row 2 → Check
Row 3 → Check
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Is Seq Scan bad? &lt;strong&gt;NO!&lt;/strong&gt; This is extremely important. If your table has only 10 rows, using an index might actually cost &lt;em&gt;more&lt;/em&gt; than checking all 10. PostgreSQL intelligently chooses between execution strategies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;B. Filter.&lt;/strong&gt; &lt;code&gt;Filter: (user_id = 101)&lt;/code&gt; means PostgreSQL keeps rows satisfying &lt;code&gt;user_id = 101&lt;/code&gt; and discards the others.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.2 📋 Common scan types
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scan type&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Seq Scan&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reads table rows sequentially&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Index Scan&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Uses an index and retrieves table rows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Index Only Scan&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Can retrieve required data directly from the index&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bitmap Index Scan&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Uses an index to identify matching row locations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bitmap Heap Scan&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Retrieves matching table rows using a bitmap&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Sequential Scan:&lt;/strong&gt; &lt;code&gt;Seq Scan on posts&lt;/code&gt; reads the table and checks rows&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Index Scan:&lt;/strong&gt; &lt;code&gt;Index Scan using idx_posts_user_id on posts&lt;/code&gt; uses an index to find matching rows, then retrieves the actual table rows&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Index Only Scan:&lt;/strong&gt; if the index contains all the columns the query needs, PostgreSQL may answer without visiting the table's data pages. It can still need visibility checks depending on the visibility map&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bitmap Index Scan:&lt;/strong&gt; useful when many matching rows must be retrieved. PostgreSQL first identifies matching row locations using the index, then fetches table pages efficiently&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  3.3 🗺️ What is a query execution plan?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 A &lt;strong&gt;query execution plan&lt;/strong&gt; is the sequence of operations PostgreSQL chooses to execute your query.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Index Scan using idx_posts_user_id
  on posts
  Index Cond: (user_id = 101)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Index Cond&lt;/code&gt;: the condition used to &lt;strong&gt;search through the index&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Filter&lt;/code&gt;: an additional condition applied to rows &lt;strong&gt;after&lt;/strong&gt; they are retrieved&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;is_deleted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PostgreSQL might use an index on &lt;code&gt;user_id&lt;/code&gt;, then filter out deleted posts.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.4 💰 What does "cost" mean?
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Index Scan (cost=0.15..8.20 rows=5 width=120)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0.15&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Estimated &lt;strong&gt;startup&lt;/strong&gt; cost&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;8.20&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Estimated &lt;strong&gt;total&lt;/strong&gt; cost&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rows=5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Estimated number of returned rows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;width=120&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Estimated average row width in bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ PostgreSQL's cost is an &lt;strong&gt;internal estimate&lt;/strong&gt;, not milliseconds. It helps PostgreSQL compare different possible plans.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  4. ⏱️ EXPLAIN ANALYZE: Measuring Actual Execution
&lt;/h2&gt;

&lt;p&gt;A very common interview question: what's the difference between &lt;code&gt;EXPLAIN&lt;/code&gt; and &lt;code&gt;EXPLAIN ANALYZE&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EXPLAIN&lt;/strong&gt; shows the &lt;strong&gt;estimated&lt;/strong&gt; plan and doesn't normally execute the query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;EXPLAIN&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;EXPLAIN ANALYZE&lt;/strong&gt; &lt;strong&gt;actually executes&lt;/strong&gt; the query and shows the plan along with measured statistics:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;EXPLAIN&lt;/span&gt; &lt;span class="k"&gt;ANALYZE&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Index Scan using idx_posts_user_id
  on posts
  (cost=0.15..8.20 rows=5 width=120)
  (actual time=0.025..0.041 rows=5 loops=1)

Planning Time: 0.120 ms
Execution Time: 0.065 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Term&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;actual time&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Measured execution time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rows&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Actual number of rows produced&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;loops&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Number of times the operation executed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Planning Time&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Time spent planning the query&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Execution Time&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Time spent executing the query&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;🎯 &lt;strong&gt;Interview important:&lt;/strong&gt; &lt;code&gt;EXPLAIN&lt;/code&gt; = what PostgreSQL &lt;em&gt;estimates&lt;/em&gt; it will do. &lt;code&gt;EXPLAIN ANALYZE&lt;/code&gt; = what PostgreSQL &lt;em&gt;actually did&lt;/em&gt;, with measured statistics.&lt;/p&gt;

&lt;p&gt;🚨 &lt;strong&gt;Warning:&lt;/strong&gt; &lt;code&gt;EXPLAIN ANALYZE&lt;/code&gt; executes the query. For &lt;code&gt;SELECT&lt;/code&gt; that's generally safe. For &lt;code&gt;INSERT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt; or &lt;code&gt;DELETE&lt;/code&gt; it &lt;strong&gt;really performs the modification&lt;/strong&gt;. Wrap it in a transaction and &lt;code&gt;ROLLBACK&lt;/code&gt; if you want to test without keeping changes.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;EXPLAIN&lt;/span&gt; &lt;span class="k"&gt;ANALYZE&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;is_deleted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;ROLLBACK&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also ask for buffer statistics:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;EXPLAIN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ANALYZE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;BUFFERS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;BUFFERS&lt;/code&gt; shows information about database pages accessed from shared buffers and storage, which helps you spot queries doing too much disk or memory I/O.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. 🧰 Types of Indexes: B-tree vs GIN vs GiST
&lt;/h2&gt;

&lt;p&gt;PostgreSQL supports different index types because different queries search for different kinds of data: a user ID, a word inside a text document, locations within a distance, elements inside a JSON document. One index type isn't suitable for every situation.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.1 🌳 B-tree Index
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;The most commonly used.&lt;/em&gt; B-tree stands for &lt;strong&gt;Balanced Tree&lt;/strong&gt; and it's PostgreSQL's &lt;strong&gt;default&lt;/strong&gt; index type.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_users_email&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="nf"&gt;users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&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;p&gt;This creates a B-tree index by default.&lt;/p&gt;

&lt;p&gt;Imagine these numbers: &lt;code&gt;10, 20, 30, 40, 50, 60, 70&lt;/code&gt;. A simplified tree:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;            40
          /    \
        20      60
       (smaller) (larger)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(A simplified binary-tree illustration. Real B-trees have multiple keys and children per node.)&lt;/em&gt; The database navigates the tree instead of checking every value.&lt;/p&gt;

&lt;p&gt;B-tree is excellent for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- 1. Equality searches&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- 2. Range searches&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- 3. Sorting&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- 4. Minimum and maximum values&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="nb"&gt;MIN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's particularly useful for integers, IDs, email addresses, dates and timestamps, numeric values, and sorting and range conditions. Whenever you search, compare or sort ordinary scalar values, B-tree is usually the &lt;strong&gt;first index type to consider&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.2 📇 GIN Index
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;GIN&lt;/strong&gt; stands for &lt;strong&gt;Generalized Inverted Index&lt;/strong&gt;. It's especially useful for searching &lt;strong&gt;inside collections of values or documents&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A post has tags:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Caption: Learning PostgreSQL     Tags: ["database", "backend", "python"]
Caption: Learning React          Tags: ["frontend", "javascript", "web"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You want all posts containing the tag &lt;code&gt;backend&lt;/code&gt;. Use PostgreSQL's array type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;caption&lt;/span&gt; &lt;span class="kt"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="kt"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'Learning PostgreSQL'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;ARRAY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'database'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'backend'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'python'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'Learning React'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;ARRAY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'frontend'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'javascript'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'web'&lt;/span&gt;&lt;span class="p"&gt;]&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;p&gt;Query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt; @&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;ARRAY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'backend'&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;p&gt;&lt;code&gt;@&amp;gt;&lt;/code&gt; means &lt;strong&gt;contains&lt;/strong&gt;: &lt;em&gt;"give me posts whose tags contain &lt;code&gt;backend&lt;/code&gt;"&lt;/em&gt;. Now create a GIN index:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_posts_tags&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="no"&gt;GIN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tags&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;p&gt;PostgreSQL can use it for supported array containment queries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why "inverted"?&lt;/strong&gt; Think of a dictionary. Instead of storing &lt;code&gt;Post 1 → database, backend, python&lt;/code&gt;, an inverted index conceptually maps each value back to the records containing it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;database   → Post 1
backend    → Post 1
python     → Post 1
frontend   → Post 2
javascript → Post 2
web        → Post 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Where is GIN useful?&lt;/strong&gt; PostgreSQL arrays, JSONB data, full-text search, and searching for elements within collections. A JSONB example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;details&lt;/span&gt; &lt;span class="kt"&gt;JSONB&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="nf"&gt;products&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;details&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'{"color": "black", "brand": "Nike"}'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_products_details&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="no"&gt;GIN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;details&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;details&lt;/span&gt; @&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'{"color": "black"}'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This searches for JSON documents containing the specified JSON structure.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 GIN is &lt;strong&gt;not&lt;/strong&gt; a replacement for B-tree. They solve different search problems.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  5.3 🗺️ GiST Index
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;GiST&lt;/strong&gt; stands for &lt;strong&gt;Generalized Search Tree&lt;/strong&gt;. It's a flexible indexing framework for different kinds of data and search operations. One great example is &lt;strong&gt;geographical data&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Imagine finding &lt;em&gt;"all users within 5 km of Bengaluru Palace"&lt;/em&gt;. That's a spatial search, and a B-tree isn't designed to efficiently handle every kind of spatial relationship. GiST can support spatial indexing through extensions such as &lt;strong&gt;PostGIS&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_locations_geom&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;locations&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="no"&gt;GIST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;geom&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;p&gt;A spatial query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;locations&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="nc"&gt;ST_DWithin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;geom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nc"&gt;ST_SetSRID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ST_MakePoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;77.59&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;12.97&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;geography&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;5000&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;p&gt;This finds locations within 5,000 metres of the point, assuming &lt;code&gt;geom&lt;/code&gt; is stored with a geography-compatible coordinate reference system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where is GiST useful?&lt;/strong&gt; Geographic and spatial searches, geometry operations, range types, and certain similarity searches using suitable operator classes.&lt;/p&gt;

&lt;h3&gt;
  
  
  ⚖️ B-tree vs GIN vs GiST
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;B-tree&lt;/th&gt;
&lt;th&gt;GIN&lt;/th&gt;
&lt;th&gt;GiST&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Main purpose&lt;/td&gt;
&lt;td&gt;Ordinary values&lt;/td&gt;
&lt;td&gt;Elements inside collections/documents&lt;/td&gt;
&lt;td&gt;Flexible search structures&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Equality&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Supported operators&lt;/td&gt;
&lt;td&gt;Operator-class dependent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Range&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;td&gt;Not generally&lt;/td&gt;
&lt;td&gt;Supported for suitable types&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sorting&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Generally no&lt;/td&gt;
&lt;td&gt;Generally no&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Arrays&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;td&gt;Operator-dependent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JSONB&lt;/td&gt;
&lt;td&gt;Some operations&lt;/td&gt;
&lt;td&gt;Excellent for supported operators&lt;/td&gt;
&lt;td&gt;Operator-dependent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Spatial search&lt;/td&gt;
&lt;td&gt;No general spatial support&lt;/td&gt;
&lt;td&gt;No general spatial support&lt;/td&gt;
&lt;td&gt;Excellent with PostGIS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;🎯 &lt;strong&gt;Interview takeaway:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;B-tree&lt;/strong&gt; → IDs, emails, dates, range searches&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GIN&lt;/strong&gt; → arrays, JSONB, full-text search&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GiST&lt;/strong&gt; → spatial and specialized searches&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📝 PostgreSQL also has &lt;strong&gt;BRIN&lt;/strong&gt; and &lt;strong&gt;hash&lt;/strong&gt; indexes. You may meet them in advanced work, but B-tree, GIN and GiST are the important three here.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  6. 🧱 Composite Indexes
&lt;/h2&gt;

&lt;p&gt;Another VERY important concept. Take this table:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;user_id&lt;/th&gt;
&lt;th&gt;created_at&lt;/th&gt;
&lt;th&gt;caption&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;Nov 1&lt;/td&gt;
&lt;td&gt;Hello&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;Nov 2&lt;/td&gt;
&lt;td&gt;Beautiful&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;Nov 3&lt;/td&gt;
&lt;td&gt;Coding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;Nov 4&lt;/td&gt;
&lt;td&gt;Weekend&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;Nov 5&lt;/td&gt;
&lt;td&gt;Learning&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You frequently run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's &lt;strong&gt;two operations&lt;/strong&gt;: find posts of user 101, then sort them newest first. You could create two separate indexes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_posts_user&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="nf"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_posts_created&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="nf"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;created_at&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;p&gt;But PostgreSQL might need to combine index information or do extra sorting. There's a better approach.&lt;/p&gt;

&lt;h3&gt;
  
  
  6.1 🤔 What is a composite index?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 A &lt;strong&gt;composite index&lt;/strong&gt; is an index created using &lt;strong&gt;multiple columns&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_posts_user_created&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="nf"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;DESC&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;p&gt;One index, two columns. Conceptually it's organized by &lt;code&gt;user_id&lt;/code&gt; first, then &lt;code&gt;created_at&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;user_id = 101
    ├── Nov 5
    ├── Nov 3
    └── Nov 1

user_id = 102
    └── Nov 2

user_id = 103
    └── Nov 4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The actual index is ordered by the first column, then the second column within matching first-column values. PostgreSQL can potentially find a user's posts &lt;strong&gt;and&lt;/strong&gt; retrieve them in date order &lt;strong&gt;without a separate sorting step&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  6.2 ⚠️ Column order matters!
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_posts_user_created&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="nf"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created_at&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;p&gt;This index is particularly useful for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Both columns&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'2026-01-01'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- First column only&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But what about this?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Second column only&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'2026-01-01'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A regular B-tree composite index is generally &lt;strong&gt;much less effective&lt;/strong&gt; here, because it's ordered primarily by &lt;code&gt;user_id&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📝 PostgreSQL 18 added some B-tree &lt;strong&gt;skip-scan&lt;/strong&gt; capabilities, but don't assume a composite index is an equally good substitute for a separate index on its second column.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  🔑 The Leftmost Prefix Rule
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_example&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="nf"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;is_deleted&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;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Query condition&lt;/th&gt;
&lt;th&gt;Suitability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;user_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅ Good&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;user_id&lt;/code&gt; + &lt;code&gt;created_at&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✅ Good&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;user_id&lt;/code&gt; + &lt;code&gt;created_at&lt;/code&gt; + &lt;code&gt;is_deleted&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✅ Good&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;created_at&lt;/code&gt; alone&lt;/td&gt;
&lt;td&gt;⚠️ Generally less suitable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;is_deleted&lt;/code&gt; alone&lt;/td&gt;
&lt;td&gt;⚠️ Generally less suitable&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This is a general guideline, not an absolute rule.&lt;/p&gt;

&lt;h3&gt;
  
  
  📍 When to create composite indexes
&lt;/h3&gt;

&lt;p&gt;When your app &lt;strong&gt;repeatedly&lt;/strong&gt; runs queries using the same combination of columns. A social feed might have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;is_deleted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A possible index:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_posts_user_deleted_created&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="nf"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;is_deleted&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;DESC&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;p&gt;But always check your actual query patterns and data distribution before deciding the column order.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ More indexes do &lt;strong&gt;not&lt;/strong&gt; automatically mean better performance.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  7. 🐌 The N+1 Query Problem
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;🎯 One of the most common database performance problems in apps using ORMs such as SQLAlchemy, Django ORM and Hibernate.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Your home feed contains &lt;strong&gt;10 posts&lt;/strong&gt;. Each has post info, author info, comments and likes. A beginner might write:&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;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;limit&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="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;author&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's count the queries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: fetch all posts&lt;/strong&gt; (1 query)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: fetch each author&lt;/strong&gt; (10 more queries)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;102&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;103&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- ...and so on&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Total: 1 + 10 = 11 queries.&lt;/strong&gt; This is the &lt;strong&gt;N+1 Query Problem&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1&lt;/strong&gt; query fetches the main records&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;N&lt;/strong&gt; additional queries fetch related data for each record&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fetch 100 posts and you might run 101 queries. Fetch 1,000 and you might run 1,001. That seriously affects performance.&lt;/p&gt;

&lt;h3&gt;
  
  
  7.1 🤔 Why is N+1 a problem?
&lt;/h3&gt;

&lt;p&gt;If every query takes 10 ms:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10 posts:   11 queries × 10 ms  = 110 ms
100 posts: 101 queries × 10 ms = 1010 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(An illustration assuming sequential queries with equal latency. Real timings vary.)&lt;/em&gt; Every unnecessary database round trip adds overhead. Even if each query is individually fast, hundreds of calls make your API slow.&lt;/p&gt;

&lt;h3&gt;
  
  
  7.2 ✅ How do we solve N+1?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Solution 1: use a JOIN&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PostgreSQL retrieves post and author information in &lt;strong&gt;one query&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution 2: eager loading in SQLAlchemy.&lt;/strong&gt; SQLAlchemy supports two common strategies: &lt;code&gt;joinedload()&lt;/code&gt; and &lt;code&gt;selectinload()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A. &lt;code&gt;joinedload()&lt;/code&gt;&lt;/strong&gt;&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;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;select&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;orm&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;joinedload&lt;/span&gt;

&lt;span class="n"&gt;stmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;options&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;joinedload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;limit&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="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scalars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stmt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;all&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;p&gt;SQLAlchemy loads posts and their authors using a &lt;strong&gt;JOIN&lt;/strong&gt;, so related data arrives as part of the query. For &lt;strong&gt;collection&lt;/strong&gt; relationships, joined loading can multiply result rows, and SQLAlchemy requires &lt;code&gt;.unique()&lt;/code&gt; when consuming results in that case.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;B. &lt;code&gt;selectinload()&lt;/code&gt;&lt;/strong&gt;&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;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;select&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;orm&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;selectinload&lt;/span&gt;

&lt;span class="n"&gt;stmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;options&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;selectinload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;comments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;limit&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="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scalars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stmt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;all&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;p&gt;First query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Second query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;comments&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;post_id&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&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="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&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="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of 10 separate comment queries, SQLAlchemy fetches related comments in &lt;strong&gt;one batch&lt;/strong&gt;. A major improvement. 🎉&lt;/p&gt;

&lt;h3&gt;
  
  
  ⚖️ &lt;code&gt;joinedload&lt;/code&gt; vs &lt;code&gt;selectinload&lt;/code&gt;
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;&lt;code&gt;joinedload&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;selectinload&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Strategy&lt;/td&gt;
&lt;td&gt;SQL JOIN&lt;/td&gt;
&lt;td&gt;Separate batch query&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Number of queries&lt;/td&gt;
&lt;td&gt;Usually fewer&lt;/td&gt;
&lt;td&gt;Usually more than one&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Many-to-one&lt;/td&gt;
&lt;td&gt;Often excellent&lt;/td&gt;
&lt;td&gt;Also suitable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One-to-many&lt;/td&gt;
&lt;td&gt;Can duplicate parent rows&lt;/td&gt;
&lt;td&gt;Often preferable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Large collections&lt;/td&gt;
&lt;td&gt;Can produce huge joined results&lt;/td&gt;
&lt;td&gt;Often more efficient&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Practical guideline:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;joinedload()&lt;/code&gt; for many-to-one or one-to-one relationships when appropriate&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;selectinload()&lt;/code&gt; for one-to-many and many-to-many relationships in many common cases&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Neither is universally best. &lt;strong&gt;Measure the actual workload.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  7.3 📱 N+1 in a social-feed app
&lt;/h3&gt;

&lt;p&gt;For &lt;code&gt;GET /feed&lt;/code&gt;, the response contains posts, authors, comments and like counts:&lt;br&gt;
&lt;/p&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="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;"caption"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Beautiful day"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"deepika"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;"comments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;"likes_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;25&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;p&gt;A poor implementation queries all four things separately &lt;strong&gt;for every post&lt;/strong&gt;. A better one:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Fetches posts using pagination&lt;/li&gt;
&lt;li&gt;Eager-loads authors&lt;/li&gt;
&lt;li&gt;Batch-fetches comments if needed&lt;/li&gt;
&lt;li&gt;Calculates like counts using aggregation or a grouped query&lt;/li&gt;
&lt;li&gt;Builds the response &lt;strong&gt;without additional queries inside a loop&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;COUNT&lt;/span&gt;&lt;span class="cm"&gt;(*) AS likes_count
FROM likes
WHERE post_id IN (1, 2, 3, 4, 5)
GROUP BY post_id;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That calculates counts for multiple posts in &lt;strong&gt;one query&lt;/strong&gt;. This is the kind of thinking expected from a good backend developer! 🧠&lt;/p&gt;




&lt;h2&gt;
  
  
  8. ⚖️ Performance vs Storage Trade-offs
&lt;/h2&gt;

&lt;p&gt;If indexes are so useful, why not index &lt;strong&gt;every&lt;/strong&gt; column? Because &lt;strong&gt;indexes are not free&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  8.1 💾 Indexes consume storage
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;posts table        → 500 MB
user_id index      → 100 MB
created_at index   → 120 MB
tags GIN index     → 200 MB
-----------------------------
Total              → 920 MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your table was 500 MB, but with three indexes storage grew to 920 MB in this simplified example. Real sizes depend on data types, values, table structure and index configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  8.2 🐢 Indexes can slow down INSERT
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="nf"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'My new post'&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Without indexes:  Insert row → Done

With indexes:     Insert row
                    ▼
                  Update user_id index
                    ▼
                  Update created_at index
                    ▼
                  Update other indexes
                    ▼
                  Done
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every relevant index needs maintenance, so more indexes can mean more &lt;strong&gt;write overhead&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  8.3 🐢 Indexes can slow down UPDATE and DELETE
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;102&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;user_id&lt;/code&gt; is indexed, PostgreSQL must maintain that index as the indexed value changes. &lt;code&gt;DELETE&lt;/code&gt; operations must also maintain index entries.&lt;/p&gt;

&lt;h3&gt;
  
  
  8.4 🐇 Indexes improve read performance
&lt;/h3&gt;

&lt;p&gt;If your app frequently runs &lt;code&gt;SELECT * FROM posts WHERE user_id = 101;&lt;/code&gt;, an appropriate index can significantly reduce the work needed to find those posts. So there's a trade-off:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;✅ Advantages&lt;/th&gt;
&lt;th&gt;❌ Disadvantages&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Faster suitable reads&lt;/td&gt;
&lt;td&gt;Additional storage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Faster filtering&lt;/td&gt;
&lt;td&gt;Slower writes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Efficient sorting&lt;/td&gt;
&lt;td&gt;Index maintenance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Faster joins in suitable cases&lt;/td&gt;
&lt;td&gt;More database complexity&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The right approach:&lt;/strong&gt; don't create an index simply because a column exists. Create indexes based on:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Frequently executed queries&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;WHERE&lt;/code&gt; conditions&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;JOIN&lt;/code&gt; conditions&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ORDER BY&lt;/code&gt; conditions&lt;/li&gt;
&lt;li&gt;Actual query execution plans&lt;/li&gt;
&lt;li&gt;The balance between reads and writes&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  9. 🛠️ Other Important Query Optimization Techniques
&lt;/h2&gt;

&lt;p&gt;Indexes are only one part of optimization.&lt;/p&gt;

&lt;h3&gt;
  
  
  9.1 🚫 Avoid &lt;code&gt;SELECT *&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This retrieves &lt;strong&gt;every column&lt;/strong&gt;. If the &lt;code&gt;users&lt;/code&gt; table has &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;username&lt;/code&gt;, &lt;code&gt;email&lt;/code&gt;, &lt;code&gt;password_hash&lt;/code&gt;, &lt;code&gt;bio&lt;/code&gt;, &lt;code&gt;profile_picture&lt;/code&gt;, &lt;code&gt;created_at&lt;/code&gt;, &lt;code&gt;address&lt;/code&gt;, &lt;code&gt;preferences&lt;/code&gt; but the frontend only needs &lt;code&gt;username&lt;/code&gt; and &lt;code&gt;profile_picture&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;profile_picture&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Less data transferred&lt;/li&gt;
&lt;li&gt;Less memory used&lt;/li&gt;
&lt;li&gt;Less unnecessary processing&lt;/li&gt;
&lt;li&gt;May allow an &lt;strong&gt;index-only scan&lt;/strong&gt; if a suitable covering index exists and visibility conditions permit&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;📝 &lt;code&gt;SELECT *&lt;/code&gt; is not automatically slow. It's inefficient when you retrieve data you don't need.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  9.2 📄 Use &lt;code&gt;LIMIT&lt;/code&gt; and pagination
&lt;/h3&gt;

&lt;p&gt;With 1 million posts, this is problematic for a feed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You don't need a million posts at once. Instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For large datasets, &lt;strong&gt;cursor pagination&lt;/strong&gt; is often preferable to deep &lt;code&gt;OFFSET&lt;/code&gt; pagination:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="s1"&gt;'2026-11-01 10:00:00'&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In production, use a stable unique tie-breaker such as &lt;code&gt;id&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;cursor_created_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;cursor_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A matching index:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_posts_created_id&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="nf"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;DESC&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;h3&gt;
  
  
  9.3 🔀 Avoid unnecessary JOINs
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;comments&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;comments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post_id&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;likes&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;likes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can produce a &lt;strong&gt;huge&lt;/strong&gt; number of rows. If one post has 5 comments and 10 likes, joining both collections produces &lt;strong&gt;50 combinations&lt;/strong&gt; for that single post. This is called &lt;strong&gt;row multiplication&lt;/strong&gt;. Only join the information you actually need, and use separate aggregate queries or appropriate loading strategies when that's better.&lt;/p&gt;

&lt;h3&gt;
  
  
  9.4 🧮 Use aggregation instead of loading everything
&lt;/h3&gt;

&lt;p&gt;Need only the number of likes?&lt;/p&gt;

&lt;p&gt;❌ Inefficient: fetch all rows and count them in Python.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;likes&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;post_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ Better: let PostgreSQL aggregate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="nb"&gt;COUNT&lt;/span&gt;&lt;span class="cm"&gt;(*)
FROM likes
WHERE post_id = 10;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  9.5 🔧 Avoid functions on indexed columns when possible
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="nb"&gt;LOWER&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'abc@gmail.com'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A normal B-tree index on &lt;code&gt;email&lt;/code&gt; might not be usable for this expression. Create an &lt;strong&gt;expression index&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_users_lower_email&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="nf"&gt;users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;LOWER&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&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;p&gt;Now the query can potentially use it.&lt;/p&gt;




&lt;h2&gt;
  
  
  10. 🧪 A Practical Optimization Exercise
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="kt"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;caption&lt;/span&gt; &lt;span class="kt"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="kt"&gt;TIMESTAMP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;is_deleted&lt;/span&gt; &lt;span class="kt"&gt;BOOLEAN&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="kc"&gt;FALSE&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;p&gt;Your app frequently runs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;is_deleted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 1: check the original execution plan&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;EXPLAIN&lt;/span&gt; &lt;span class="k"&gt;ANALYZE&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;is_deleted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Observe: Is PostgreSQL using Seq Scan? How many rows are examined? Is there a Sort operation? How long does execution take?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: create a composite index&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_posts_user_deleted_created&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="nf"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;is_deleted&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;DESC&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;p&gt;&lt;strong&gt;Step 3: run &lt;code&gt;EXPLAIN ANALYZE&lt;/code&gt; again&lt;/strong&gt; and compare. You may see an Index Scan instead of a Sequential Scan, and possibly no separate Sort. But if the table is small, PostgreSQL may still choose Seq Scan. That doesn't mean your index is broken.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: understand the result&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Possible sequential scan&lt;/td&gt;
&lt;td&gt;Possible index scan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;May inspect many rows&lt;/td&gt;
&lt;td&gt;Can navigate to matching entries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;May require sorting&lt;/td&gt;
&lt;td&gt;Index may provide ordering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Less index storage&lt;/td&gt;
&lt;td&gt;Additional index storage&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That's how you investigate and optimize real database queries. 🔬&lt;/p&gt;




&lt;h2&gt;
  
  
  11. ⚠️ Common Mistakes
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mistake&lt;/th&gt;
&lt;th&gt;Better approach&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Creating indexes on every column&lt;/td&gt;
&lt;td&gt;Index based on query patterns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Assuming Seq Scan is always bad&lt;/td&gt;
&lt;td&gt;Check table size and execution cost&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Using &lt;code&gt;SELECT *&lt;/code&gt; everywhere&lt;/td&gt;
&lt;td&gt;Fetch only required columns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fetching all records&lt;/td&gt;
&lt;td&gt;Use pagination&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ignoring N+1 queries&lt;/td&gt;
&lt;td&gt;Use eager loading and batch queries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Creating too many indexes&lt;/td&gt;
&lt;td&gt;Balance read and write performance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ignoring composite index order&lt;/td&gt;
&lt;td&gt;Design indexes around actual conditions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Optimizing without measuring&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;EXPLAIN ANALYZE&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Assuming an index always improves speed&lt;/td&gt;
&lt;td&gt;Compare actual execution plans&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  12. 🧵 How It All Connects in a Real Backend
&lt;/h2&gt;

&lt;p&gt;Your mentor asks you to optimize &lt;code&gt;GET /feed&lt;/code&gt;, which retrieves posts, authors and likes. Here's the workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;🐢 &lt;strong&gt;Identify the slow API.&lt;/strong&gt; &lt;code&gt;GET /feed&lt;/code&gt; takes too long&lt;/li&gt;
&lt;li&gt;🔎 &lt;strong&gt;Inspect the SQL queries.&lt;/strong&gt; Check which queries SQLAlchemy generates&lt;/li&gt;
&lt;li&gt;🔁 &lt;strong&gt;Find unnecessary database calls.&lt;/strong&gt; Is there an N+1 problem?&lt;/li&gt;
&lt;li&gt;📊 &lt;strong&gt;Analyze execution plans.&lt;/strong&gt; Use &lt;code&gt;EXPLAIN ANALYZE&lt;/code&gt; and inspect scans, rows, sorting and buffers&lt;/li&gt;
&lt;li&gt;🗂️ &lt;strong&gt;Choose suitable indexes.&lt;/strong&gt; B-tree for ordinary lookups, GIN for supported JSONB/array searches, GiST for spatial or specialized operations&lt;/li&gt;
&lt;li&gt;📦 &lt;strong&gt;Optimize data retrieval.&lt;/strong&gt; Use JOINs, eager loading, aggregation and pagination appropriately&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Measure again.&lt;/strong&gt; Compare performance and make sure results are still correct&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Database query time and API response time are &lt;strong&gt;not the same&lt;/strong&gt;. Your API might be slow because of database execution, network latency, Python processing, JSON serialization or external API calls. Don't blame PostgreSQL for every slow endpoint!&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  13. 🎤 Interview Questions and Answers
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q1. What is query optimization?&lt;/strong&gt;&lt;br&gt;
Improving database query performance by reducing unnecessary operations, improving execution plans and retrieving data efficiently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q2. What is an index?&lt;/strong&gt;&lt;br&gt;
An additional data structure that helps a database locate records faster, without scanning the entire table in suitable cases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q3. What is EXPLAIN?&lt;/strong&gt;&lt;br&gt;
It displays the execution plan PostgreSQL estimates it will use to execute a query.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q4. What is EXPLAIN ANALYZE?&lt;/strong&gt;&lt;br&gt;
It executes the query and displays the actual execution statistics along with the plan.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q5. What's the difference between B-tree and GIN?&lt;/strong&gt;&lt;br&gt;
B-tree suits ordinary values, equality, ranges and sorting. GIN is useful for searching elements within arrays, JSONB documents and full-text search.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q6. What is GiST?&lt;/strong&gt;&lt;br&gt;
A flexible indexing framework for specialized search operations, including spatial queries with PostGIS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q7. What is a composite index?&lt;/strong&gt;&lt;br&gt;
An index created using multiple columns, for example &lt;code&gt;CREATE INDEX idx_user_date ON posts(user_id, created_at);&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q8. Why does column order matter in a composite index?&lt;/strong&gt;&lt;br&gt;
A B-tree composite index is ordered primarily by its first column, then subsequent columns, so the order affects which queries can efficiently use it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q9. What is the N+1 query problem?&lt;/strong&gt;&lt;br&gt;
It happens when an application runs one query to retrieve parent records and then an additional query for each parent to retrieve related data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q10. How do you solve N+1 in SQLAlchemy?&lt;/strong&gt;&lt;br&gt;
Use eager loading strategies such as &lt;code&gt;joinedload()&lt;/code&gt; and &lt;code&gt;selectinload()&lt;/code&gt;, or suitable batch queries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q11. Why shouldn't we create indexes on every column?&lt;/strong&gt;&lt;br&gt;
Indexes consume storage and increase &lt;code&gt;INSERT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt; and &lt;code&gt;DELETE&lt;/code&gt; overhead because the indexes must be maintained.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q12. What is an Index Only Scan?&lt;/strong&gt;&lt;br&gt;
A scan that can obtain the required data from the index without fetching the corresponding table rows, subject to visibility checks.&lt;/p&gt;




&lt;h2&gt;
  
  
  14. 📝 Final Revision Sheet
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;Remember this&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Query Optimization&lt;/td&gt;
&lt;td&gt;Make database queries efficient&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Index&lt;/td&gt;
&lt;td&gt;Helps locate records faster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EXPLAIN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Estimated execution plan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EXPLAIN ANALYZE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Actual execution statistics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Seq Scan&lt;/td&gt;
&lt;td&gt;Reads table rows sequentially&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Index Scan&lt;/td&gt;
&lt;td&gt;Uses an index to locate records&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Index Only Scan&lt;/td&gt;
&lt;td&gt;Retrieves required data from the index when possible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B-tree&lt;/td&gt;
&lt;td&gt;IDs, equality, ranges, sorting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GIN&lt;/td&gt;
&lt;td&gt;Arrays, JSONB, full-text search&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GiST&lt;/td&gt;
&lt;td&gt;Spatial and specialized searches&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Composite Index&lt;/td&gt;
&lt;td&gt;Multiple columns in one index&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Leftmost Prefix&lt;/td&gt;
&lt;td&gt;Leading columns matter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;N+1&lt;/td&gt;
&lt;td&gt;One main query plus N additional queries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;joinedload&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Eager loading using JOIN&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;selectinload&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Eager loading using batch SELECT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage trade-off&lt;/td&gt;
&lt;td&gt;Indexes consume extra disk space&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write trade-off&lt;/td&gt;
&lt;td&gt;Index maintenance adds write overhead&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  15. 🏋️ Practice Checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Create a PostgreSQL table containing 100,000 sample posts&lt;/li&gt;
&lt;li&gt;[ ] Run &lt;code&gt;EXPLAIN&lt;/code&gt; on a query without an index&lt;/li&gt;
&lt;li&gt;[ ] Create a B-tree index on &lt;code&gt;user_id&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Run &lt;code&gt;EXPLAIN ANALYZE&lt;/code&gt; again and compare plans&lt;/li&gt;
&lt;li&gt;[ ] Create a composite index on &lt;code&gt;user_id&lt;/code&gt; and &lt;code&gt;created_at&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Create a GIN index on an array column and test &lt;code&gt;@&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Understand a GiST index using a PostGIS example&lt;/li&gt;
&lt;li&gt;[ ] Reproduce an N+1 problem using SQLAlchemy&lt;/li&gt;
&lt;li&gt;[ ] Fix it using &lt;code&gt;joinedload()&lt;/code&gt; or &lt;code&gt;selectinload()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Compare query execution time, index size and write overhead&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Don't just memorize B-tree, GIN, GiST and &lt;code&gt;EXPLAIN&lt;/code&gt;. &lt;strong&gt;Actually run queries in PostgreSQL&lt;/strong&gt; and watch how the execution plan changes. That's how you develop real database optimization skills.&lt;/p&gt;

&lt;p&gt;🎯 &lt;em&gt;A good backend developer doesn't just make queries work. They make queries work **efficiently&lt;/em&gt;* as the application grows.*&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;If this post helped you, drop a ❤️ or 🦄 and share it with a friend learning backend development. Happy coding! 👩‍💻✨&lt;/p&gt;

</description>
      <category>database</category>
      <category>performance</category>
      <category>postgres</category>
      <category>sql</category>
    </item>
    <item>
      <title>Structural &amp; Behavioral Design Patterns in Python: Repository, Factory, Strategy, Decorator &amp; Adapter Explained 🧩</title>
      <dc:creator>Deepika Pusala</dc:creator>
      <pubDate>Thu, 12 Nov 2026 06:24:14 +0000</pubDate>
      <link>https://dev.to/deepika_pusala/structural-behavioral-design-patterns-in-python-repository-factory-strategy-decorator--3lld</link>
      <guid>https://dev.to/deepika_pusala/structural-behavioral-design-patterns-in-python-repository-factory-strategy-decorator--3lld</guid>
      <description>&lt;h1&gt;
  
  
  🧩 Structural &amp;amp; Behavioral Design Patterns: Let's Learn from ZERO!
&lt;/h1&gt;

&lt;p&gt;Design patterns teach you how to organize real-world application code so it is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📖 Easy to understand&lt;/li&gt;
&lt;li&gt;🛠️ Easy to maintain&lt;/li&gt;
&lt;li&gt;🧪 Easy to test&lt;/li&gt;
&lt;li&gt;➕ Easy to extend with new features&lt;/li&gt;
&lt;li&gt;🔗 Less tightly coupled&lt;/li&gt;
&lt;li&gt;♻️ Reusable across different parts of an application&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  1. 🤔 First, What Are Design Patterns?
&lt;/h2&gt;

&lt;p&gt;Imagine you're building an Instagram-like app with login, creating posts, fetching posts, likes, image uploads, notifications and follows. At first you might write everything in one function:&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;create_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Validate user&lt;/span&gt;
    &lt;span class="c1"&gt;# Check authentication&lt;/span&gt;
    &lt;span class="c1"&gt;# Save post to database&lt;/span&gt;
    &lt;span class="c1"&gt;# Upload image&lt;/span&gt;
    &lt;span class="c1"&gt;# Send notification&lt;/span&gt;
    &lt;span class="c1"&gt;# Return response&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works at first. But when the app grows to &lt;strong&gt;100 features&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;💥 Changing one feature might break another&lt;/li&gt;
&lt;li&gt;🧪 Testing becomes difficult&lt;/li&gt;
&lt;li&gt;🔁 Code becomes duplicated&lt;/li&gt;
&lt;li&gt;🧶 Database logic gets mixed with business logic&lt;/li&gt;
&lt;li&gt;😩 Adding new functionality becomes complicated&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 A &lt;strong&gt;design pattern&lt;/strong&gt; is a &lt;strong&gt;reusable solution or blueprint&lt;/strong&gt; for a commonly occurring software design problem.&lt;/p&gt;

&lt;p&gt;⚠️ A design pattern is &lt;strong&gt;NOT&lt;/strong&gt; a library, framework, or ready-made code you copy. It's an &lt;em&gt;approach&lt;/em&gt; to organizing your code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  1.1 🗂️ Three Categories of Design Patterns
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Concerned with&lt;/th&gt;
&lt;th&gt;Examples&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🏭 &lt;strong&gt;Creational&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;How objects are &lt;strong&gt;created&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Factory, Singleton, Builder&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🧱 &lt;strong&gt;Structural&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;How classes and objects are &lt;strong&gt;organized and connected&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Repository, Adapter, Decorator, Facade&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🎭 &lt;strong&gt;Behavioral&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;How objects &lt;strong&gt;communicate&lt;/strong&gt; and distribute responsibilities&lt;/td&gt;
&lt;td&gt;Strategy, Observer, Command, State&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Our focus today:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Main purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Repository&lt;/td&gt;
&lt;td&gt;Architectural / structural&lt;/td&gt;
&lt;td&gt;Separates database operations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Factory&lt;/td&gt;
&lt;td&gt;Creational&lt;/td&gt;
&lt;td&gt;Creates objects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Strategy&lt;/td&gt;
&lt;td&gt;Behavioral&lt;/td&gt;
&lt;td&gt;Changes algorithms easily&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Decorator&lt;/td&gt;
&lt;td&gt;Structural&lt;/td&gt;
&lt;td&gt;Adds functionality without changing original code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Adapter&lt;/td&gt;
&lt;td&gt;Structural&lt;/td&gt;
&lt;td&gt;Makes incompatible interfaces work together&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;📝 Repository is often called a design pattern, but it's more specifically a &lt;strong&gt;data-access architectural pattern&lt;/strong&gt;. Factory is creational, while Strategy is behavioral.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  2. 🧹 Separation of Concerns (SoC)
&lt;/h2&gt;

&lt;p&gt;Before individual patterns, one foundational principle. Think of a restaurant 🍽️: the waiter takes orders, the chef prepares food, the cashier handles payments, the storekeeper manages ingredients. Would you ask one person to do everything? Probably not. Software should work the same way.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;Separation of Concerns&lt;/strong&gt; means dividing an application into different parts, where each part handles a &lt;strong&gt;specific responsibility&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In a social-feed backend:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Responsibility&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🛣️ Controller / Router&lt;/td&gt;
&lt;td&gt;Handles HTTP requests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🧠 Service&lt;/td&gt;
&lt;td&gt;Contains business logic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;📚 Repository&lt;/td&gt;
&lt;td&gt;Communicates with the database&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🗄️ Model&lt;/td&gt;
&lt;td&gt;Represents database structure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;✔️ Schema&lt;/td&gt;
&lt;td&gt;Validates request and response data&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A user creates a post:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;⚛️ Frontend (React)       → user clicks Create Post
        ▼
🛣️ Controller / Router    → receives the HTTP POST request
        ▼
🧠 Service                → checks business rules, decides what should happen
        ▼
📚 Repository             → performs database operations
        ▼
🐘 PostgreSQL             → stores the post
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is called &lt;strong&gt;Layered Architecture&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. 🥞 Layered Architecture: Controller → Service → Repository
&lt;/h2&gt;

&lt;p&gt;Layered architecture divides an app into layers, each with its own responsibility. The most common backend structure is &lt;strong&gt;Controller (or Router) → Service → Repository → Database&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.1 🛣️ Controller Layer
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 A &lt;strong&gt;controller&lt;/strong&gt; is the entry point through which an external client communicates with the backend.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In FastAPI we generally use &lt;code&gt;APIRouter&lt;/code&gt; and route functions:&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;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;APIRouter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;

&lt;span class="n"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;APIRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/posts"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PostCreate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;post_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;React sends a POST request&lt;/li&gt;
&lt;li&gt;FastAPI receives it&lt;/li&gt;
&lt;li&gt;The controller gets the request data&lt;/li&gt;
&lt;li&gt;It calls the service&lt;/li&gt;
&lt;li&gt;It returns the service result&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The controller should &lt;strong&gt;NOT&lt;/strong&gt; contain all business logic or SQL queries. Think of it as a &lt;strong&gt;receptionist&lt;/strong&gt; 🛎️: it receives your request and sends it to the right department, without personally doing every job.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.2 🧠 Service Layer
&lt;/h3&gt;

&lt;p&gt;The service layer contains the app's &lt;strong&gt;business logic&lt;/strong&gt;, the actual rules that define how your app behaves. In Instagram, for example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A user cannot create a post without authentication&lt;/li&gt;
&lt;li&gt;A user cannot edit another person's private post&lt;/li&gt;
&lt;li&gt;A post must have valid content or media&lt;/li&gt;
&lt;li&gt;A user cannot like the same post twice
&lt;/li&gt;
&lt;/ul&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;PostService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post_repository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post_repository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;post_repository&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Post cannot be empty"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post_repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;user_id&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="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;PostService&lt;/code&gt; handles post-related business logic&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;__init__()&lt;/code&gt; receives the repository&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;create_post()&lt;/code&gt; checks whether content is empty&lt;/li&gt;
&lt;li&gt;If valid, it asks the repository to save the post&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Notice: the service does &lt;strong&gt;not&lt;/strong&gt; write SQL. It delegates database work to the repository.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.3 📚 Repository Layer
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 A &lt;strong&gt;repository&lt;/strong&gt; handles communication between your application and the database. It sits between business logic and data storage.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&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;PostRepository&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;user_id&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="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;refresh&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Create a &lt;code&gt;Post&lt;/code&gt; database object&lt;/li&gt;
&lt;li&gt;Add it to the database session&lt;/li&gt;
&lt;li&gt;Commit the transaction&lt;/li&gt;
&lt;li&gt;Refresh the object to retrieve updated database values&lt;/li&gt;
&lt;li&gt;Return the saved post&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  3.4 🤔 Why separate these three layers?
&lt;/h3&gt;

&lt;p&gt;Imagine one function doing everything:&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="nd"&gt;@router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/posts"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PostCreate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Validation&lt;/span&gt;
    &lt;span class="c1"&gt;# Authentication&lt;/span&gt;
    &lt;span class="c1"&gt;# Business rules&lt;/span&gt;
    &lt;span class="c1"&gt;# SQL queries&lt;/span&gt;
    &lt;span class="c1"&gt;# Database commit&lt;/span&gt;
    &lt;span class="c1"&gt;# Response formatting&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's tightly coupled code. If you change your database and database code is scattered everywhere, you modify many functions. With layered architecture, database changes are concentrated mainly in the repository and persistence configuration.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Controller&lt;/th&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Repository&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Handles HTTP&lt;/td&gt;
&lt;td&gt;Handles business rules&lt;/td&gt;
&lt;td&gt;Handles data access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Receives requests&lt;/td&gt;
&lt;td&gt;Makes decisions&lt;/td&gt;
&lt;td&gt;Executes queries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Calls service&lt;/td&gt;
&lt;td&gt;Calls repository&lt;/td&gt;
&lt;td&gt;Talks to database&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Returns responses&lt;/td&gt;
&lt;td&gt;Returns business results&lt;/td&gt;
&lt;td&gt;Returns stored data&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;🎤 &lt;strong&gt;Interview question:&lt;/strong&gt; &lt;em&gt;Why shouldn't controllers directly communicate with databases?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Answer:&lt;/strong&gt; It mixes HTTP handling, business logic and data-access responsibilities. Separating them improves maintainability, testability and reusability, and reduces coupling.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  4. 📚 Repository Pattern
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Structural / data-access pattern&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4.1 🚨 The problem
&lt;/h3&gt;

&lt;p&gt;Without the Repository Pattern:&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="nd"&gt;@router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/posts"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The controller talks directly to the database. With 20 endpoints needing database access, you'd repeat database operations everywhere, causing duplication and tight coupling.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.2 ✅ The solution
&lt;/h3&gt;

&lt;p&gt;Create a separate repository class that handles all database operations for posts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❌ Without Repository Pattern:  Controller ──▶ Database
✅ With Repository Pattern:     Controller ──▶ Service ──▶ PostRepository ──▶ Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4.3 🧵 Full example
&lt;/h3&gt;

&lt;p&gt;The repository:&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;PostRepository&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_by_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;refresh&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&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;p&gt;The service:&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;PostService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;repository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_all_posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_all&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;p&gt;The controller:&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="nd"&gt;@router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/posts"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;post_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_all_posts&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;h3&gt;
  
  
  4.4 ✅ Advantages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Database logic stays in one place&lt;/li&gt;
&lt;li&gt;Easier unit testing&lt;/li&gt;
&lt;li&gt;Reduces duplication&lt;/li&gt;
&lt;li&gt;Business logic doesn't need to know SQL details&lt;/li&gt;
&lt;li&gt;Easier to maintain&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4.5 🤔 Can we change databases with zero changes?
&lt;/h3&gt;

&lt;p&gt;Not automatically! PostgreSQL and MongoDB have different data models and query behavior. The Repository Pattern isolates much of that difference, but changing databases may still require changes to repositories, models, transactions and other persistence code.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📝 A repository is &lt;strong&gt;not&lt;/strong&gt; just a folder containing SQL queries. It represents the data-access operations the rest of the application needs.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  5. 🏭 Factory Pattern
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Creational pattern&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5.1 🚨 The problem
&lt;/h3&gt;

&lt;p&gt;Your app supports email, SMS and push notifications. You could create objects directly everywhere:&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;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;EmailNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;sms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SMSNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;push&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PushNotification&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;p&gt;But with 50 places creating notifications, what happens when the creation process changes? You must update &lt;strong&gt;many places&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.2 🤔 What is the Factory Pattern?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 The &lt;strong&gt;Factory Pattern&lt;/strong&gt; provides a separate component or method responsible for &lt;strong&gt;creating objects&lt;/strong&gt;. Instead of creating objects yourself everywhere, you ask a factory to create the appropriate one.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;🍕 Think of ordering pizza or a burger. You don't walk into the kitchen and make it yourself. The kitchen receives your order and creates the item. That's a factory.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.3 🐍 Python example
&lt;/h3&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;EmailNotification&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Sending email:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SMSNotification&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Sending SMS:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PushNotification&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Sending push notification:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The factory:&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;NotificationFactory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nd"&gt;@staticmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_notification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;notification_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;EmailNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;notification_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"sms"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;SMSNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;notification_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"push"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;PushNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Invalid notification type"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use it:&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;notification&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;NotificationFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_notification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Your post was liked!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Sending email: Your post was liked!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5.4 🔍 The flow
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Application: "I need an email notification."
        ▼
NotificationFactory → decides which object to create
        ▼
EmailNotification object → created and returned
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5.5 📍 When to use a Factory
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Creating different notification types&lt;/li&gt;
&lt;li&gt;Creating different payment processors&lt;/li&gt;
&lt;li&gt;Creating different database clients&lt;/li&gt;
&lt;li&gt;Creating different file parsers&lt;/li&gt;
&lt;li&gt;Creating different authentication providers&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5.6 ⚖️ Factory vs normal object creation
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Normal creation&lt;/th&gt;
&lt;th&gt;Factory&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Application creates objects directly&lt;/td&gt;
&lt;td&gt;Factory creates objects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Creation logic may be duplicated&lt;/td&gt;
&lt;td&gt;Creation logic is centralized&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Application knows concrete classes&lt;/td&gt;
&lt;td&gt;Application can depend on a common interface&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 Factory focuses on &lt;strong&gt;HOW objects are created&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  6. 🎯 Strategy Pattern
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Behavioral pattern. One of the MOST important patterns for backend development.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  6.1 🚨 The problem
&lt;/h3&gt;

&lt;p&gt;A payment system supports UPI, credit card, debit card and PayPal. Each processes payments differently. Without the Strategy Pattern:&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;make_payment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payment_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;payment_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"upi"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Processing UPI payment"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;payment_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"card"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Processing card payment"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;payment_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"paypal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Processing PayPal payment"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fine at first. But with 20 payment methods the function becomes &lt;strong&gt;huge&lt;/strong&gt;, and every new method means modifying the same function.&lt;/p&gt;

&lt;h3&gt;
  
  
  6.2 🤔 What is the Strategy Pattern?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 The &lt;strong&gt;Strategy Pattern&lt;/strong&gt; lets you define multiple algorithms, put each in a &lt;strong&gt;separate class&lt;/strong&gt;, and &lt;strong&gt;select the appropriate one at runtime&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In simple words: &lt;strong&gt;same task, different ways of performing it.&lt;/strong&gt; 🚌 To travel to college you can choose bus, car, bike or metro. The destination is the same, but the method changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  6.3 🐍 Implementation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 1: create different strategies&lt;/strong&gt;&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;UpiPayment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"Paid {amount} using UPI"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CardPayment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"Paid {amount} using Card"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PayPalPayment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"Paid {amount} using PayPal"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each class implements the same operation, &lt;code&gt;pay()&lt;/code&gt;, but performs it differently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: create the context class&lt;/strong&gt; (the class that uses a selected strategy)&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;PaymentProcessor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;strategy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strategy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;strategy&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;make_payment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We pass a payment strategy object into &lt;code&gt;PaymentProcessor&lt;/code&gt;. It doesn't need to know whether it's UPI, Card or PayPal. It only needs an object that supports &lt;code&gt;pay()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: use different strategies&lt;/strong&gt;&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;upi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;UpiPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;processor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PaymentProcessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upi&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;processor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;make_payment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Paid 500 using UPI
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Change the strategy:&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;card&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CardPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;processor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PaymentProcessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;processor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;make_payment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Paid 1000 using Card
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PaymentProcessor (Context) → uses a selected payment strategy
        ├── UPI     → pay()
        ├── Card    → pay()
        └── PayPal  → pay()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6.4 📱 Strategy in a social-feed project
&lt;/h3&gt;

&lt;p&gt;Different feed-generation algorithms:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Chronological:&lt;/strong&gt; newest posts first&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Popularity-based:&lt;/strong&gt; most liked posts first&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Personalized:&lt;/strong&gt; posts based on user interests&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Instead of one huge function, create separate strategies:&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;ChronologicalFeed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PopularityFeed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;like_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&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;p&gt;A context uses either one:&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;FeedService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;strategy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strategy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;strategy&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_feed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FeedService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ChronologicalFeed&lt;/span&gt;&lt;span class="p"&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;feed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_feed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can change the strategy without rewriting &lt;code&gt;FeedService&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  6.5 ✅ Advantages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Easy to add new algorithms&lt;/li&gt;
&lt;li&gt;Avoids huge &lt;code&gt;if-elif&lt;/code&gt; conditions&lt;/li&gt;
&lt;li&gt;Reduces code duplication&lt;/li&gt;
&lt;li&gt;Makes algorithms independently testable&lt;/li&gt;
&lt;li&gt;Supports the Open/Closed Principle&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6.6 📖 Important terminology
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Term&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Strategy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;An interchangeable algorithm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Concrete Strategy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The actual implementation, such as UPI or Card&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Context&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The class that uses the strategy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Runtime selection&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Choosing which strategy to use while the app runs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;🎤 &lt;strong&gt;Interview answer:&lt;/strong&gt; The Strategy Pattern defines a family of interchangeable algorithms, encapsulates each in a separate class, and lets the application select one at runtime without changing the client code.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  7. 🎁 Decorator Pattern
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Structural pattern.&lt;/em&gt; It lets you add extra functionality to existing objects &lt;strong&gt;without modifying their original classes&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  7.1 🚨 The problem
&lt;/h3&gt;

&lt;p&gt;A coffee-ordering app has a simple coffee. Customers want milk, chocolate, whipped cream, milk and chocolate, and so on. One approach is a class for each combination:&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;SimpleCoffee&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CoffeeWithMilk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CoffeeWithChocolate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CoffeeWithMilkAndChocolate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With 15 toppings you may end up with a huge number of combinations. This is the &lt;strong&gt;class explosion&lt;/strong&gt; problem. 💥&lt;/p&gt;

&lt;h3&gt;
  
  
  7.2 🤔 What is the Decorator Pattern?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 The &lt;strong&gt;Decorator Pattern&lt;/strong&gt; dynamically adds responsibilities or functionality to an object by &lt;strong&gt;wrapping it inside another object&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Take an existing object, wrap it, and give it extra functionality without changing the original. ☕ You buy a coffee, then add milk, chocolate and cream. Your original coffee still exists. You're just adding features to it.&lt;/p&gt;

&lt;h3&gt;
  
  
  7.3 🐍 Python implementation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 1: the basic component&lt;/strong&gt;&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;Coffee&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;cost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"Basic Coffee"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: a decorator for milk&lt;/strong&gt;&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;MilkDecorator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;coffee&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;coffee&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;coffee&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;cost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;coffee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;coffee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;" + Milk"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3: a decorator for chocolate&lt;/strong&gt;&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;ChocolateDecorator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;coffee&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;coffee&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;coffee&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;cost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;coffee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;coffee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;" + Chocolate"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4: combine them&lt;/strong&gt;&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;coffee&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Coffee&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;coffee&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MilkDecorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coffee&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;coffee&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ChocolateDecorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coffee&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coffee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coffee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cost&lt;/span&gt;&lt;span class="p"&gt;(&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Basic Coffee + Milk + Chocolate
150
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Basic Coffee        ₹100
   ▼ wrapped by
MilkDecorator       +₹20
   ▼ wrapped by
ChocolateDecorator  +₹30
   ▼
Final coffee        ₹150
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;coffee = MilkDecorator(coffee)&lt;/code&gt; passes the original coffee into &lt;code&gt;MilkDecorator&lt;/code&gt;. Then &lt;code&gt;coffee = ChocolateDecorator(coffee)&lt;/code&gt; passes the milk-decorated coffee into &lt;code&gt;ChocolateDecorator&lt;/code&gt;. This is called &lt;strong&gt;wrapping&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  7.4 ⚡ Real-world example: FastAPI decorators
&lt;/h3&gt;

&lt;p&gt;You've probably already used decorators!&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="nd"&gt;@router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/posts"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Posts"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="nd"&gt;@router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/posts"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Created"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Python decorators are a &lt;strong&gt;language feature&lt;/strong&gt; that can wrap or modify function behavior. FastAPI uses them to register route functions and associate HTTP methods and paths with them. More examples:&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="nd"&gt;@staticmethod&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;span class="nd"&gt;@property&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;username&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_username&lt;/span&gt;


&lt;span class="nd"&gt;@functools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lru_cache&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_settings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Settings&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;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Important distinction:&lt;/strong&gt; the &lt;strong&gt;Decorator Design Pattern&lt;/strong&gt; is a particular object-composition pattern. Not every Python &lt;code&gt;@decorator&lt;/code&gt; is an implementation of the GoF Decorator Pattern.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  7.5 ✅ Advantages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Adds functionality dynamically&lt;/li&gt;
&lt;li&gt;Avoids modifying existing classes&lt;/li&gt;
&lt;li&gt;Avoids creating many subclasses for combinations&lt;/li&gt;
&lt;li&gt;Supports the Open/Closed Principle&lt;/li&gt;
&lt;li&gt;Allows multiple decorators to be combined&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  7.6 📍 When to use it
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Logging&lt;/li&gt;
&lt;li&gt;Caching&lt;/li&gt;
&lt;li&gt;Authentication wrappers&lt;/li&gt;
&lt;li&gt;Authorization checks&lt;/li&gt;
&lt;li&gt;Adding features to objects&lt;/li&gt;
&lt;li&gt;Compression and encryption wrappers&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  8. 🔌 Adapter Pattern
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Structural pattern.&lt;/em&gt; One of the easiest to understand with a real-life example.&lt;/p&gt;

&lt;h3&gt;
  
  
  8.1 🚨 The problem
&lt;/h3&gt;

&lt;p&gt;Your phone has a USB-C port, but your charger has a different connector. Can you connect them directly? &lt;strong&gt;No!&lt;/strong&gt; You need an &lt;strong&gt;adapter&lt;/strong&gt; that converts one interface into another compatible one. Software works the same way.&lt;/p&gt;

&lt;h3&gt;
  
  
  8.2 🤔 What is the Adapter Pattern?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 The &lt;strong&gt;Adapter Pattern&lt;/strong&gt; lets two incompatible interfaces work together by introducing a middle component that converts one interface into the one expected by the other. Think of it as a &lt;strong&gt;translator&lt;/strong&gt; between two incompatible systems. 🗣️&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  8.3 🐍 Python example
&lt;/h3&gt;

&lt;p&gt;Your application expects every payment gateway to have &lt;code&gt;pay(amount)&lt;/code&gt;. But an external provider has &lt;code&gt;make_payment(value)&lt;/code&gt;, so your app can't use it where &lt;code&gt;pay()&lt;/code&gt; is expected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: the external payment provider&lt;/strong&gt;&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;ExternalPaymentGateway&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;make_payment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"External payment: {value}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: create an adapter&lt;/strong&gt;&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;PaymentAdapter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gateway&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gateway&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gateway&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;make_payment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3: use the adapter&lt;/strong&gt;&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;external_gateway&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ExternalPaymentGateway&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;adapter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PaymentAdapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;external_gateway&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;adapter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;External payment: 500
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;The application calls &lt;code&gt;adapter.pay(500)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The adapter receives the amount&lt;/li&gt;
&lt;li&gt;The adapter converts the call into &lt;code&gt;gateway.make_payment(500)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The external gateway processes the payment
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Your Application      → calls pay(amount)
        ▼
Payment Adapter       → translates the method call
        ▼
External Payment Gateway → receives make_payment(value)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  8.4 🌍 Real-world examples
&lt;/h3&gt;

&lt;p&gt;Your app might integrate &lt;strong&gt;Razorpay, Stripe, PayPal&lt;/strong&gt; or different cloud storage providers. Each has different APIs and method names. Create adapters that expose a &lt;strong&gt;common interface&lt;/strong&gt; to your application:&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;StripeAdapter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stripe_client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stripe_client&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;charge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_payment_intent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;amount&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;p&gt;Your app uses &lt;code&gt;charge()&lt;/code&gt; without needing to understand all the provider-specific details.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📝 In production you'd also handle currency units, authentication, errors and payment verification properly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  8.5 ✅ Advantages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Integrates third-party services&lt;/li&gt;
&lt;li&gt;Reuses existing code&lt;/li&gt;
&lt;li&gt;Avoids modifying external libraries&lt;/li&gt;
&lt;li&gt;Reduces coupling between your app and external systems&lt;/li&gt;
&lt;li&gt;Makes switching providers easier&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;🎤 &lt;strong&gt;Interview answer:&lt;/strong&gt; The Adapter Pattern converts the interface of an existing class into another interface expected by the client, allowing incompatible classes to work together.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  9. 🧬 Composition Over Inheritance
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;An object-oriented design principle, and VERY important for writing clean code.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  9.1 👪 What is inheritance?
&lt;/h3&gt;

&lt;p&gt;Inheritance lets one class acquire properties and methods from another:&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;Animal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;eat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Animal is eating"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Dog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;bark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Dog is barking"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;dog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Dog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;dog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;dog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bark&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Animal is eating
Dog is barking
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Dog&lt;/code&gt; inherits &lt;code&gt;eat()&lt;/code&gt; from &lt;code&gt;Animal&lt;/code&gt;. This is an &lt;strong&gt;IS-A&lt;/strong&gt; relationship: a dog &lt;strong&gt;is an&lt;/strong&gt; animal.&lt;/p&gt;

&lt;h3&gt;
  
  
  9.2 🧩 What is composition?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;Composition&lt;/strong&gt; means creating a class using &lt;strong&gt;objects of other classes&lt;/strong&gt;. Instead of inheriting functionality, a class contains or uses another object to perform a task. This is a &lt;strong&gt;HAS-A&lt;/strong&gt; relationship.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A car &lt;strong&gt;has an&lt;/strong&gt; engine. A car is &lt;em&gt;not&lt;/em&gt; an engine. 🚗&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;Engine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Engine started"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start_car&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Car is ready"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;car&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;car&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_car&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Engine started
Car is ready
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;self.engine = Engine()&lt;/code&gt; means the &lt;code&gt;Car&lt;/code&gt; &lt;strong&gt;contains&lt;/strong&gt; an &lt;code&gt;Engine&lt;/code&gt; object and uses its functionality instead of inheriting from it.&lt;/p&gt;

&lt;h3&gt;
  
  
  9.3 ⚖️ Inheritance vs composition
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Inheritance:  Animal ◀── inherits from ── Dog     (IS-A)
Composition:  Car ── HAS-A ──▶ Engine             (uses another object)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  9.4 🤔 Why is composition often preferred?
&lt;/h3&gt;

&lt;p&gt;A notification system has:&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;EmailNotification&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SMSNotification&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PushNotification&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You want logging on every notification. One approach is inheritance:&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;EmailWithLogging&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;EmailNotification&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SMSWithLogging&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SMSNotification&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PushWithLogging&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PushNotification&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now add logging, caching, retry and monitoring, and you may create a huge number of subclasses. That's the &lt;strong&gt;inheritance explosion&lt;/strong&gt; problem. Use composition instead:&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;Logger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"LOG:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NotificationService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Sending notification"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Notification sent:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;NotificationService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Hello!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can replace the &lt;code&gt;Logger&lt;/code&gt; with another implementation &lt;strong&gt;without changing the notification service&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  9.5 ✅ Advantages of composition
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;More flexible code&lt;/li&gt;
&lt;li&gt;Less dependency on parent classes&lt;/li&gt;
&lt;li&gt;Easier testing&lt;/li&gt;
&lt;li&gt;Easier to replace components&lt;/li&gt;
&lt;li&gt;Supports loose coupling&lt;/li&gt;
&lt;li&gt;Encourages reusable functionality&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  9.6 🤷 Does composition mean never use inheritance?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Absolutely not!&lt;/strong&gt; Inheritance is useful when a genuine IS-A relationship exists: a Dog is an Animal, an AdminUser is a User, a Circle is a Shape. Composition is preferable when you want to reuse behavior or collaborate with other objects &lt;strong&gt;without creating an artificial parent-child relationship&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🎤 &lt;em&gt;"Favor composition over inheritance"&lt;/em&gt; means prefer combining objects with specific responsibilities over building deep, rigid inheritance hierarchies.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  10. 🧵 How All These Patterns Work Together
&lt;/h2&gt;

&lt;p&gt;A user uploads a post containing an image. A well-organized architecture could work like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;⚛️ React Frontend → user creates a post with an image
        ▼
🛣️ Controller / FastAPI Router → receives and validates the HTTP request
        ▼
🧠 PostService → applies post creation rules
        │
        ├── 🏭 Factory   → creates the appropriate storage provider
        ├── 🔌 Adapter   → converts the storage provider's API into your app's expected interface
        ├── 🎯 Strategy  → selects an image-processing or compression algorithm
        └── 🎁 Decorator → adds optional logging, caching or monitoring around an operation
        ▼
📚 PostRepository → saves post information in PostgreSQL
        ▼
🐘 PostgreSQL → stores post records and image URLs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;📝 These are &lt;em&gt;possible&lt;/em&gt; applications of the patterns, not a requirement that every service must use all five.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The main lesson: &lt;strong&gt;each component has a clear responsibility.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  11. ⚖️ Separation of Concerns vs Loose Coupling vs High Cohesion
&lt;/h2&gt;

&lt;p&gt;These three are often discussed together.&lt;/p&gt;

&lt;h3&gt;
  
  
  11.1 🧹 Separation of Concerns
&lt;/h3&gt;

&lt;p&gt;Each component has a specific responsibility. The controller handles HTTP requests, the service handles business logic, the repository handles database access. Don't mix everything into one class.&lt;/p&gt;

&lt;h3&gt;
  
  
  11.2 🔗 Loose Coupling
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Coupling&lt;/strong&gt; is how strongly two components depend on each other.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tight coupling:&lt;/strong&gt; one component heavily depends on the internal implementation of another&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Loose coupling:&lt;/strong&gt; components interact through well-defined interfaces and have fewer dependencies on each other's internals&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;❌ Tightly coupled:&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;NotificationService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;EmailNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here &lt;code&gt;NotificationService&lt;/code&gt; directly creates an &lt;code&gt;EmailNotification&lt;/code&gt;, so it's permanently dependent on that concrete class.&lt;/p&gt;

&lt;p&gt;✅ Loosely coupled:&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;NotificationService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can inject &lt;code&gt;EmailNotification&lt;/code&gt;, &lt;code&gt;SMSNotification&lt;/code&gt; or &lt;code&gt;PushNotification&lt;/code&gt;. The service doesn't need to know which concrete implementation it receives. This is &lt;strong&gt;Dependency Injection&lt;/strong&gt;. 💉&lt;/p&gt;

&lt;h3&gt;
  
  
  11.3 🎯 High Cohesion
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Cohesion&lt;/strong&gt; is how closely related the responsibilities inside a component are.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;High cohesion:&lt;/strong&gt; a class or module focuses on one closely related set of responsibilities&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Low cohesion:&lt;/strong&gt; a class handles unrelated responsibilities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;❌ Bad design:&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;UserManager&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_payment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ Better design:&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;UserService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EmailService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ReportService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaymentService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_payment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  📊 Comparison
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;Main goal&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Separation of Concerns&lt;/td&gt;
&lt;td&gt;Divide responsibilities&lt;/td&gt;
&lt;td&gt;Organized architecture&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Loose Coupling&lt;/td&gt;
&lt;td&gt;Reduce dependencies&lt;/td&gt;
&lt;td&gt;Flexibility&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;High Cohesion&lt;/td&gt;
&lt;td&gt;Keep related responsibilities together&lt;/td&gt;
&lt;td&gt;Focused components&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 Good software design = separation of concerns + loose coupling + high cohesion + clear responsibilities + easy testing and maintenance.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  12. 🥊 Important Differences Between the Patterns
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;🎯 This is where interviewers may try to confuse you.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  12.1 🏭 Factory vs 🎯 Strategy
&lt;/h3&gt;

&lt;p&gt;Both use different classes, but they solve different problems.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Factory&lt;/th&gt;
&lt;th&gt;Strategy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Creates objects&lt;/td&gt;
&lt;td&gt;Selects behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Focuses on object creation&lt;/td&gt;
&lt;td&gt;Focuses on algorithms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;Which object should I create?&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Which algorithm should I use?&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Example: create a UPI or Card processor&lt;/td&gt;
&lt;td&gt;Example: use a UPI or Card payment strategy&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A factory might create a UPI payment object, and the strategy determines how it processes a payment. They can also be &lt;strong&gt;used together&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  12.2 🔌 Adapter vs 🎁 Decorator
&lt;/h3&gt;

&lt;p&gt;Both can wrap another object, but their purposes differ.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Adapter&lt;/th&gt;
&lt;th&gt;Decorator&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Changes the &lt;strong&gt;interface&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Adds &lt;strong&gt;functionality&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Makes incompatible objects compatible&lt;/td&gt;
&lt;td&gt;Enhances existing object behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Acts as a translator&lt;/td&gt;
&lt;td&gt;Acts as a wrapper&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Example: converts &lt;code&gt;make_payment()&lt;/code&gt; into &lt;code&gt;pay()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Example: adds logging to &lt;code&gt;pay()&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  12.3 📚 Repository vs 🧠 Service
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Repository&lt;/th&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Handles database operations&lt;/td&gt;
&lt;td&gt;Handles business rules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Saves, retrieves, deletes data&lt;/td&gt;
&lt;td&gt;Decides what should happen&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Knows persistence details&lt;/td&gt;
&lt;td&gt;Should avoid persistence implementation details&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Example: &lt;code&gt;get_post_by_id()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Example: &lt;code&gt;create_post()&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  12.4 🧬 Inheritance vs Composition vs Decorator
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;Main idea&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Inheritance&lt;/td&gt;
&lt;td&gt;Get functionality from a parent class&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Composition&lt;/td&gt;
&lt;td&gt;Use another object as a component&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Decorator&lt;/td&gt;
&lt;td&gt;Wrap an object to add behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  13. 🧭 How to Identify Which Pattern to Use
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Your problem&lt;/th&gt;
&lt;th&gt;Suitable pattern&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Database queries are scattered across controllers&lt;/td&gt;
&lt;td&gt;📚 Repository&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Need to create different notification objects&lt;/td&gt;
&lt;td&gt;🏭 Factory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Need multiple feed-generation algorithms&lt;/td&gt;
&lt;td&gt;🎯 Strategy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Need to add logging without modifying original classes&lt;/td&gt;
&lt;td&gt;🎁 Decorator&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A third-party API has an incompatible method&lt;/td&gt;
&lt;td&gt;🔌 Adapter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One class inherits too many unrelated behaviors&lt;/td&gt;
&lt;td&gt;🧬 Composition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One class is doing everything&lt;/td&gt;
&lt;td&gt;🧹 Separation of Concerns / SRP&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  14. ⚠️ Common Mistakes Beginners Make
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;❌ &lt;strong&gt;Using design patterns everywhere.&lt;/strong&gt; Not every class needs a Factory, Adapter or Strategy. Use patterns when they solve an actual problem.&lt;/li&gt;
&lt;li&gt;❌ &lt;strong&gt;Putting business logic inside repositories.&lt;/strong&gt; Repositories should primarily handle data access.&lt;/li&gt;
&lt;li&gt;❌ &lt;strong&gt;Putting SQL queries inside controllers.&lt;/strong&gt; Delegate persistence operations to the repository layer.&lt;/li&gt;
&lt;li&gt;❌ &lt;strong&gt;Confusing Factory and Strategy.&lt;/strong&gt; Factory creates objects; Strategy selects interchangeable behavior.&lt;/li&gt;
&lt;li&gt;❌ &lt;strong&gt;Confusing Adapter and Decorator.&lt;/strong&gt; Adapter changes compatibility; Decorator adds functionality.&lt;/li&gt;
&lt;li&gt;❌ &lt;strong&gt;Creating unnecessarily deep inheritance hierarchies.&lt;/strong&gt; Prefer composition when objects need to collaborate rather than inherit from each other.&lt;/li&gt;
&lt;li&gt;❌ &lt;strong&gt;Creating huge service classes.&lt;/strong&gt; Keep related business responsibilities together.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  15. 🎤 Interview Questions and Answers
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q1. What are design patterns?&lt;/strong&gt;&lt;br&gt;
Reusable approaches to commonly occurring software design problems. They help developers write maintainable, flexible and reusable code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q2. What is layered architecture?&lt;/strong&gt;&lt;br&gt;
It divides an application into separate layers such as Controller, Service and Repository, where each layer has a specific responsibility.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q3. Why do we use the Repository Pattern?&lt;/strong&gt;&lt;br&gt;
To separate database access logic from business logic, improving maintainability, reusability and testability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q4. What is the Factory Pattern?&lt;/strong&gt;&lt;br&gt;
A centralized way to create objects without exposing all object-creation details to the client.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q5. What is the Strategy Pattern?&lt;/strong&gt;&lt;br&gt;
It encapsulates different algorithms into separate interchangeable classes and lets the application select one at runtime.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q6. What is the Decorator Pattern?&lt;/strong&gt;&lt;br&gt;
It dynamically adds behavior to an existing object without modifying its original class.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q7. What is the Adapter Pattern?&lt;/strong&gt;&lt;br&gt;
It allows incompatible interfaces to work together by converting one interface into another expected interface.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q8. What is composition over inheritance?&lt;/strong&gt;&lt;br&gt;
The principle of preferring object composition and collaboration over unnecessary inheritance hierarchies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q9. What is dependency injection?&lt;/strong&gt;&lt;br&gt;
A technique in which an object receives its dependencies from outside rather than creating them internally.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q10. What are the benefits of separation of concerns?&lt;/strong&gt;&lt;br&gt;
It improves readability, maintainability, testability and reusability, and makes individual components easier to modify.&lt;/p&gt;


&lt;h2&gt;
  
  
  16. 📝 Final Revision Sheet
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern / principle&lt;/th&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;Idea&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;📚 Repository&lt;/td&gt;
&lt;td&gt;Structural / data access&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PostRepository&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Separates database operations from business logic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🏭 Factory&lt;/td&gt;
&lt;td&gt;Creational&lt;/td&gt;
&lt;td&gt;&lt;code&gt;NotificationFactory&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Creates the appropriate object&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🎯 Strategy&lt;/td&gt;
&lt;td&gt;Behavioral&lt;/td&gt;
&lt;td&gt;Payment strategies&lt;/td&gt;
&lt;td&gt;Selects between interchangeable algorithms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🎁 Decorator&lt;/td&gt;
&lt;td&gt;Structural&lt;/td&gt;
&lt;td&gt;Logging decorator&lt;/td&gt;
&lt;td&gt;Adds functionality by wrapping an object&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🔌 Adapter&lt;/td&gt;
&lt;td&gt;Structural&lt;/td&gt;
&lt;td&gt;Payment API adapter&lt;/td&gt;
&lt;td&gt;Makes incompatible interfaces work together&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🧬 Composition&lt;/td&gt;
&lt;td&gt;Design principle&lt;/td&gt;
&lt;td&gt;Car HAS an Engine&lt;/td&gt;
&lt;td&gt;Builds functionality from collaborating objects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🥞 Layered Architecture&lt;/td&gt;
&lt;td&gt;Architectural pattern&lt;/td&gt;
&lt;td&gt;Controller → Service → Repository&lt;/td&gt;
&lt;td&gt;Separates responsibilities into layers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🧹 Separation of Concerns&lt;/td&gt;
&lt;td&gt;Design principle&lt;/td&gt;
&lt;td&gt;One focused responsibility per component&lt;/td&gt;
&lt;td&gt;Keeps different responsibilities separate&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h2&gt;
  
  
  17. 🧪 Test Your Understanding!
&lt;/h2&gt;

&lt;p&gt;Try these before peeking at the answers! ❤️&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Your app supports UPI, PayPal and Card payments, and you want to choose a payment algorithm at runtime. Which pattern?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Factory&lt;/li&gt;
&lt;li&gt;B) Strategy&lt;/li&gt;
&lt;li&gt;C) Adapter&lt;/li&gt;
&lt;li&gt;D) Repository&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Your controller contains SQL queries. You want to move all database operations into a separate component. Which pattern?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Decorator&lt;/li&gt;
&lt;li&gt;B) Factory&lt;/li&gt;
&lt;li&gt;C) Repository&lt;/li&gt;
&lt;li&gt;D) Adapter&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;An external API uses &lt;code&gt;make_payment()&lt;/code&gt;, but your app expects &lt;code&gt;pay()&lt;/code&gt;. What do you use?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Adapter&lt;/li&gt;
&lt;li&gt;B) Strategy&lt;/li&gt;
&lt;li&gt;C) Decorator&lt;/li&gt;
&lt;li&gt;D) Factory&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;You want to add logging to an existing object without modifying its class. Which pattern?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Repository&lt;/li&gt;
&lt;li&gt;B) Factory&lt;/li&gt;
&lt;li&gt;C) Decorator&lt;/li&gt;
&lt;li&gt;D) Strategy&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;A &lt;code&gt;Car&lt;/code&gt; uses an &lt;code&gt;Engine&lt;/code&gt; object instead of inheriting from &lt;code&gt;Engine&lt;/code&gt;. What is this?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Inheritance&lt;/li&gt;
&lt;li&gt;B) Composition&lt;/li&gt;
&lt;li&gt;C) Adapter&lt;/li&gt;
&lt;li&gt;D) Factory&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;You want one class to create &lt;code&gt;EmailNotification&lt;/code&gt;, &lt;code&gt;SMSNotification&lt;/code&gt; or &lt;code&gt;PushNotification&lt;/code&gt; objects. Which pattern?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Strategy&lt;/li&gt;
&lt;li&gt;B) Factory&lt;/li&gt;
&lt;li&gt;C) Repository&lt;/li&gt;
&lt;li&gt;D) Decorator&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;/p&gt;
  👀 Click to reveal answers
  &lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;B&lt;/strong&gt;: Strategy. Interchangeable algorithms chosen at runtime&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C&lt;/strong&gt;: Repository. It separates data access from the controller&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A&lt;/strong&gt;: Adapter. It translates one interface into another&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C&lt;/strong&gt;: Decorator. It wraps an object to add behavior&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;B&lt;/strong&gt;: Composition. This is a HAS-A relationship&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;B&lt;/strong&gt;: Factory. It centralizes object creation&lt;/li&gt;
&lt;/ol&gt;



&lt;p&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  18. 🏁 What to Remember
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;🚫 Don't put everything in one file&lt;/li&gt;
&lt;li&gt;🛣️ Keep controllers responsible for HTTP communication&lt;/li&gt;
&lt;li&gt;🧠 Keep services responsible for business rules&lt;/li&gt;
&lt;li&gt;📚 Keep repositories responsible for database operations&lt;/li&gt;
&lt;li&gt;🏭 Use &lt;strong&gt;Factory&lt;/strong&gt; when object creation becomes complex&lt;/li&gt;
&lt;li&gt;🎯 Use &lt;strong&gt;Strategy&lt;/strong&gt; when multiple algorithms are interchangeable&lt;/li&gt;
&lt;li&gt;🎁 Use &lt;strong&gt;Decorator&lt;/strong&gt; when you need to add behavior without modifying the original object&lt;/li&gt;
&lt;li&gt;🔌 Use &lt;strong&gt;Adapter&lt;/strong&gt; when integrating incompatible external systems&lt;/li&gt;
&lt;li&gt;🧬 Prefer &lt;strong&gt;composition&lt;/strong&gt; when objects need to collaborate&lt;/li&gt;
&lt;li&gt;🔗 Aim for &lt;strong&gt;loose coupling&lt;/strong&gt; and &lt;strong&gt;high cohesion&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;🎯 The ultimate goal isn't to use as many design patterns as possible. It's to write software that is &lt;strong&gt;easy to understand, extend, test and maintain&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;If this post helped you, drop a ❤️ or 🦄 and share it with a friend learning software design. Happy coding! 👩‍💻✨&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>python</category>
      <category>softwaredevelopment</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>SOLID Principles Deep Dive (I &amp; D): Interface Segregation and Dependency Inversion Explained 🧩</title>
      <dc:creator>Deepika Pusala</dc:creator>
      <pubDate>Thu, 12 Nov 2026 06:18:16 +0000</pubDate>
      <link>https://dev.to/deepika_pusala/solid-principles-deep-dive-i-d-interface-segregation-and-dependency-inversion-explained-2doo</link>
      <guid>https://dev.to/deepika_pusala/solid-principles-deep-dive-i-d-interface-segregation-and-dependency-inversion-explained-2doo</guid>
      <description>&lt;h1&gt;
  
  
  🧩 SOLID Principles Deep Dive (I &amp;amp; D): Let's Learn from ZERO!
&lt;/h1&gt;

&lt;p&gt;We've reached the &lt;strong&gt;final two principles of SOLID&lt;/strong&gt;! 🎉 In this post we'll understand &lt;strong&gt;Interface Segregation (I)&lt;/strong&gt; and &lt;strong&gt;Dependency Inversion (D)&lt;/strong&gt; from absolute zero, with Python examples, real-world scenarios, FastAPI applications and interview questions.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔤 The Five SOLID Principles
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Letter&lt;/th&gt;
&lt;th&gt;Principle&lt;/th&gt;
&lt;th&gt;One-line idea&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;S&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Single Responsibility&lt;/td&gt;
&lt;td&gt;A class should have one responsibility&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;O&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Open/Closed&lt;/td&gt;
&lt;td&gt;Open for extension, closed for modification&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;L&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Liskov Substitution&lt;/td&gt;
&lt;td&gt;Child classes should be usable wherever their parent is expected&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;I&lt;/strong&gt; ⭐&lt;/td&gt;
&lt;td&gt;Interface Segregation&lt;/td&gt;
&lt;td&gt;Don't force a class to implement methods it doesn't need&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;D&lt;/strong&gt; ⭐&lt;/td&gt;
&lt;td&gt;Dependency Inversion&lt;/td&gt;
&lt;td&gt;Depend on abstractions, not concrete implementations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;(Today's topics are marked with ⭐.)&lt;/em&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  🧱 PART 1: Interface Segregation Principle (ISP)
&lt;/h1&gt;

&lt;h2&gt;
  
  
  1. 🤔 First, what is an interface?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 An &lt;strong&gt;interface&lt;/strong&gt; is a &lt;strong&gt;contract&lt;/strong&gt; that specifies what operations an object should provide.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Think about a &lt;strong&gt;TV remote&lt;/strong&gt; 📺. It has buttons for power, volume, channel and mute. You don't need to know how the TV performs those operations internally. You only need to know &lt;strong&gt;which operations are available&lt;/strong&gt;. That's the basic idea of an interface.&lt;/p&gt;

&lt;p&gt;In Python, we commonly represent interfaces using:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Abstract Base Classes (ABC)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Abstract methods&lt;/strong&gt; using &lt;code&gt;@abstractmethod&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Protocols&lt;/strong&gt; using &lt;code&gt;typing.Protocol&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We'll use the ABC approach first.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. 📜 What is the Interface Segregation Principle?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;Clients should not be forced to depend on interfaces they do not use.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In simple English: imagine an interface with &lt;strong&gt;10 methods&lt;/strong&gt;, but a particular class only needs &lt;strong&gt;3&lt;/strong&gt;. Should you force that class to implement all 10? &lt;strong&gt;NO!&lt;/strong&gt; Instead, divide the large interface into &lt;strong&gt;smaller, specific interfaces&lt;/strong&gt;, so each class implements only the methods relevant to it. That's ISP.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. 🚨 The problem, with a real-world example
&lt;/h2&gt;

&lt;p&gt;An employee management system has three kinds of employees: &lt;strong&gt;Developer, Manager, Intern&lt;/strong&gt;. Suppose one common &lt;code&gt;Employee&lt;/code&gt; interface contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;work()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;attend_meeting()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;approve_leave()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;write_code()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But look at what each role really does:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Employee&lt;/th&gt;
&lt;th&gt;Work&lt;/th&gt;
&lt;th&gt;Meeting&lt;/th&gt;
&lt;th&gt;Approve leave&lt;/th&gt;
&lt;th&gt;Write code&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Developer&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Manager&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Not necessarily&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Intern&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A developer or an intern shouldn't be forced to implement &lt;code&gt;approve_leave()&lt;/code&gt;. The interface is too large and mixes unrelated responsibilities. That's called a &lt;strong&gt;fat interface&lt;/strong&gt;. 🐷&lt;/p&gt;

&lt;h2&gt;
  
  
  4. ❌ Without ISP: bad implementation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;abc&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="no"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;abstractmethod&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Employee&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nd"&gt;@abstractmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="nd"&gt;@abstractmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;attend_meeting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="nd"&gt;@abstractmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;approve_leave&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="nd"&gt;@abstractmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;write_code&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Employee&lt;/code&gt; is an abstract class that forces every child to implement all four methods. Now a developer:&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;Developer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Employee&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Developer is working"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;attend_meeting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Developer is attending meeting"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;approve_leave&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;NotImplementedError&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;write_code&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Developer is writing code"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See &lt;code&gt;approve_leave()&lt;/code&gt;? A developer doesn't approve leave! We're implementing a method the developer doesn't need, which is exactly the problem ISP prevents.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problems with this design:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Unnecessary methods are forced on classes&lt;/li&gt;
&lt;li&gt;Classes contain meaningless implementations&lt;/li&gt;
&lt;li&gt;Changes to the large interface can affect unrelated classes&lt;/li&gt;
&lt;li&gt;Code becomes harder to maintain&lt;/li&gt;
&lt;li&gt;It can cause &lt;strong&gt;runtime errors&lt;/strong&gt; when unsupported methods are called&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  5. ✅ With ISP: correct implementation
&lt;/h2&gt;

&lt;p&gt;Instead of one big interface, split it into smaller ones:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before ISP:  Employee → work() · attend_meeting() · approve_leave() · write_code()
                        (ONE LARGE INTERFACE)

After ISP:   Workable            → work()
             MeetingParticipant  → attend_meeting()
             LeaveApprover       → approve_leave()
             CodeWriter          → write_code()
                        (small, focused interfaces)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;abc&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="no"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;abstractmethod&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Workable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nd"&gt;@abstractmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MeetingParticipant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nd"&gt;@abstractmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;attend_meeting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LeaveApprover&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nd"&gt;@abstractmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;approve_leave&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CodeWriter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nd"&gt;@abstractmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;write_code&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now each interface has one specific responsibility. Let's build the classes:&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;Developer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Workable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;MeetingParticipant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;CodeWriter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Developer is working"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;attend_meeting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Developer is attending meeting"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;write_code&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Developer is writing code"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Manager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Workable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;MeetingParticipant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;LeaveApprover&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Manager is managing the team"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;attend_meeting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Manager is attending meeting"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;approve_leave&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Manager approved leave"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Intern&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Workable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;MeetingParticipant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;CodeWriter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Intern is learning and working"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;attend_meeting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Intern is attending meeting"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;write_code&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Intern is writing code"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each class implements &lt;strong&gt;only the relevant interfaces&lt;/strong&gt;. Nobody is forced to implement unnecessary methods. That's the Interface Segregation Principle! 🎯&lt;/p&gt;

&lt;h2&gt;
  
  
  6. 🐷 Fat interface vs thin interface
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Fat interface&lt;/th&gt;
&lt;th&gt;Thin interface&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Contains too many methods&lt;/td&gt;
&lt;td&gt;Contains fewer focused methods&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Includes unrelated operations&lt;/td&gt;
&lt;td&gt;Includes related operations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Forces unnecessary implementations&lt;/td&gt;
&lt;td&gt;Classes implement what they need&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Difficult to maintain&lt;/td&gt;
&lt;td&gt;Easier to maintain&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 &lt;strong&gt;Fat interface&lt;/strong&gt; = too many unrelated responsibilities. &lt;strong&gt;Thin interface&lt;/strong&gt; = a small, focused contract.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  7. 💳 Another example: a payment system
&lt;/h2&gt;

&lt;p&gt;E-commerce payment methods: &lt;strong&gt;credit card, UPI, cash on delivery&lt;/strong&gt;. Suppose you create one interface:&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;PaymentMethod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nd"&gt;@abstractmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="nd"&gt;@abstractmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;refund&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="nd"&gt;@abstractmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;scan_qr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Credit cards can pay and refund&lt;/li&gt;
&lt;li&gt;UPI can pay, refund and scan QR codes&lt;/li&gt;
&lt;li&gt;Cash on delivery might not support online refunds or QR scanning&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One interface forces every payment method to support everything. Split it instead:&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;Payable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nd"&gt;@abstractmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Refundable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nd"&gt;@abstractmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;refund&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;QRScannable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nd"&gt;@abstractmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;scan_qr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now payment implementations choose the capabilities they actually support. This is useful in payment gateways, notification systems, file processing and authentication systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. ⚡ ISP in real full-stack development
&lt;/h2&gt;

&lt;p&gt;In a social-feed backend you might have &lt;strong&gt;normal users, admins and content moderators&lt;/strong&gt;, with operations like create post, like post, comment, delete any user's post, block users and manage reports. Would you give every user every operation? Of course not! A normal user creates posts and comments, while an admin can also delete inappropriate posts and manage users. Separate these capabilities into focused interfaces:&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;PostCreator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nd"&gt;@abstractmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostModerator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nd"&gt;@abstractmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;delete_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserManager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nd"&gt;@abstractmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;block_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Different classes implement different capabilities, which makes it easier to extend when new roles and permissions arrive.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ ISP is about designing &lt;strong&gt;small, cohesive&lt;/strong&gt; interfaces. It does &lt;strong&gt;not&lt;/strong&gt; mean every single method needs its own interface. Group methods that naturally belong together.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  9. ✅ Advantages of ISP
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;🧹 &lt;strong&gt;Less unnecessary code:&lt;/strong&gt; classes don't implement methods they don't need&lt;/li&gt;
&lt;li&gt;🔗 &lt;strong&gt;Loose coupling:&lt;/strong&gt; classes depend on fewer operations&lt;/li&gt;
&lt;li&gt;🛠️ &lt;strong&gt;Easy maintenance:&lt;/strong&gt; changes to one interface are less likely to affect unrelated classes&lt;/li&gt;
&lt;li&gt;📖 &lt;strong&gt;Better readability:&lt;/strong&gt; each interface has a clear purpose&lt;/li&gt;
&lt;li&gt;🧪 &lt;strong&gt;Better testing:&lt;/strong&gt; test smaller components independently&lt;/li&gt;
&lt;li&gt;📈 &lt;strong&gt;Better scalability:&lt;/strong&gt; new capabilities don't require changing a huge interface&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  10. 🕵️ When should you use ISP?
&lt;/h2&gt;

&lt;p&gt;Use it when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An interface has too many unrelated methods&lt;/li&gt;
&lt;li&gt;Classes implement methods they don't actually support&lt;/li&gt;
&lt;li&gt;You see &lt;code&gt;NotImplementedError&lt;/code&gt; in classes just because the parent interface forces it&lt;/li&gt;
&lt;li&gt;Different clients need different subsets of operations&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ ISP doesn't mean creating hundreds of tiny interfaces without a reason. &lt;strong&gt;Over-segregation&lt;/strong&gt; also makes code hard to understand.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  🔄 PART 2: Dependency Inversion Principle (DIP)
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;🏛️ One of the most important SOLID principles for understanding professional &lt;strong&gt;backend architecture&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To understand DIP we need three concepts first: &lt;strong&gt;dependency&lt;/strong&gt;, &lt;strong&gt;abstraction vs concretion&lt;/strong&gt;, and &lt;strong&gt;dependency inversion&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. 🔗 What is a dependency?
&lt;/h2&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;Engine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Engine started"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start_car&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&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;p&gt;The car needs an engine to operate, so &lt;code&gt;Car&lt;/code&gt; &lt;strong&gt;depends on&lt;/strong&gt; &lt;code&gt;Engine&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 When one class needs another class to perform its work, the first class has a &lt;strong&gt;dependency&lt;/strong&gt; on the second.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Car class    (needs an engine to work)
     │  depends on
     ▼
Engine class (provides start() functionality)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But what if you want an &lt;strong&gt;electric&lt;/strong&gt; or &lt;strong&gt;hybrid&lt;/strong&gt; engine? &lt;code&gt;Car&lt;/code&gt; directly creates a specific &lt;code&gt;Engine&lt;/code&gt;, which makes it &lt;strong&gt;tightly coupled&lt;/strong&gt; to that implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. 🪢 What is tight coupling?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Coupling&lt;/strong&gt; describes how strongly two software components depend on each other.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tight coupling:&lt;/strong&gt; one class is strongly dependent on the internal implementation of another&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Loose coupling:&lt;/strong&gt; one class depends on a &lt;strong&gt;stable contract&lt;/strong&gt; rather than a particular implementation
&lt;/li&gt;
&lt;/ul&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;DieselEngine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Diesel engine started"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DieselEngine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start_car&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&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;p&gt;The problem line:&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="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DieselEngine&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;p&gt;&lt;code&gt;Car&lt;/code&gt; directly creates a &lt;code&gt;DieselEngine&lt;/code&gt;. Tomorrow you need an electric one:&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;ElectricEngine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Electric engine started"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...and now you must &lt;strong&gt;modify the &lt;code&gt;Car&lt;/code&gt; class&lt;/strong&gt;. As the app grows with many implementations, changing them becomes hard when business logic directly creates them. That's tight coupling. 😩&lt;/p&gt;

&lt;h2&gt;
  
  
  3. 🎭 What is abstraction?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;Abstraction&lt;/strong&gt; means hiding implementation details and exposing only the essential functionality.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When you transfer money in a banking app 🏦, you enter the amount, enter the receiver and click &lt;strong&gt;Transfer&lt;/strong&gt;. Do you need to know the database queries, server communication, transaction validation and network operations? &lt;strong&gt;No!&lt;/strong&gt; You interact with one simple operation, and the complicated internals are hidden. That's abstraction.&lt;/p&gt;

&lt;h3&gt;
  
  
  ⚖️ Abstraction vs implementation
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Abstraction&lt;/th&gt;
&lt;th&gt;Implementation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;What&lt;/strong&gt; should happen&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;How&lt;/strong&gt; it happens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Defines a contract&lt;/td&gt;
&lt;td&gt;Provides actual logic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Focuses on functionality&lt;/td&gt;
&lt;td&gt;Focuses on internal details&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Example: &lt;em&gt;Send notification&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;Example: &lt;em&gt;Send using email&lt;/em&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In Python:&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;from&lt;/span&gt; &lt;span class="n"&gt;abc&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="no"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;abstractmethod&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NotificationService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nd"&gt;@abstractmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is an &lt;strong&gt;abstraction&lt;/strong&gt;. It says &lt;em&gt;"any notification service must provide a &lt;code&gt;send()&lt;/code&gt; method"&lt;/em&gt;, but not how to send. Now implement it in different ways:&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;EmailNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;NotificationService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Sending email:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SMSNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;NotificationService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Sending SMS:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PushNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;NotificationService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Sending push notification:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are &lt;strong&gt;concrete implementations&lt;/strong&gt;: each follows the same contract but performs the operation differently.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;              NotificationService      ← abstraction · send(message)
             ╱         │          ╲
          Email       SMS         Push     ← concrete implementations
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🧱 What is a concretion?
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;concretion&lt;/strong&gt; is an actual class that provides the implementation of an abstraction.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;NotificationService&lt;/code&gt; → abstraction&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;EmailNotification&lt;/code&gt;, &lt;code&gt;SMSNotification&lt;/code&gt;, &lt;code&gt;PushNotification&lt;/code&gt; → concrete implementations&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 &lt;strong&gt;Abstraction defines WHAT. Concretion defines HOW.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  4. 📜 What exactly is the Dependency Inversion Principle?
&lt;/h2&gt;

&lt;p&gt;The official definition:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;High-level modules should not depend on low-level modules. Both should depend on abstractions.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Abstractions should not depend on details. Details should depend on abstractions.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;Translated: your application has two kinds of layers.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;High-level module:&lt;/strong&gt; contains important &lt;strong&gt;business logic&lt;/strong&gt; (e.g. an order service that processes customer orders)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Low-level module:&lt;/strong&gt; performs &lt;strong&gt;technical operations&lt;/strong&gt; (sending emails, saving to a database, uploading images, sending SMS)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The high-level business logic should not be tightly connected to one specific low-level implementation. Instead, &lt;strong&gt;both depend on an abstraction&lt;/strong&gt;. That's DIP!&lt;/p&gt;

&lt;h2&gt;
  
  
  5. 🏗️ High-level vs low-level modules
&lt;/h2&gt;

&lt;p&gt;A customer places an order. What should happen?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Validate the order&lt;/li&gt;
&lt;li&gt;Calculate the total&lt;/li&gt;
&lt;li&gt;Save the order&lt;/li&gt;
&lt;li&gt;Send a confirmation email&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OrderService&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;🔝 High-level module&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EmailService&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;🔧 Low-level module&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DatabaseRepository&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;🔧 Low-level module&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PaymentGateway&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;🔧 Low-level module&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;OrderService&lt;/code&gt; is high-level because it contains the important &lt;strong&gt;business workflow&lt;/strong&gt;. Email and database services are low-level because they perform &lt;strong&gt;specific technical operations&lt;/strong&gt; used by that workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. ❌ Without DIP: bad implementation
&lt;/h2&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;EmailService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Email sent:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email_service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;EmailService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;place_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Order placed successfully"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s2"&gt;"Your order has been confirmed"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key line:&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="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email_service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;EmailService&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;p&gt;&lt;code&gt;OrderService&lt;/code&gt; directly creates an &lt;code&gt;EmailService&lt;/code&gt;, so:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It knows about &lt;code&gt;EmailService&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;It's tightly coupled to &lt;code&gt;EmailService&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;It can't easily switch to SMS or push notifications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your manager says &lt;em&gt;"we now want order confirmations through SMS instead of email"&lt;/em&gt;, you must change &lt;code&gt;OrderService&lt;/code&gt;. That's undesirable, because &lt;strong&gt;the business workflow shouldn't change just because the notification technology changes&lt;/strong&gt;. This violates DIP. 🚨&lt;/p&gt;

&lt;h2&gt;
  
  
  7. ✅ With DIP: correct implementation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Step 1: create an abstraction&lt;/strong&gt;&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;from&lt;/span&gt; &lt;span class="n"&gt;abc&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="no"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;abstractmethod&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NotificationService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nd"&gt;@abstractmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: create concrete implementations&lt;/strong&gt;&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;EmailService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;NotificationService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Email sent:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SMSService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;NotificationService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"SMS sent:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3: modify the high-level module&lt;/strong&gt;&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;OrderService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;notification_service&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;notification_service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;notification_service&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;place_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Order placed successfully"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;notification_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s2"&gt;"Your order has been confirmed"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Previously we created the email service &lt;strong&gt;inside&lt;/strong&gt; &lt;code&gt;OrderService&lt;/code&gt;. Now we &lt;strong&gt;receive&lt;/strong&gt; the notification service from outside. A major difference! 🌟&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: inject the implementation&lt;/strong&gt;&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;email_service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;EmailService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;order_service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OrderService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email_service&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;order_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;place_order&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order placed successfully
Email sent: Your order has been confirmed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Want SMS instead?&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;sms_service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SMSService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;order_service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OrderService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sms_service&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;order_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;place_order&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order placed successfully
SMS sent: Your order has been confirmed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Did we modify &lt;code&gt;OrderService&lt;/code&gt;? &lt;strong&gt;NO!&lt;/strong&gt; We just passed a different implementation. That's the power of DIP. 💪&lt;/p&gt;

&lt;h2&gt;
  
  
  8. 🔍 Exactly what changed?
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❌ Without DIP
OrderService (business logic) ──▶ EmailService (specific implementation)
                  tightly coupled

✅ With DIP
OrderService (high-level) ──▶ NotificationService (abstraction / contract) ◀── EmailService
                                                                             ◀── SMSService
                  loosely coupled architecture
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Previously: &lt;code&gt;OrderService → EmailService&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;After applying DIP: &lt;code&gt;OrderService → NotificationService ← EmailService&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Both&lt;/strong&gt; the high-level and low-level components depend on the abstraction. The dependency direction has been &lt;strong&gt;inverted&lt;/strong&gt;. 🔄&lt;/p&gt;

&lt;h2&gt;
  
  
  9. 💉 What is Dependency Injection (DI)?
&lt;/h2&gt;

&lt;p&gt;You may have heard about Dependency Injection in FastAPI. &lt;strong&gt;DIP and DI are related, but they are NOT the same.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;Dependency Injection&lt;/strong&gt; means providing an object or dependency to a class &lt;strong&gt;from outside&lt;/strong&gt; instead of making that class create it internally.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Without DI:&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;OrderService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;payment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PaymentService&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;p&gt;With DI:&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;OrderService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;payment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;payment&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ⚖️ DIP vs DI
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;DIP&lt;/th&gt;
&lt;th&gt;DI&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A design &lt;strong&gt;principle&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;A technique / pattern&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Focuses on &lt;strong&gt;abstractions&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Focuses on &lt;strong&gt;providing dependencies&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reduces tight coupling&lt;/td&gt;
&lt;td&gt;Helps achieve loose coupling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Architectural guideline&lt;/td&gt;
&lt;td&gt;Practical implementation technique&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DIP&lt;/strong&gt; tells you how your architecture should be designed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DI&lt;/strong&gt; gives you a way to supply the required objects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can use DI to implement DIP. But simply injecting a &lt;strong&gt;concrete&lt;/strong&gt; class doesn't automatically guarantee DIP. The dependency should be designed around an appropriate &lt;strong&gt;abstraction&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. ⚡ How does this work in FastAPI?
&lt;/h2&gt;

&lt;p&gt;FastAPI has a dependency injection system called &lt;code&gt;Depends()&lt;/code&gt;:&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;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&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="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Deepika"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;UserService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/users/{user_id}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UserService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_user_service&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A client sends a GET request&lt;/li&gt;
&lt;li&gt;FastAPI sees &lt;code&gt;Depends(get_user_service)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;FastAPI calls the dependency provider&lt;/li&gt;
&lt;li&gt;The provider creates and returns a &lt;code&gt;UserService&lt;/code&gt; object&lt;/li&gt;
&lt;li&gt;FastAPI passes that object to the endpoint&lt;/li&gt;
&lt;li&gt;The endpoint calls &lt;code&gt;service.get_user()&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The endpoint doesn't create the service itself. That's dependency injection.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP Request  GET /users/10
      ▼
FastAPI Router → endpoint receives the request
      ▼
Depends()      → requests the dependency
      ▼
get_user_service() → provides a UserService object
      ▼
Endpoint uses the service → service.get_user(user_id)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;📝 &lt;code&gt;Depends()&lt;/code&gt; is the &lt;strong&gt;DI mechanism&lt;/strong&gt;. To achieve DIP, you should also design your service and repository dependencies around &lt;strong&gt;abstractions&lt;/strong&gt; when appropriate.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  11. 🗄️ A more realistic FastAPI example: the repository pattern
&lt;/h2&gt;

&lt;p&gt;A very important example for backend architecture. Your layers: &lt;strong&gt;API/router → service → repository → database&lt;/strong&gt;. The service needs user data, but it shouldn't need to know &lt;em&gt;which database technology&lt;/em&gt; is used. Today it's PostgreSQL. Tomorrow it might be another database, or an &lt;strong&gt;in-memory repository for testing&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: define the repository abstraction&lt;/strong&gt;&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;from&lt;/span&gt; &lt;span class="n"&gt;abc&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="no"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;abstractmethod&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nd"&gt;@abstractmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: create a concrete implementation&lt;/strong&gt;&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;PostgreSQLUserRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;UserRepository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Fetching user from PostgreSQL"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Deepika"&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;p&gt;&lt;strong&gt;Step 3: create the high-level service&lt;/strong&gt;&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;UserService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UserRepository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;repository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user_details&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice &lt;code&gt;repository: UserRepository&lt;/code&gt;. The service depends on the &lt;strong&gt;abstraction&lt;/strong&gt;, not directly on &lt;code&gt;PostgreSQLUserRepository&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: inject the implementation&lt;/strong&gt;&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;repository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PostgreSQLUserRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;user_service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;UserService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_user_details&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="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Fetching user from PostgreSQL
{'id': 10, 'name': 'Deepika'}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now replace PostgreSQL with an in-memory implementation:&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;InMemoryUserRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;UserRepository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Test User"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="n"&gt;repository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;InMemoryUserRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;user_service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;UserService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_user_details&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="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You changed the repository implementation &lt;strong&gt;without changing &lt;code&gt;UserService&lt;/code&gt;&lt;/strong&gt;. That's DIP in action. 🎯&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real-world connection&lt;/strong&gt; to a social-feed backend:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;PostService&lt;/code&gt; → post-related business logic&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PostRepository&lt;/code&gt; → an interface for post data access&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PostgresPostRepository&lt;/code&gt; → the actual PostgreSQL implementation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The service shouldn't need to know the SQL queries the repository uses.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📝 This is a simplified architectural example. A real SQLAlchemy implementation may use &lt;code&gt;AsyncSession&lt;/code&gt;, async repository methods and FastAPI dependencies.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  12. 🧪 Why is DIP great for testing?
&lt;/h2&gt;

&lt;p&gt;When testing &lt;code&gt;UserService&lt;/code&gt;, it normally connects to PostgreSQL. But what if your database is unavailable, you don't want to create real records, you want predictable test data, or you want to test business logic independently? &lt;strong&gt;Inject a fake repository:&lt;/strong&gt;&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;FakeUserRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;UserRepository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Testing User"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="n"&gt;fake_repository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FakeUserRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;UserService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fake_repository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_user_details&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="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"Testing User"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We tested &lt;code&gt;UserService&lt;/code&gt; &lt;strong&gt;without connecting to PostgreSQL&lt;/strong&gt;! That's possible because the service depends on an abstraction, so we can supply a different implementation. A hugely useful technique for unit testing.&lt;/p&gt;

&lt;h2&gt;
  
  
  13. 🧠 DIP vs DI vs IoC
&lt;/h2&gt;

&lt;p&gt;These three terms confuse everyone. Let's separate them:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DIP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High-level and low-level modules depend on abstractions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dependencies are provided from outside&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;IoC&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Control over object creation or execution is handed to another component or framework&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Inversion of Control (IoC)&lt;/strong&gt; is the broader concept. In a traditional program you create and manage all objects yourself. With FastAPI, the framework manages dependency resolution when it processes a request. That's IoC. &lt;strong&gt;DI&lt;/strong&gt; is one way to implement IoC, and &lt;strong&gt;DIP&lt;/strong&gt; is an architectural principle that keeps components independent of concrete implementations.&lt;/p&gt;

&lt;h2&gt;
  
  
  14. ✅ Advantages of DIP
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;🔗 &lt;strong&gt;Loose coupling:&lt;/strong&gt; business logic isn't tied to specific technologies&lt;/li&gt;
&lt;li&gt;🔄 &lt;strong&gt;Easy replacement:&lt;/strong&gt; swap PostgreSQL for another repository implementation&lt;/li&gt;
&lt;li&gt;🧪 &lt;strong&gt;Easy testing:&lt;/strong&gt; inject fake or mock implementations&lt;/li&gt;
&lt;li&gt;🛠️ &lt;strong&gt;Maintainability:&lt;/strong&gt; technical changes don't necessarily affect business logic&lt;/li&gt;
&lt;li&gt;📈 &lt;strong&gt;Scalability:&lt;/strong&gt; add new implementations without rewriting business logic&lt;/li&gt;
&lt;li&gt;🔀 &lt;strong&gt;Flexibility:&lt;/strong&gt; switch email providers, payment gateways, storage services and more&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  15. 🕵️ When should you use DIP?
&lt;/h2&gt;

&lt;p&gt;Use it when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Business logic directly creates database or external service objects&lt;/li&gt;
&lt;li&gt;You want to support multiple implementations&lt;/li&gt;
&lt;li&gt;You want to test business logic independently&lt;/li&gt;
&lt;li&gt;Your application has multiple architectural layers&lt;/li&gt;
&lt;li&gt;You want to avoid tightly coupling services to specific technologies&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Don't create abstractions for &lt;strong&gt;every&lt;/strong&gt; class unnecessarily. A simple app with no meaningful variation may not need a separate interface for every dependency.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  ⚠️ PART 3: Common Mistakes and Important Differences
&lt;/h1&gt;

&lt;h2&gt;
  
  
  1. ⚔️ ISP vs DIP
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ISP&lt;/th&gt;
&lt;th&gt;DIP&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Interface Segregation Principle&lt;/td&gt;
&lt;td&gt;Dependency Inversion Principle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Focuses on &lt;strong&gt;interface size and design&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Focuses on &lt;strong&gt;dependency direction&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Avoids unnecessary methods&lt;/td&gt;
&lt;td&gt;Avoids dependence on concrete implementations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Uses small, focused interfaces&lt;/td&gt;
&lt;td&gt;Uses abstractions between modules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Example: separate payment capabilities&lt;/td&gt;
&lt;td&gt;Example: &lt;code&gt;PaymentService&lt;/code&gt; depends on a &lt;code&gt;PaymentGateway&lt;/code&gt; abstraction&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 &lt;strong&gt;ISP:&lt;/strong&gt; &lt;em&gt;"Don't force me to implement methods I don't need."&lt;/em&gt; &lt;strong&gt;DIP:&lt;/strong&gt; &lt;em&gt;"Don't force me to depend on a specific implementation."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  2. 📋 Abstraction vs concretion: quick revision
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;Abstraction&lt;/th&gt;
&lt;th&gt;Concrete implementation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Notification&lt;/td&gt;
&lt;td&gt;&lt;code&gt;NotificationService&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;EmailService&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database&lt;/td&gt;
&lt;td&gt;&lt;code&gt;UserRepository&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PostgreSQLUserRepository&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Payment&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PaymentGateway&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;RazorpayGateway&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage&lt;/td&gt;
&lt;td&gt;&lt;code&gt;FileStorage&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;S3Storage&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authentication&lt;/td&gt;
&lt;td&gt;&lt;code&gt;AuthProvider&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GoogleAuthProvider&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;📝 These are examples of possible designs. An abstraction doesn't have to be a Python ABC. It can also be a &lt;code&gt;Protocol&lt;/code&gt; or another suitable contract.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  3. ❌ Common mistakes to avoid
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Mistake 1: thinking DIP and DI are the same.&lt;/strong&gt; They aren't. DIP is a principle; DI is a technique.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mistake 2: thinking every interface must have only one method.&lt;/strong&gt; No. Interfaces should group &lt;em&gt;related&lt;/em&gt; operations. They should just avoid forcing &lt;em&gt;unrelated&lt;/em&gt; operations on clients.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mistake 3: thinking abstraction means hiding all code.&lt;/strong&gt; No. Abstraction means exposing essential functionality while hiding unnecessary implementation details.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mistake 4: thinking DIP means never depending on concrete classes.&lt;/strong&gt; Not exactly. Concrete implementations must exist and be instantiated somewhere. The important thing is that high-level business logic shouldn't be tightly coupled to them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mistake 5: thinking FastAPI's &lt;code&gt;Depends()&lt;/code&gt; automatically guarantees DIP.&lt;/strong&gt; No. It provides dependency injection, but your architecture must still be designed appropriately.&lt;/p&gt;




&lt;h1&gt;
  
  
  🎤 PART 4: Interview Preparation
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Q1. What is the Interface Segregation Principle?&lt;/strong&gt;&lt;br&gt;
ISP states that clients should not be forced to depend on interfaces they do not use. We divide large interfaces into smaller, focused ones so classes only implement relevant methods.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q2. What is a fat interface?&lt;/strong&gt;&lt;br&gt;
An interface containing too many methods, often including unrelated operations that force implementing classes to provide unnecessary functionality.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q3. What is the Dependency Inversion Principle?&lt;/strong&gt;&lt;br&gt;
DIP states that high-level modules should not depend directly on low-level modules. Both should depend on abstractions. This reduces coupling and makes applications easier to maintain and test.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q4. What's the difference between abstraction and implementation?&lt;/strong&gt;&lt;br&gt;
Abstraction defines &lt;em&gt;what&lt;/em&gt; functionality should be provided, while implementation defines &lt;em&gt;how&lt;/em&gt; it's performed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q5. What is tight coupling?&lt;/strong&gt;&lt;br&gt;
When one component strongly depends on another component's specific implementation, making changes and replacements difficult.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q6. What is Dependency Injection?&lt;/strong&gt;&lt;br&gt;
A technique in which a class receives its required dependencies from an external source instead of creating them internally.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q7. How does FastAPI support Dependency Injection?&lt;/strong&gt;&lt;br&gt;
Through &lt;code&gt;Depends()&lt;/code&gt;, which lets dependencies such as services, database sessions and authentication logic be provided to endpoints.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q8. How does DIP improve testing?&lt;/strong&gt;&lt;br&gt;
Since business logic depends on abstractions, we can inject mock or fake implementations instead of connecting to actual external services or databases.&lt;/p&gt;


&lt;h1&gt;
  
  
  🧪 PART 5: Test Your Understanding!
&lt;/h1&gt;

&lt;p&gt;Try these before peeking at the answers! ❤️&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;A class is forced to implement a method that it never uses. Which principle is violated?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) DIP&lt;/li&gt;
&lt;li&gt;B) ISP&lt;/li&gt;
&lt;li&gt;C) SRP&lt;/li&gt;
&lt;li&gt;D) OCP&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;An &lt;code&gt;OrderService&lt;/code&gt; directly creates &lt;code&gt;EmailService&lt;/code&gt; inside its constructor. What's the main design problem?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Tight coupling&lt;/li&gt;
&lt;li&gt;B) Encapsulation&lt;/li&gt;
&lt;li&gt;C) Polymorphism&lt;/li&gt;
&lt;li&gt;D) Normalization&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;What should high-level and low-level modules depend on, according to DIP?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Concrete classes&lt;/li&gt;
&lt;li&gt;B) Database tables&lt;/li&gt;
&lt;li&gt;C) Abstractions&lt;/li&gt;
&lt;li&gt;D) Global variables&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;You pass a fake repository into &lt;code&gt;UserService&lt;/code&gt; for testing. What technique are you using?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Inheritance only&lt;/li&gt;
&lt;li&gt;B) Dependency Injection&lt;/li&gt;
&lt;li&gt;C) Normalization&lt;/li&gt;
&lt;li&gt;D) Overloading&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Which is a concrete implementation?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) &lt;code&gt;NotificationService&lt;/code&gt; abstraction&lt;/li&gt;
&lt;li&gt;B) &lt;code&gt;UserRepository&lt;/code&gt; contract&lt;/li&gt;
&lt;li&gt;C) &lt;code&gt;EmailService&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;D) Payment interface&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;/p&gt;
  👀 Click to reveal answers
  &lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;B&lt;/strong&gt;: ISP. The class is being forced to depend on a method it doesn't use&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A&lt;/strong&gt;: Tight coupling. The service is bound to one specific implementation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C&lt;/strong&gt;: Abstractions. Both layers should depend on contracts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;B&lt;/strong&gt;: Dependency Injection. The dependency is supplied from outside&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C&lt;/strong&gt;: &lt;code&gt;EmailService&lt;/code&gt; is an actual class that provides the implementation&lt;/li&gt;
&lt;/ol&gt;



&lt;p&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  📝 PART 6: Final Revision, All Five SOLID Principles
&lt;/h1&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Letter&lt;/th&gt;
&lt;th&gt;Principle&lt;/th&gt;
&lt;th&gt;Idea&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;S&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Single Responsibility&lt;/td&gt;
&lt;td&gt;One class should have one reason to change&lt;/td&gt;
&lt;td&gt;Separate email sending from order processing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;O&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Open/Closed&lt;/td&gt;
&lt;td&gt;Open for extension, closed for modification&lt;/td&gt;
&lt;td&gt;Add a new payment method without modifying existing payment logic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;L&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Liskov Substitution&lt;/td&gt;
&lt;td&gt;Child classes should be replaceable wherever their parent is expected, without breaking correct behavior&lt;/td&gt;
&lt;td&gt;Any valid subtype of a payment processor satisfies the payment contract&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;I&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Interface Segregation&lt;/td&gt;
&lt;td&gt;Don't force classes to implement methods they don't need&lt;/td&gt;
&lt;td&gt;Separate &lt;code&gt;Workable&lt;/code&gt;, &lt;code&gt;LeaveApprover&lt;/code&gt; and &lt;code&gt;CodeWriter&lt;/code&gt; interfaces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;D&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dependency Inversion&lt;/td&gt;
&lt;td&gt;Depend on abstractions, not concrete implementations&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;UserService&lt;/code&gt; depends on &lt;code&gt;UserRepository&lt;/code&gt;, not &lt;code&gt;PostgreSQLUserRepository&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  🧭 Five Questions to Ask When Designing Your Backend
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;S:&lt;/strong&gt; Does this class have too many responsibilities?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;O:&lt;/strong&gt; Can I add new functionality without unnecessarily changing existing code?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;L:&lt;/strong&gt; Can I replace a child implementation without breaking the expected behavior?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I:&lt;/strong&gt; Am I forcing a class to implement unnecessary methods?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;D:&lt;/strong&gt; Am I depending on a concrete implementation when I should depend on an abstraction?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you can answer these while designing your FastAPI services, repositories and database layers, you're starting to think like a &lt;strong&gt;software engineer&lt;/strong&gt;, not just someone who writes code. 🧠&lt;/p&gt;




&lt;p&gt;If this post helped you, drop a ❤️ or 🦄 and share it with a friend learning software design. Happy coding! 👩‍💻✨&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>python</category>
      <category>softwareengineering</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>SOLID Principles Deep Dive (S, O, L): Explained from Absolute Zero 🧱</title>
      <dc:creator>Deepika Pusala</dc:creator>
      <pubDate>Thu, 12 Nov 2026 06:11:35 +0000</pubDate>
      <link>https://dev.to/deepika_pusala/solid-principles-deep-dive-s-o-l-explained-from-absolute-zero-3g49</link>
      <guid>https://dev.to/deepika_pusala/solid-principles-deep-dive-s-o-l-explained-from-absolute-zero-3g49</guid>
      <description>&lt;h1&gt;
  
  
  🧱 SOLID Principles Deep Dive (S, O, L): Let's Learn from ZERO!
&lt;/h1&gt;

&lt;p&gt;SOLID shows up in Python, FastAPI, Java, Spring Boot, React architecture and almost every real-world codebase. Interviewers and mentors rarely stop at "define SOLID". They ask &lt;strong&gt;why&lt;/strong&gt; we use it, &lt;strong&gt;what problem it solves&lt;/strong&gt;, and &lt;strong&gt;how you apply it in a project&lt;/strong&gt;. 🎯&lt;/p&gt;

&lt;p&gt;In this post we'll cover the first three principles, &lt;strong&gt;S, O and L&lt;/strong&gt;, step by step, with simple examples and practical code. 🚀&lt;/p&gt;




&lt;h2&gt;
  
  
  1. 🤔 First, What Are SOLID Principles?
&lt;/h2&gt;

&lt;p&gt;Imagine you're building an &lt;strong&gt;Instagram-like social media app&lt;/strong&gt; with registration and login, posts, image uploads, likes, comments, notifications and follows. At first you write everything in one huge class:&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;SocialMedia&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;register_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;login_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;upload_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;like_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;comment_on_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_notification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works, right? But when the app grows to &lt;strong&gt;100 features&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;😵 Changing login might accidentally affect registration&lt;/li&gt;
&lt;li&gt;😵 Changing image upload might affect post creation&lt;/li&gt;
&lt;li&gt;🧪 Testing individual features becomes difficult&lt;/li&gt;
&lt;li&gt;♻️ Reusing functionality becomes difficult&lt;/li&gt;
&lt;li&gt;🐌 Adding new features gets complicated&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is &lt;strong&gt;tightly coupled, poorly organized code&lt;/strong&gt;. We need a better way to design software. That's where &lt;strong&gt;SOLID&lt;/strong&gt; comes in!&lt;/p&gt;

&lt;h3&gt;
  
  
  🔤 What does SOLID stand for?
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Letter&lt;/th&gt;
&lt;th&gt;Principle&lt;/th&gt;
&lt;th&gt;One-line idea&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;S&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Single Responsibility&lt;/td&gt;
&lt;td&gt;One class should have one responsibility&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;O&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Open/Closed&lt;/td&gt;
&lt;td&gt;Open for extension, closed for modification&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;L&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Liskov Substitution&lt;/td&gt;
&lt;td&gt;Child classes should safely replace parent classes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;I&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Interface Segregation&lt;/td&gt;
&lt;td&gt;Don't force classes to implement methods they don't need&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;D&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dependency Inversion&lt;/td&gt;
&lt;td&gt;Depend on abstractions, not concrete implementations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Today we'll go deep on &lt;strong&gt;S, O and L&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. 1️⃣ S: Single Responsibility Principle (SRP)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  2.1 🤔 What does it mean?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;A class should have only one reason to change.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Single&lt;/strong&gt; = one&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Responsibility&lt;/strong&gt; = one specific job&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Principle&lt;/strong&gt; = a rule to follow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In simple English: a class should focus on &lt;strong&gt;one particular job&lt;/strong&gt; instead of handling several unrelated ones.&lt;/p&gt;

&lt;p&gt;🍽️ &lt;strong&gt;Real-life example:&lt;/strong&gt; a restaurant chef prepares food. The chef should &lt;em&gt;not&lt;/em&gt; also manage customer payments, clean the whole restaurant, maintain the database or handle salaries. Those jobs belong to other people or departments. When one responsibility changes, it shouldn't unnecessarily affect everything else. The same logic applies to classes.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.2 ❌ SRP with bad code
&lt;/h3&gt;

&lt;p&gt;A user can register, be saved to a database, and receive a welcome email. A beginner might write:&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;User&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Validate user&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"User registered"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Save user to database&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Saving user to database"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Send welcome email&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Sending welcome email"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This class has &lt;strong&gt;three different responsibilities&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Functionality&lt;/th&gt;
&lt;th&gt;Responsibility&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Validate user&lt;/td&gt;
&lt;td&gt;Business validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Save user&lt;/td&gt;
&lt;td&gt;Database operations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Send email&lt;/td&gt;
&lt;td&gt;Email communication&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Now imagine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📧 The email provider changes from Gmail SMTP to SendGrid → modify &lt;code&gt;User&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;🗄️ The database changes from PostgreSQL to MySQL → modify &lt;code&gt;User&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;✅ Validation rules change → modify &lt;code&gt;User&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The class has &lt;strong&gt;multiple reasons to change&lt;/strong&gt;. That's an SRP violation. 🚨&lt;/p&gt;

&lt;h3&gt;
  
  
  2.3 ✅ Correct implementation using SRP
&lt;/h3&gt;

&lt;p&gt;Separate responsibilities into different classes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UserService     → handles user registration business logic
UserRepository  → database operations
EmailService    → email operations
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Class 1: &lt;code&gt;UserRepository&lt;/code&gt;&lt;/strong&gt; (only database operations)&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;UserRepository&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Saving user to database"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Class 2: &lt;code&gt;EmailService&lt;/code&gt;&lt;/strong&gt; (only sending emails)&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;EmailService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_welcome_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Sending welcome email"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Class 3: &lt;code&gt;UserService&lt;/code&gt;&lt;/strong&gt; (coordinates the registration process)&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;UserService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email_service&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;repository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email_service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;email_service&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;register_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Validating user"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_welcome_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Registration completed"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it:&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;repository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;UserRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;email_service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;EmailService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;user_service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;UserService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;email_service&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;user_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;"Deepika"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"deepika@gmail.com"&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Validating user
Saving user to database
Sending welcome email
Registration completed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each class now has a clear responsibility.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📝 &lt;code&gt;UserService&lt;/code&gt; still coordinates several steps, but its responsibility is &lt;strong&gt;the registration use case&lt;/strong&gt;. Having multiple method calls doesn't automatically violate SRP.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2.4 ✅ Why is SRP important?
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Benefit&lt;/th&gt;
&lt;th&gt;Explanation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🛠️ Maintainability&lt;/td&gt;
&lt;td&gt;Easier to modify individual functionality&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;♻️ Reusability&lt;/td&gt;
&lt;td&gt;Reuse &lt;code&gt;EmailService&lt;/code&gt; in other modules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🧪 Testability&lt;/td&gt;
&lt;td&gt;Test each class independently&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;📖 Readability&lt;/td&gt;
&lt;td&gt;Code becomes easier to understand&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🛡️ Reduced side effects&lt;/td&gt;
&lt;td&gt;Changes are less likely to break unrelated features&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  2.5 ⚡ SRP in a FastAPI backend
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;backend/
└── app/
    ├── api/
    │   └── routes/
    │       ├── auth.py
    │       └── posts.py
    │
    ├── services/
    │   ├── auth_service.py
    │   └── post_service.py
    │
    ├── repositories/
    │   ├── user_repository.py
    │   └── post_repository.py
    │
    └── models/
        ├── user.py
        └── post.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Responsibility&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🛣️ API routes&lt;/td&gt;
&lt;td&gt;Receive HTTP requests and return responses&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🧠 Services&lt;/td&gt;
&lt;td&gt;Handle business logic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;📚 Repositories&lt;/td&gt;
&lt;td&gt;Communicate with the database&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🗄️ Models&lt;/td&gt;
&lt;td&gt;Represent database entities&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For example: &lt;code&gt;auth.py&lt;/code&gt; handles login and registration &lt;strong&gt;endpoints&lt;/strong&gt;, &lt;code&gt;auth_service.py&lt;/code&gt; handles authentication &lt;strong&gt;business logic&lt;/strong&gt;, &lt;code&gt;user_repository.py&lt;/code&gt; handles user &lt;strong&gt;database operations&lt;/strong&gt;, and &lt;code&gt;user.py&lt;/code&gt; represents the &lt;strong&gt;User database model&lt;/strong&gt;. This separation supports SRP.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🎤 &lt;strong&gt;Interview question:&lt;/strong&gt; &lt;em&gt;What happens if we put database queries, business logic and HTTP request handling inside one route function?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Answer:&lt;/strong&gt; The route becomes responsible for too many things. It becomes harder to test, maintain, reuse and modify independently.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  3. 2️⃣ O: Open/Closed Principle (OCP)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3.1 🤔 What does it mean?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;Software entities should be open for extension but closed for modification.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It sounds confusing at first, so let's take each phrase:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Open for extension&lt;/strong&gt; → we can &lt;strong&gt;add&lt;/strong&gt; new functionality&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Closed for modification&lt;/strong&gt; → we avoid repeatedly &lt;strong&gt;changing existing, working code&lt;/strong&gt; to support every new requirement&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ It does &lt;strong&gt;NOT&lt;/strong&gt; mean existing code can never be changed. It means designing code so that adding a feature usually &lt;strong&gt;doesn't require modifying existing, stable logic&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;💳 &lt;strong&gt;Real-life example:&lt;/strong&gt; a payment app first supports credit and debit cards. Later the company adds UPI, PayPal and net banking. A poorly designed system forces you to change the &lt;em&gt;same payment-processing function&lt;/em&gt; every time. A better design lets you add new payment methods &lt;strong&gt;without rewriting the existing payment flow&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.2 ❌ OCP with bad code
&lt;/h3&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;PaymentService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payment_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;payment_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"card"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Processing card payment"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;payment_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"upi"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Processing UPI payment"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;payment_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"paypal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Processing PayPal payment"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works! But tomorrow we add Google Pay, so we modify &lt;code&gt;pay()&lt;/code&gt;. Next month another method, and we modify &lt;code&gt;pay()&lt;/code&gt; again. The method keeps growing with new conditions, which makes it harder to maintain and increases the chance of &lt;strong&gt;breaking existing functionality&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.3 ✅ Correct implementation using OCP
&lt;/h3&gt;

&lt;p&gt;We use &lt;strong&gt;abstraction&lt;/strong&gt; and &lt;strong&gt;polymorphism&lt;/strong&gt;. Don't worry, we'll explain both.&lt;/p&gt;

&lt;p&gt;First, a common &lt;strong&gt;interface&lt;/strong&gt; using Python's &lt;code&gt;ABC&lt;/code&gt;. An interface is essentially a &lt;strong&gt;contract&lt;/strong&gt; that says which methods an implementation must provide.&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;from&lt;/span&gt; &lt;span class="n"&gt;abc&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="no"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;abstractmethod&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaymentMethod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nd"&gt;@abstractmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ABC&lt;/code&gt; lets us define an &lt;strong&gt;abstract base class&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@abstractmethod&lt;/code&gt; declares a method that child classes &lt;strong&gt;must&lt;/strong&gt; implement&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PaymentMethod&lt;/code&gt; defines the common contract&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now the different payment methods:&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;CardPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PaymentMethod&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"Paid {amount} using Card"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UPIPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PaymentMethod&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"Paid {amount} using UPI"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PayPalPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PaymentMethod&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"Paid {amount} using PayPal"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The payment service:&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;PaymentService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_payment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payment_method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;payment_method&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it:&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;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PaymentService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;card&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CardPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;upi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;UPIPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;paypal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PayPalPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;process_payment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;process_payment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;process_payment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paypal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Paid 1000 using Card
Paid 500 using UPI
Paid 2000 using PayPal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the business introduces &lt;strong&gt;Google Pay&lt;/strong&gt;. We simply add another class:&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;GooglePay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PaymentMethod&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"Paid {amount} using Google Pay"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;google_pay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GooglePay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;process_payment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;google_pay&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Paid 1500 using Google Pay
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look carefully: we &lt;strong&gt;did not modify&lt;/strong&gt; the existing &lt;code&gt;PaymentService&lt;/code&gt; class. We &lt;strong&gt;extended&lt;/strong&gt; the system by adding a new implementation. &lt;strong&gt;OCP achieved!&lt;/strong&gt; 🎉&lt;/p&gt;

&lt;h3&gt;
  
  
  3.4 🎭 What is polymorphism here?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;Polymorphism&lt;/strong&gt; means &lt;em&gt;one common interface, many different implementations.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;payment_method&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same method call behaves differently depending on the object passed:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Object&lt;/th&gt;
&lt;th&gt;What happens?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CardPayment&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Card payment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UPIPayment&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;UPI payment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PayPalPayment&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;PayPal payment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GooglePay&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Google Pay payment&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Same method name, different behavior. That's polymorphism.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.5 ✅ Why is OCP important?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;➕ Adding new features becomes easier&lt;/li&gt;
&lt;li&gt;🧱 Existing tested code stays stable&lt;/li&gt;
&lt;li&gt;🔁 Reduces repeated modifications&lt;/li&gt;
&lt;li&gt;📈 Improves scalability&lt;/li&gt;
&lt;li&gt;🛡️ Reduces the risk of introducing bugs into existing functionality&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3.6 📱 OCP in a social media app
&lt;/h3&gt;

&lt;p&gt;Your app supports &lt;strong&gt;email notifications&lt;/strong&gt; at first. Later you add &lt;strong&gt;push&lt;/strong&gt;, &lt;strong&gt;SMS&lt;/strong&gt; and &lt;strong&gt;in-app&lt;/strong&gt; notifications. Instead of one huge function with many &lt;code&gt;if/elif&lt;/code&gt; branches, create a &lt;strong&gt;common notification interface&lt;/strong&gt;. Each notification type implements its own sending behavior. When a new type arrives, you &lt;strong&gt;add a new implementation&lt;/strong&gt; and don't rewrite the main notification-processing logic.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🎤 &lt;strong&gt;Interview question:&lt;/strong&gt; &lt;em&gt;Why is OCP useful in a growing application?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Answer:&lt;/strong&gt; Requirements change frequently. OCP lets developers add new functionality through extension instead of repeatedly modifying existing, stable code, reducing the risk of breaking existing features.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  4. 3️⃣ L: Liskov Substitution Principle (LSP)
&lt;/h2&gt;

&lt;p&gt;Probably the trickiest of the three at first. But once you see the example, it's easy. 😄&lt;/p&gt;

&lt;h3&gt;
  
  
  4.1 🤔 What does it mean?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;Objects of a parent class should be replaceable with objects of its child classes without breaking the correctness of the program.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Parent class&lt;/strong&gt; = base class&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Child class&lt;/strong&gt; = derived class&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Substitution&lt;/strong&gt; = replacing one thing with another&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In simple English: if a child class inherits from a parent class, the child should behave in a way that is &lt;strong&gt;compatible with what the parent promises&lt;/strong&gt;. Wherever you use a child object instead of a parent object, the application should keep working correctly.&lt;/p&gt;

&lt;h3&gt;
  
  
  🧬 First, understand inheritance
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Inheritance&lt;/strong&gt; is when one class acquires properties and methods from another:&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;Animal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;eat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Animal is eating"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Dog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;bark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Dog is barking"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Animal&lt;/code&gt; is the parent, &lt;code&gt;Dog&lt;/code&gt; is the child, and &lt;code&gt;Dog&lt;/code&gt; inherits &lt;code&gt;eat()&lt;/code&gt;:&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;dog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Dog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;dog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;dog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bark&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Animal is eating
Dog is barking
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But LSP asks something more important: &lt;strong&gt;can we safely use the child class wherever the parent class is expected?&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4.2 🐦 Real-life example: birds
&lt;/h3&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;Bird&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Bird is flying"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Sparrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Bird&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Sparrow is flying"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Eagle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Bird&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Eagle is flying"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both can fly, so everything is fine. Now:&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;make_bird_fly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bird&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;bird&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nf"&gt;make_bird_fly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Sparrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;make_bird_fly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Eagle&lt;/span&gt;&lt;span class="p"&gt;(&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Sparrow is flying
Eagle is flying
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The child objects substitute for the parent type without problems. That's LSP. ✅&lt;/p&gt;

&lt;h3&gt;
  
  
  4.3 ❌ Understanding an LSP violation
&lt;/h3&gt;

&lt;p&gt;Now we introduce a &lt;strong&gt;Penguin&lt;/strong&gt;. Can a penguin fly? &lt;strong&gt;No!&lt;/strong&gt; But our parent class says every &lt;code&gt;Bird&lt;/code&gt; has a &lt;code&gt;fly()&lt;/code&gt; method.&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;Penguin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Bird&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Penguins cannot fly"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;penguin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Penguin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;make_bird_fly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;penguin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Exception: Penguins cannot fly
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;make_bird_fly()&lt;/code&gt; expects a bird that can fly. &lt;code&gt;Penguin&lt;/code&gt; inherits from &lt;code&gt;Bird&lt;/code&gt; but breaks that expectation. The parent promises flying behavior, and the child can't deliver it. This is a classic &lt;strong&gt;Liskov Substitution Principle violation&lt;/strong&gt;. 🚨&lt;/p&gt;

&lt;h3&gt;
  
  
  4.4 ✅ How do we fix it?
&lt;/h3&gt;

&lt;p&gt;Redesign the hierarchy. Instead of saying every &lt;code&gt;Bird&lt;/code&gt; can fly, &lt;strong&gt;separate general bird behavior from flying behavior&lt;/strong&gt;:&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;Bird&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;eat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Bird is eating"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FlyingBird&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Bird&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Bird is flying"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Sparrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;FlyingBird&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Sparrow is flying"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Eagle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;FlyingBird&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Eagle is flying"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Penguin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Bird&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;swim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Penguin is swimming"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bird           → common bird behavior: eat()
 ├── FlyingBird → fly()
 │     ├── Sparrow
 │     └── Eagle
 └── Penguin    → swim()   (does not promise flying behavior)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now:&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;make_bird_fly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bird&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;FlyingBird&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;bird&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nf"&gt;make_bird_fly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Sparrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;make_bird_fly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Eagle&lt;/span&gt;&lt;span class="p"&gt;(&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;p&gt;We don't pass &lt;code&gt;Penguin&lt;/code&gt;. The design now reflects the &lt;strong&gt;actual capabilities&lt;/strong&gt; of the objects.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 LSP isn't simply about whether inheritance &lt;em&gt;exists&lt;/em&gt;. It's about whether the child class &lt;strong&gt;honors the behavioral contract&lt;/strong&gt; of the parent class.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  4.5 📐 Another important example: Rectangle and Square
&lt;/h3&gt;

&lt;p&gt;A famous interview example. Mathematically a &lt;strong&gt;square is a special kind of rectangle&lt;/strong&gt;, so a developer might try this:&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;Rectangle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;set_width&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;set_height&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;area&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Square&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Rectangle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;set_width&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;set_height&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test it:&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;calculate_area&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rectangle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;rectangle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_width&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;rectangle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_height&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;rectangle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;area&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;p&gt;With a normal rectangle:&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;rectangle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Rectangle&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="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;calculate_area&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rectangle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# 50 ✅&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With a square:&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;square&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Square&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;calculate_area&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;square&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="c1"&gt;# 100 ❌&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why? Setting the height to 10 also changes the square's width to 10. The function expected a rectangle whose width and height can change &lt;strong&gt;independently&lt;/strong&gt;, and &lt;code&gt;Square&lt;/code&gt; breaks that expectation. That's an LSP violation.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 &lt;strong&gt;The lesson:&lt;/strong&gt; a mathematically valid inheritance relationship doesn't always make a good &lt;em&gt;software&lt;/em&gt; inheritance relationship.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  4.6 🔍 How to spot LSP violations
&lt;/h3&gt;

&lt;p&gt;Warning signs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A child method &lt;strong&gt;throws an exception&lt;/strong&gt; for behavior the parent promises to support&lt;/li&gt;
&lt;li&gt;A child method &lt;strong&gt;does nothing&lt;/strong&gt; when the parent promises meaningful behavior&lt;/li&gt;
&lt;li&gt;A child &lt;strong&gt;changes the expected output or behavior&lt;/strong&gt; in an incompatible way&lt;/li&gt;
&lt;li&gt;A child requires &lt;strong&gt;stricter inputs&lt;/strong&gt; than the parent&lt;/li&gt;
&lt;li&gt;A child returns &lt;strong&gt;weaker or incompatible results&lt;/strong&gt; than the parent contract promises&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There are deeper formal rules behind this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Preconditions should &lt;strong&gt;not be strengthened&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Postconditions should &lt;strong&gt;not be weakened&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Parent invariants should be &lt;strong&gt;preserved&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Term&lt;/th&gt;
&lt;th&gt;Simple meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Preconditions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;What must be true &lt;em&gt;before&lt;/em&gt; a method runs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Postconditions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;What the method guarantees &lt;em&gt;after&lt;/em&gt; execution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Invariants&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Rules that must stay true throughout the object's valid lifetime&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For example, if a parent method accepts any positive number, a child shouldn't suddenly accept only numbers greater than 100. That would &lt;strong&gt;strengthen the precondition&lt;/strong&gt; and violate LSP.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.7 ✅ Why is LSP important?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🔒 Makes inheritance reliable&lt;/li&gt;
&lt;li&gt;🚫 Prevents unexpected runtime errors&lt;/li&gt;
&lt;li&gt;♻️ Improves code reusability&lt;/li&gt;
&lt;li&gt;🎭 Makes polymorphism safer&lt;/li&gt;
&lt;li&gt;🏗️ Helps you design correct class hierarchies&lt;/li&gt;
&lt;li&gt;🧪 Makes unit testing easier, because child objects can be tested against parent expectations&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  5. ⚖️ Comparing S, O and L
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Principle&lt;/th&gt;
&lt;th&gt;Main question&lt;/th&gt;
&lt;th&gt;Problem it prevents&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;S&lt;/strong&gt;: SRP&lt;/td&gt;
&lt;td&gt;Does this class have one clear responsibility?&lt;/td&gt;
&lt;td&gt;Classes doing too many unrelated jobs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;O&lt;/strong&gt;: OCP&lt;/td&gt;
&lt;td&gt;Can I add features without repeatedly modifying stable code?&lt;/td&gt;
&lt;td&gt;Constant modification of existing logic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;L&lt;/strong&gt;: LSP&lt;/td&gt;
&lt;td&gt;Can a child object replace its parent safely?&lt;/td&gt;
&lt;td&gt;Incorrect inheritance and unexpected behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;One example to remember each:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🅢 &lt;strong&gt;SRP:&lt;/strong&gt; a &lt;code&gt;User&lt;/code&gt; class shouldn't handle everything. Separate database operations, email sending and registration.&lt;/li&gt;
&lt;li&gt;🅞 &lt;strong&gt;OCP:&lt;/strong&gt; adding Google Pay shouldn't require rewriting &lt;code&gt;PaymentService&lt;/code&gt;. Add a new implementation of the common contract.&lt;/li&gt;
&lt;li&gt;🅛 &lt;strong&gt;LSP:&lt;/strong&gt; a &lt;code&gt;Penguin&lt;/code&gt; shouldn't be forced to implement flying. Design inheritance around actual behavioral capabilities.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  6. 🤝 How Are These Principles Related?
&lt;/h2&gt;

&lt;p&gt;They're separate, but they complement each other. Imagine an e-commerce backend with &lt;code&gt;PaymentService&lt;/code&gt;, &lt;code&gt;EmailService&lt;/code&gt;, &lt;code&gt;OrderService&lt;/code&gt;, &lt;code&gt;PaymentMethod&lt;/code&gt;, &lt;code&gt;CardPayment&lt;/code&gt; and &lt;code&gt;UPIPayment&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SRP:&lt;/strong&gt; each class has a well-defined responsibility&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OCP:&lt;/strong&gt; you can introduce a new payment method without changing the existing payment-processing logic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LSP:&lt;/strong&gt; every payment implementation honors the common &lt;code&gt;PaymentMethod&lt;/code&gt; contract, so the payment service can safely use any of them&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One good design can follow all three principles at once. ✨&lt;/p&gt;




&lt;h2&gt;
  
  
  7. ⚠️ Common Mistakes with SOLID
&lt;/h2&gt;

&lt;p&gt;Following SOLID doesn't mean creating hundreds of unnecessary classes.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mistake&lt;/th&gt;
&lt;th&gt;Correct understanding&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;"SRP means one method per class"&lt;/td&gt;
&lt;td&gt;No. It means one &lt;strong&gt;cohesive&lt;/strong&gt; responsibility per class&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"OCP means never modify existing code"&lt;/td&gt;
&lt;td&gt;No. It means designing for extension &lt;strong&gt;without unnecessary modification&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"LSP means every child must have exactly the same code as its parent"&lt;/td&gt;
&lt;td&gt;No. Children can implement things differently but must &lt;strong&gt;preserve expected behavior&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"More classes always mean better SOLID design"&lt;/td&gt;
&lt;td&gt;No. Avoid unnecessary complexity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Inheritance automatically means LSP is satisfied"&lt;/td&gt;
&lt;td&gt;No. &lt;strong&gt;Behavioral compatibility&lt;/strong&gt; is what matters&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  8. 🎤 Interview Questions and Answers
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q1. What is SOLID?&lt;/strong&gt;&lt;br&gt;
A collection of five object-oriented design principles that help developers write maintainable, flexible, reusable and scalable software.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q2. What's the difference between SRP and OCP?&lt;/strong&gt;&lt;br&gt;
SRP ensures a class has one clear responsibility. OCP allows new functionality to be added without repeatedly modifying existing, stable code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q3. What does "one reason to change" mean in SRP?&lt;/strong&gt;&lt;br&gt;
A class should be responsible for one cohesive area of functionality. If that responsibility changes, the class may need modification.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q4. How do we implement OCP in Python?&lt;/strong&gt;&lt;br&gt;
Using abstraction, abstract base classes, inheritance and polymorphism to create extensible designs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q5. What is polymorphism?&lt;/strong&gt;&lt;br&gt;
It lets different objects respond to the same method call in their own ways.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q6. What happens when LSP is violated?&lt;/strong&gt;&lt;br&gt;
A child object may cause unexpected errors or incorrect behavior when used in place of its parent object.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q7. Is a Square a Rectangle? Why can it violate LSP?&lt;/strong&gt;&lt;br&gt;
Mathematically yes. But if a &lt;code&gt;Rectangle&lt;/code&gt; lets width and height change independently, a &lt;code&gt;Square&lt;/code&gt; that forces them to stay equal violates the expected behavior of that implementation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q8. Are SOLID principles only for Java?&lt;/strong&gt;&lt;br&gt;
No. They apply to object-oriented design in Python, Java, C++, C# and other languages, and their ideas also help when designing larger application architectures.&lt;/p&gt;


&lt;h2&gt;
  
  
  9. 🧪 Test Your Understanding!
&lt;/h2&gt;

&lt;p&gt;Try these before peeking at the answers! ❤️&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;A class handles user registration, database operations and email sending. Which principle is violated?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) OCP&lt;/li&gt;
&lt;li&gt;B) SRP&lt;/li&gt;
&lt;li&gt;C) LSP&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;You add Google Pay by creating a new payment class without modifying &lt;code&gt;PaymentService&lt;/code&gt;. Which principle are you following?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) SRP&lt;/li&gt;
&lt;li&gt;B) LSP&lt;/li&gt;
&lt;li&gt;C) OCP&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;A &lt;code&gt;Penguin&lt;/code&gt; inherits from &lt;code&gt;Bird&lt;/code&gt;, but the parent promises every bird can fly. Which principle is violated?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) LSP&lt;/li&gt;
&lt;li&gt;B) OCP&lt;/li&gt;
&lt;li&gt;C) SRP&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Different payment classes implement the same &lt;code&gt;pay()&lt;/code&gt; method differently. What OOP concept is this?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Encapsulation&lt;/li&gt;
&lt;li&gt;B) Polymorphism&lt;/li&gt;
&lt;li&gt;C) Normalization&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;A child class requires stricter input conditions than its parent method. Which principle may be violated?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) SRP&lt;/li&gt;
&lt;li&gt;B) OCP&lt;/li&gt;
&lt;li&gt;C) LSP&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;/p&gt;
  👀 Click to reveal answers
  &lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;B&lt;/strong&gt;: SRP. The class has several unrelated reasons to change&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C&lt;/strong&gt;: OCP. You extended the system without modifying existing code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A&lt;/strong&gt;: LSP. The child can't honor the parent's behavioral contract&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;B&lt;/strong&gt;: Polymorphism. One interface, many implementations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C&lt;/strong&gt;: LSP. Strengthening preconditions breaks substitutability&lt;/li&gt;
&lt;/ol&gt;



&lt;p&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  10. 📝 Final Revision Notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🅢 &lt;strong&gt;Single Responsibility Principle:&lt;/strong&gt; one class = one clear responsibility = one main reason to change&lt;/li&gt;
&lt;li&gt;🅞 &lt;strong&gt;Open/Closed Principle:&lt;/strong&gt; open for extension, closed for unnecessary modification&lt;/li&gt;
&lt;li&gt;🅛 &lt;strong&gt;Liskov Substitution Principle:&lt;/strong&gt; child objects must be safely usable wherever their parent objects are expected&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Don't just memorize the definitions.&lt;/strong&gt; Whenever you write a FastAPI service, repository, model or authentication module, ask yourself:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Is this class doing too many jobs?&lt;/li&gt;
&lt;li&gt;Can I add a new feature without rewriting existing logic?&lt;/li&gt;
&lt;li&gt;If I inherit from another class, will my child class preserve its expected behavior?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's how you start thinking like a software engineer, not just someone who writes code. 🧠&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;If this post helped you, drop a ❤️ or 🦄 and share it with a friend learning software design. Happy coding! 👩‍💻✨&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>programming</category>
      <category>softwareengineering</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Transactions &amp; Basic Indexing in PostgreSQL: COMMIT, ROLLBACK, EXPLAIN &amp; SQLAlchemy ⚛️</title>
      <dc:creator>Deepika Pusala</dc:creator>
      <pubDate>Wed, 11 Nov 2026 07:35:30 +0000</pubDate>
      <link>https://dev.to/deepika_pusala/transactions-basic-indexing-in-postgresql-commit-rollback-explain-sqlalchemy-1e87</link>
      <guid>https://dev.to/deepika_pusala/transactions-basic-indexing-in-postgresql-commit-rollback-explain-sqlalchemy-1e87</guid>
      <description>&lt;h1&gt;
  
  
  ⚛️ Transactions &amp;amp; Basic Indexing
&lt;/h1&gt;

&lt;p&gt;This is another &lt;strong&gt;VERY important&lt;/strong&gt; backend concept, especially when you work with &lt;strong&gt;PostgreSQL, SQLAlchemy, FastAPI&lt;/strong&gt; and real-world applications. 💪&lt;/p&gt;

&lt;p&gt;Imagine you're building an &lt;strong&gt;e-commerce app&lt;/strong&gt; 🛒. A user buys a product and your app must reduce its stock. Now imagine something breaks halfway:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Product A's stock gets reduced&lt;/li&gt;
&lt;li&gt;❌ Product B's stock does &lt;strong&gt;not&lt;/strong&gt; get increased&lt;/li&gt;
&lt;li&gt;💥 Your database now holds &lt;strong&gt;incorrect information&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;How do we prevent this? That's exactly where &lt;strong&gt;transactions, ROLLBACK, COMMIT and indexing&lt;/strong&gt; come in.&lt;/p&gt;

&lt;p&gt;We'll learn everything from absolute zero, with SQL examples, real-world analogies, PostgreSQL experiments and FastAPI connections. 🚀&lt;/p&gt;




&lt;h2&gt;
  
  
  📚 Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;What is a database transaction?&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;BEGIN&lt;/code&gt;, &lt;code&gt;COMMIT&lt;/code&gt; and &lt;code&gt;ROLLBACK&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Atomicity and Consistency in practice&lt;/li&gt;
&lt;li&gt;Hands-on: transfer stock between two products&lt;/li&gt;
&lt;li&gt;What are database indexes?&lt;/li&gt;
&lt;li&gt;Primary, Unique and Composite indexes&lt;/li&gt;
&lt;li&gt;Other index types worth knowing&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;EXPLAIN&lt;/code&gt; and &lt;code&gt;EXPLAIN ANALYZE&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Hands-on: add an index and compare plans&lt;/li&gt;
&lt;li&gt;Transactions in SQLAlchemy 2.0&lt;/li&gt;
&lt;li&gt;Indexes in SQLAlchemy models and Alembic&lt;/li&gt;
&lt;li&gt;Real-world usage&lt;/li&gt;
&lt;li&gt;Common beginner mistakes&lt;/li&gt;
&lt;li&gt;Quick revision sheet&lt;/li&gt;
&lt;li&gt;Quiz&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1️⃣ What is a database transaction?
&lt;/h2&gt;

&lt;p&gt;Here's a &lt;code&gt;products&lt;/code&gt; table:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;products&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;product_id&lt;/th&gt;
&lt;th&gt;product_name&lt;/th&gt;
&lt;th&gt;stock&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Laptop&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Mobile&lt;/td&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Headphones&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A customer buys 5 laptops, so your app runs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;product_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Laptop stock is now 45. But what if...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;💥 the app crashes right after this update?&lt;/li&gt;
&lt;li&gt;💥 the &lt;strong&gt;second&lt;/strong&gt; query fails?&lt;/li&gt;
&lt;li&gt;💥 the database connection drops?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We need a way to make a group of related operations &lt;strong&gt;succeed together or fail together&lt;/strong&gt;. That mechanism is a &lt;strong&gt;transaction&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📦 &lt;strong&gt;Definition:&lt;/strong&gt; a &lt;strong&gt;database transaction&lt;/strong&gt; is a group of one or more SQL operations treated as a &lt;strong&gt;single logical unit of work&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The principle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ If &lt;strong&gt;all&lt;/strong&gt; operations succeed → &lt;strong&gt;save&lt;/strong&gt; the changes&lt;/li&gt;
&lt;li&gt;❌ If &lt;strong&gt;something&lt;/strong&gt; fails → &lt;strong&gt;undo&lt;/strong&gt; the changes made by that transaction&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  💸 Bank transfer example
&lt;/h3&gt;

&lt;p&gt;Transfer ₹1,000 from Account A to Account B.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Account&lt;/th&gt;
&lt;th&gt;Balance&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;₹5,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B&lt;/td&gt;
&lt;td&gt;₹3,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two operations are required:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Subtract ₹1,000 from A&lt;/li&gt;
&lt;li&gt;Add ₹1,000 to B&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Expected after&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Account&lt;/th&gt;
&lt;th&gt;Balance&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;₹4,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B&lt;/td&gt;
&lt;td&gt;₹4,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;But if &lt;strong&gt;only the first&lt;/strong&gt; operation succeeds:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Account&lt;/th&gt;
&lt;th&gt;Balance&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;₹4,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B&lt;/td&gt;
&lt;td&gt;₹3,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Where did ₹1,000 go? 😱 That's an &lt;strong&gt;inconsistent state&lt;/strong&gt;. A transaction prevents it by making both operations succeed together, or neither.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔄 How a transaction flows
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BEGIN                      → start the transaction
  ├─ Operation 1           → subtract ₹1,000 from A
  ├─ Operation 2           → add ₹1,000 to B
  │
  ├─ all good?  → COMMIT   → save changes permanently ✅
  └─ failure?   → ROLLBACK → undo everything ❌
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three SQL commands drive this: &lt;code&gt;BEGIN&lt;/code&gt;, &lt;code&gt;COMMIT&lt;/code&gt; and &lt;code&gt;ROLLBACK&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  2️⃣ BEGIN, COMMIT and ROLLBACK
&lt;/h2&gt;

&lt;h3&gt;
  
  
  2.1 🚦 &lt;code&gt;BEGIN&lt;/code&gt;: start a transaction
&lt;/h3&gt;

&lt;p&gt;It tells the database: &lt;em&gt;"I'm starting a transaction. Treat the upcoming operations as one unit."&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;product_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The database starts tracking the changes. They are &lt;strong&gt;not yet permanent&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.2 💾 &lt;code&gt;COMMIT&lt;/code&gt;: save the transaction
&lt;/h3&gt;

&lt;p&gt;It tells the database: &lt;em&gt;"Everything worked. Save all changes permanently."&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;product_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;COMMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Transaction begins&lt;/li&gt;
&lt;li&gt;Stock is reduced by 5&lt;/li&gt;
&lt;li&gt;The database commits&lt;/li&gt;
&lt;li&gt;Changes become &lt;strong&gt;permanent&lt;/strong&gt; ✅&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  2.3 ↩️ &lt;code&gt;ROLLBACK&lt;/code&gt;: undo the transaction
&lt;/h3&gt;

&lt;p&gt;It tells the database: &lt;em&gt;"Something went wrong. Undo all changes made in this transaction."&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;product_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;ROLLBACK&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Moment&lt;/th&gt;
&lt;th&gt;Laptop stock&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Initially&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;After the &lt;code&gt;UPDATE&lt;/code&gt; (inside the transaction)&lt;/td&gt;
&lt;td&gt;45&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;After &lt;code&gt;ROLLBACK&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;50&lt;/strong&gt; ✅ (restored)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  2.4 📊 The three commands
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BEGIN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start a transaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;COMMIT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Save changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ROLLBACK&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Undo uncommitted changes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;PostgreSQL detail:&lt;/strong&gt; if a statement fails inside a transaction, PostgreSQL marks the transaction as &lt;strong&gt;aborted&lt;/strong&gt;. Until you run &lt;code&gt;ROLLBACK&lt;/code&gt;, further ordinary statements in that transaction are rejected.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  3️⃣ Atomicity and Consistency in practice ⚛️
&lt;/h2&gt;

&lt;p&gt;Remember &lt;strong&gt;ACID&lt;/strong&gt;?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A&lt;/strong&gt;: Atomicity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C&lt;/strong&gt;: Consistency&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I&lt;/strong&gt;: Isolation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;D&lt;/strong&gt;: Durability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Today we focus on &lt;strong&gt;Atomicity&lt;/strong&gt; and &lt;strong&gt;Consistency&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.1 ⚛️ Atomicity
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;All operations in a transaction happen together, or none of them happen.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Think of an electrical switch: it's ON or OFF, never "half on". 💡&lt;/p&gt;

&lt;p&gt;A bank transfer needs both:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;accounts&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;account_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;accounts&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;account_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the second query fails, the first &lt;strong&gt;must be undone&lt;/strong&gt;. That's atomicity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❌ WITHOUT atomicity
   Account A: ₹5,000 → updated
   Account B: ₹3,000 → FAILED
   Result: money disappeared from the system!

✅ WITH atomicity
   Account A: ₹5,000 → restored
   Account B: ₹3,000 → unchanged
   Result: the whole transaction was rolled back.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.2 ✅ Consistency
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A transaction must take the database from one valid state to another valid state.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Suppose your rule is: &lt;em&gt;stock must never be negative.&lt;/em&gt; We enforce it with a constraint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CHECK&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Laptop stock is 10, and a customer tries to buy 15:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;product_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That would give &lt;code&gt;10 - 15 = -5&lt;/code&gt;. PostgreSQL &lt;strong&gt;rejects&lt;/strong&gt; the operation because it violates the &lt;code&gt;CHECK&lt;/code&gt; constraint. The database never enters an invalid state. 🛡️&lt;/p&gt;

&lt;h3&gt;
  
  
  3.3 ⚖️ Atomicity vs Consistency
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Atomicity&lt;/th&gt;
&lt;th&gt;Consistency&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;All or nothing&lt;/td&gt;
&lt;td&gt;Valid state → valid state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Focuses on &lt;strong&gt;transaction execution&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Focuses on &lt;strong&gt;database rules&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Uses &lt;code&gt;COMMIT&lt;/code&gt; and &lt;code&gt;ROLLBACK&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Uses constraints and correct transaction logic&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 &lt;strong&gt;Atomicity&lt;/strong&gt; protects a transaction from partial completion. &lt;strong&gt;Consistency&lt;/strong&gt; protects the correctness of the data.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  4️⃣ Hands-on: transfer stock between two products 🧪
&lt;/h2&gt;

&lt;p&gt;You have two products:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;product_id&lt;/th&gt;
&lt;th&gt;product&lt;/th&gt;
&lt;th&gt;stock&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Laptop&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Mobile&lt;/td&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Goal: move &lt;strong&gt;10 units&lt;/strong&gt; from Laptop to Mobile. After success: Laptop = 40, Mobile = 40. Then we'll &lt;strong&gt;deliberately fail&lt;/strong&gt; halfway to see ROLLBACK in action.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1️⃣: create the table
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;product_id&lt;/span&gt;   &lt;span class="kt"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;product_name&lt;/span&gt; &lt;span class="kt"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;stock&lt;/span&gt;        &lt;span class="kt"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;CHECK&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&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;ul&gt;
&lt;li&gt;
&lt;code&gt;product_id&lt;/code&gt; → unique identifier&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;product_name&lt;/code&gt; → the name&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;stock&lt;/code&gt; → available quantity&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;CHECK (stock &amp;gt;= 0)&lt;/code&gt; → blocks negative stock&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 2️⃣: insert sample data
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Laptop'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&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="s1"&gt;'Mobile'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;product_id&lt;/th&gt;
&lt;th&gt;product_name&lt;/th&gt;
&lt;th&gt;stock&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Laptop&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Mobile&lt;/td&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Step 3️⃣: a successful transfer ✅
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Reduce Laptop stock by 10&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;product_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Increase Mobile stock by 10&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;product_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;COMMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Product&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Laptop&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td&gt;40&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mobile&lt;/td&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;td&gt;40&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Both operations succeeded, so we committed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4️⃣: deliberately fail midway 💥
&lt;/h3&gt;

&lt;p&gt;First, reset the stock:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;product_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;product_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now run this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Operation 1: reduce Laptop stock (this works)&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;product_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Operation 2: deliberately cause an error (division by zero!)&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;product_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Undo everything&lt;/span&gt;
&lt;span class="k"&gt;ROLLBACK&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What happens:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Operation 1 executes inside the transaction, and Laptop stock is temporarily &lt;strong&gt;40&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Operation 2 &lt;strong&gt;fails&lt;/strong&gt; with &lt;em&gt;division by zero&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;PostgreSQL marks the transaction as &lt;strong&gt;aborted&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;We run &lt;code&gt;ROLLBACK;&lt;/code&gt;, and the first operation is undone too ↩️&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Product&lt;/th&gt;
&lt;th&gt;Initial&lt;/th&gt;
&lt;th&gt;After rollback&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Laptop&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;50&lt;/strong&gt; ✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mobile&lt;/td&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;30&lt;/strong&gt; ✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Atomicity demonstrated!&lt;/strong&gt; 🎉&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Practical detail:&lt;/strong&gt; after the error, you must send &lt;code&gt;ROLLBACK;&lt;/code&gt; as a &lt;strong&gt;separate statement&lt;/strong&gt;. The remaining statements in an aborted transaction can't run normally. (If you paste everything in one go in some clients, the error may stop the script before reaching &lt;code&gt;ROLLBACK&lt;/code&gt;. Just run &lt;code&gt;ROLLBACK;&lt;/code&gt; yourself afterwards.)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  🧪 Bonus: break the CHECK constraint
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;   &lt;span class="c1"&gt;-- Laptop only has 50!&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;product_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- ERROR: new row violates check constraint&lt;/span&gt;

&lt;span class="k"&gt;ROLLBACK&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This shows &lt;strong&gt;Consistency&lt;/strong&gt; protecting you, and &lt;strong&gt;Atomicity&lt;/strong&gt; cleaning up. 🛡️&lt;/p&gt;




&lt;h2&gt;
  
  
  5️⃣ What are database indexes? 📇
&lt;/h2&gt;

&lt;p&gt;Now the second big concept: &lt;strong&gt;indexing&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Imagine a library with &lt;strong&gt;1,00,000 books&lt;/strong&gt; 📚. A student asks: &lt;em&gt;"Where's the book Database Management Systems?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;❌ Without an index:&lt;/strong&gt; you check Book 1, Book 2, Book 3... up to Book 1,00,000. You may examine almost every book.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅ With an index:&lt;/strong&gt; the library has an alphabetical index. You jump to &lt;strong&gt;D&lt;/strong&gt;, find the location, done. Much faster!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📦 &lt;strong&gt;Definition:&lt;/strong&gt; a &lt;strong&gt;database index&lt;/strong&gt; is a data structure that helps the database find rows faster &lt;strong&gt;without scanning the entire table&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;users&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;user_id&lt;/th&gt;
&lt;th&gt;username&lt;/th&gt;
&lt;th&gt;email&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;deepika&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:deepika@gmail.com"&gt;deepika@gmail.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;ishu&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:ishu@gmail.com"&gt;ishu@gmail.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;rahul&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:rahul@gmail.com"&gt;rahul@gmail.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;priya&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:priya@gmail.com"&gt;priya@gmail.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'ishu@gmail.com'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Without an index → PostgreSQL may scan the &lt;strong&gt;whole table&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;With a suitable index → PostgreSQL can locate the row &lt;strong&gt;efficiently&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Indexes have costs!&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every &lt;code&gt;INSERT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt; or &lt;code&gt;DELETE&lt;/code&gt; may also need to update the indexes&lt;/li&gt;
&lt;li&gt;Indexes consume storage space&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So never create indexes on every column blindly.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  6️⃣ Types of indexes you should know
&lt;/h2&gt;

&lt;h3&gt;
  
  
  6.1 🆔 Primary Key index
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt;  &lt;span class="kt"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="kt"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;    &lt;span class="kt"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;)&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;p&gt;PostgreSQL &lt;strong&gt;automatically creates a unique B-tree index&lt;/strong&gt; for the primary key, because every &lt;code&gt;user_id&lt;/code&gt; must be unique.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;user_id&lt;/th&gt;
&lt;th&gt;username&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Deepika&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Rahul&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Priya&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You can't insert another row with &lt;code&gt;user_id = 1&lt;/code&gt;. The database rejects the duplicate.&lt;/p&gt;

&lt;p&gt;Facts to remember:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ A primary key can't contain &lt;code&gt;NULL&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;✅ A table has only &lt;strong&gt;one&lt;/strong&gt; primary key constraint&lt;/li&gt;
&lt;li&gt;✅ It can span one or &lt;strong&gt;multiple&lt;/strong&gt; columns&lt;/li&gt;
&lt;li&gt;✅ PostgreSQL auto-creates a unique index to enforce it&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;You generally &lt;strong&gt;don't&lt;/strong&gt; need to manually index a primary key again.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  6.2 🦄 Unique index
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;unique index&lt;/strong&gt; prevents duplicate values in the indexed column (or column combination).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_users_email&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="nf"&gt;users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&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;p&gt;Now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'john'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'deepika@gmail.com'&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;p&gt;If that email already exists, PostgreSQL &lt;strong&gt;rejects&lt;/strong&gt; the insert.&lt;/p&gt;

&lt;p&gt;Useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📧 Preventing duplicate email accounts&lt;/li&gt;
&lt;li&gt;👤 Preventing duplicate usernames&lt;/li&gt;
&lt;li&gt;🆔 Preventing duplicate employee IDs&lt;/li&gt;
&lt;li&gt;📏 Enforcing uniqueness in business data&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Primary Key&lt;/th&gt;
&lt;th&gt;Unique Index&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Identifies a row&lt;/td&gt;
&lt;td&gt;Enforces uniqueness&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cannot contain &lt;code&gt;NULL&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Can allow &lt;code&gt;NULL&lt;/code&gt; values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One per table&lt;/td&gt;
&lt;td&gt;Multiple unique indexes possible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Automatically creates a unique index&lt;/td&gt;
&lt;td&gt;Created explicitly or via a &lt;code&gt;UNIQUE&lt;/code&gt; constraint&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 &lt;strong&gt;Nuance:&lt;/strong&gt; PostgreSQL unique indexes normally allow &lt;strong&gt;multiple NULLs&lt;/strong&gt;, since NULLs aren't considered equal. You can change that with &lt;code&gt;NULLS NOT DISTINCT&lt;/code&gt; (PostgreSQL 15+).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A &lt;code&gt;UNIQUE&lt;/code&gt; constraint also creates a unique index automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="kt"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;   &lt;span class="kt"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&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;p&gt;No need for a second unique index on &lt;code&gt;email&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  6.3 🧩 Composite index
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;composite index&lt;/strong&gt; covers &lt;strong&gt;multiple columns together&lt;/strong&gt;. It's especially important in backend work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;orders&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;order_id&lt;/th&gt;
&lt;th&gt;user_id&lt;/th&gt;
&lt;th&gt;status&lt;/th&gt;
&lt;th&gt;amount&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;completed&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;pending&lt;/td&gt;
&lt;td&gt;700&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;pending&lt;/td&gt;
&lt;td&gt;300&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;completed&lt;/td&gt;
&lt;td&gt;900&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Your app often runs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'pending'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We filter on two columns, so we create one index on both:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_orders_user_status&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="nf"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&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;h4&gt;
  
  
  🚨 Column order matters!
&lt;/h4&gt;

&lt;p&gt;The index &lt;code&gt;(user_id, status)&lt;/code&gt; is useful for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'pending'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But it is generally &lt;strong&gt;less useful&lt;/strong&gt; for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'pending'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why? The index is organized by &lt;code&gt;user_id&lt;/code&gt; &lt;strong&gt;first&lt;/strong&gt;, then &lt;code&gt;status&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📞 &lt;strong&gt;Analogy:&lt;/strong&gt; a phone directory sorted by &lt;strong&gt;last name, then first name&lt;/strong&gt;. Know the last name? Easy. Only know the first name? You'd basically have to read the whole book.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is the &lt;strong&gt;leftmost-prefix principle&lt;/strong&gt;. For PostgreSQL B-tree indexes, the leading column matters most, though the planner can sometimes still use the index in other ways.&lt;/p&gt;

&lt;h4&gt;
  
  
  When to use composite indexes
&lt;/h4&gt;

&lt;p&gt;When your app &lt;strong&gt;repeatedly filters on multiple columns&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'active'&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;category_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'2026-01-01'&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;tenant_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'abc@gmail.com'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The right column order depends on your &lt;strong&gt;actual query patterns&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  7️⃣ Other index types worth knowing 🧰
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Index type&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;B-tree&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;General-purpose: equality, range and sorting (the &lt;strong&gt;default&lt;/strong&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hash&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Equality comparisons&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GIN&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Searching inside arrays, JSONB and full-text data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GiST&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Geometric, range and specialized searches&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;BRIN&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Very large tables whose values correlate with physical order (like timestamps)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For now, focus on &lt;strong&gt;B-tree&lt;/strong&gt;. It's what PostgreSQL uses by default.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_products_name&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="no"&gt;BTREE&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product_name&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;p&gt;Useful for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;product_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Laptop'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and for range comparisons on sortable values.&lt;/p&gt;




&lt;h2&gt;
  
  
  8️⃣ EXPLAIN: how does the database run your query? 🔍
&lt;/h2&gt;

&lt;p&gt;You write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'deepika@gmail.com'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You know &lt;em&gt;what&lt;/em&gt; you want. But &lt;em&gt;how&lt;/em&gt; does PostgreSQL find it? Does it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Scan every row?&lt;/li&gt;
&lt;li&gt;Use an index?&lt;/li&gt;
&lt;li&gt;Use another strategy?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;PostgreSQL has a &lt;strong&gt;query planner&lt;/strong&gt; that picks an &lt;strong&gt;execution plan&lt;/strong&gt;. &lt;code&gt;EXPLAIN&lt;/code&gt; lets you see it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📦 &lt;strong&gt;Definition:&lt;/strong&gt; &lt;code&gt;EXPLAIN&lt;/code&gt; displays the execution plan PostgreSQL &lt;strong&gt;intends&lt;/strong&gt; to use for a query.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;EXPLAIN&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'deepika@gmail.com'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Possible output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Seq Scan on users
  Filter: (email = 'deepika@gmail.com')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  8.1 📖 Sequential Scan (Seq Scan)
&lt;/h3&gt;

&lt;p&gt;PostgreSQL reads the table's rows &lt;strong&gt;one after another&lt;/strong&gt; to find matches, like searching a notebook page by page. 📓&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;EXPLAIN&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'deepika'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With no suitable index, PostgreSQL may choose a Seq Scan.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;❓ &lt;strong&gt;Is a Seq Scan always bad?&lt;/strong&gt; &lt;strong&gt;NO!&lt;/strong&gt; For a table with only 10 rows, reading everything is cheaper than using an index. Even &lt;em&gt;with&lt;/em&gt; an index, PostgreSQL may choose a Seq Scan if it estimates that reading most of the table is cheaper.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  8.2 🎯 Index Scan
&lt;/h3&gt;

&lt;p&gt;PostgreSQL uses an index to locate matching rows, then fetches those rows from the table.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_users_username&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="nf"&gt;users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;EXPLAIN&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'deepika'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Possible output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Index Scan using idx_users_username on users
  Index Cond: (username = 'deepika')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells us PostgreSQL found a suitable index and used it. ✅&lt;/p&gt;

&lt;h3&gt;
  
  
  8.3 ⚡ Index Only Scan
&lt;/h3&gt;

&lt;p&gt;Sometimes PostgreSQL can answer the query &lt;strong&gt;using only the index&lt;/strong&gt;, without touching the table.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_users_email&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="nf"&gt;users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;EXPLAIN&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'deepika@gmail.com'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PostgreSQL may choose an &lt;strong&gt;Index Only Scan&lt;/strong&gt; because the requested column is already in the index. (It may still check visibility information via the &lt;em&gt;visibility map&lt;/em&gt;.)&lt;/p&gt;

&lt;h3&gt;
  
  
  8.4 📏 EXPLAIN ANALYZE
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;EXPLAIN&lt;/code&gt; → shows the &lt;strong&gt;estimated&lt;/strong&gt; plan&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;EXPLAIN ANALYZE&lt;/code&gt; → &lt;strong&gt;actually runs&lt;/strong&gt; the query and reports &lt;strong&gt;measured&lt;/strong&gt; details
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;EXPLAIN&lt;/span&gt; &lt;span class="k"&gt;ANALYZE&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'deepika@gmail.com'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sample output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Index Scan using idx_users_email on users
  (cost=0.15..8.17 rows=1 width=...)
  (actual time=0.020..0.022 rows=1 loops=1)
Planning Time: 0.100 ms
Execution Time: 0.050 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Term&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cost&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Estimated planner cost (&lt;strong&gt;not&lt;/strong&gt; milliseconds)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rows&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Estimated number of returned rows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;width&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Estimated average row size in bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;actual time&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Measured time in milliseconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;loops&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Times the plan node executed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Planning Time&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Time taken to create the plan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Execution Time&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Time taken to run the query&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;🚨 &lt;strong&gt;Very important:&lt;/strong&gt; &lt;code&gt;EXPLAIN ANALYZE&lt;/code&gt; &lt;strong&gt;executes&lt;/strong&gt; the query! Be careful with &lt;code&gt;UPDATE&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt; and other data-changing statements. Wrap them in a transaction and roll back:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;EXPLAIN&lt;/span&gt; &lt;span class="k"&gt;ANALYZE&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;product_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;ROLLBACK&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The update runs, then gets undone. 😎 (Transactions and EXPLAIN working together!)&lt;/p&gt;




&lt;h2&gt;
  
  
  9️⃣ Hands-on: add an index and compare plans 🧪
&lt;/h2&gt;

&lt;p&gt;Let's create a big table so the difference is visible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1️⃣: create the table
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt;  &lt;span class="kt"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="kt"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;    &lt;span class="kt"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;city&lt;/span&gt;     &lt;span class="kt"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&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;h3&gt;
  
  
  Step 2️⃣: insert 1 lakh (100,000) rows
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="s1"&gt;'user_'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'user_'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'@gmail.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;CASE&lt;/span&gt;
        &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="s1"&gt;'Bangalore'&lt;/span&gt;
        &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="s1"&gt;'Hyderabad'&lt;/span&gt;
        &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="s1"&gt;'Chennai'&lt;/span&gt;
    &lt;span class="k"&gt;END&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="nf"&gt;generate_series&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;generate_series(1, 100000)&lt;/code&gt; is a PostgreSQL function that generates the numbers 1 to 100000. So we insert 1 lakh sample users. 🎉&lt;/p&gt;

&lt;p&gt;Refresh planner statistics:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ANALYZE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3️⃣: query WITHOUT an email index
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;EXPLAIN&lt;/span&gt; &lt;span class="k"&gt;ANALYZE&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'user_50000@gmail.com'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll likely see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Seq Scan on users
  Filter: (email = 'user_50000@gmail.com')
  Rows Removed by Filter: 99999
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PostgreSQL scanned the table because there's no index on &lt;code&gt;email&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4️⃣: create the index
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_users_email&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="nf"&gt;users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;ANALYZE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5️⃣: run the same query again
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;EXPLAIN&lt;/span&gt; &lt;span class="k"&gt;ANALYZE&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'user_50000@gmail.com'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you'll likely see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Index Scan using idx_users_email on users
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  📊 What to compare
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Before index&lt;/th&gt;
&lt;th&gt;After index&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Scan type&lt;/td&gt;
&lt;td&gt;Seq Scan&lt;/td&gt;
&lt;td&gt;Index Scan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rows examined&lt;/td&gt;
&lt;td&gt;Whole table&lt;/td&gt;
&lt;td&gt;Just the match&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Execution time&lt;/td&gt;
&lt;td&gt;Slower&lt;/td&gt;
&lt;td&gt;Much faster ⚡&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Compare: &lt;strong&gt;scan type&lt;/strong&gt;, &lt;strong&gt;estimated cost&lt;/strong&gt;, &lt;strong&gt;actual execution time&lt;/strong&gt; and &lt;strong&gt;rows scanned or returned&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Heads up:&lt;/strong&gt; PostgreSQL can &lt;em&gt;still&lt;/em&gt; choose a Seq Scan after you create an index. That doesn't mean the index is broken. The planner picks whatever it estimates is cheaper.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For &lt;strong&gt;learning only&lt;/strong&gt;, you can discourage Seq Scans in your current session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;enable_seqscan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;OFF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- ...run EXPLAIN ANALYZE...&lt;/span&gt;
&lt;span class="no"&gt;RESET&lt;/span&gt; &lt;span class="n"&gt;enable_seqscan&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🚫 Never use this as a production performance fix. It's only a way to inspect alternative plans.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  🎁 Bonus experiment: composite index
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Before&lt;/span&gt;
&lt;span class="k"&gt;EXPLAIN&lt;/span&gt; &lt;span class="k"&gt;ANALYZE&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Bangalore'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'user_300'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Create a composite index&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_users_city_username&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="nf"&gt;users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;ANALYZE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- After&lt;/span&gt;
&lt;span class="k"&gt;EXPLAIN&lt;/span&gt; &lt;span class="k"&gt;ANALYZE&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Bangalore'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'user_300'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then try &lt;code&gt;WHERE username = 'user_300'&lt;/code&gt; alone and see whether the planner still uses the composite index. This helps you &lt;em&gt;feel&lt;/em&gt; the leftmost-prefix rule. 🧠&lt;/p&gt;




&lt;h2&gt;
  
  
  🔟 Transactions in SQLAlchemy 2.0 🐍
&lt;/h2&gt;

&lt;p&gt;Now let's connect all this to your real stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⚡ FastAPI&lt;/li&gt;
&lt;li&gt;🧪 SQLAlchemy 2.0&lt;/li&gt;
&lt;li&gt;🐘 PostgreSQL&lt;/li&gt;
&lt;li&gt;🔄 &lt;code&gt;AsyncSession&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In raw SQL we write &lt;code&gt;BEGIN; COMMIT; ROLLBACK;&lt;/code&gt;. In SQLAlchemy we use &lt;strong&gt;transaction methods or context managers&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  10.1 ✅ Basic transaction
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;update&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;transfer_stock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;)&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;with&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stock&lt;/span&gt;&lt;span class="o"&gt;=&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;stock&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stock&lt;/span&gt;&lt;span class="o"&gt;=&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;stock&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;10&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;(&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;ul&gt;
&lt;li&gt;Starts a transaction&lt;/li&gt;
&lt;li&gt;If the block finishes normally → &lt;strong&gt;COMMIT&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;If an exception escapes the block → &lt;strong&gt;ROLLBACK&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  →  &lt;span class="n"&gt;transaction&lt;/span&gt; &lt;span class="n"&gt;starts&lt;/span&gt;
    &lt;span class="no"&gt;UPDATE&lt;/span&gt; &lt;span class="c1"&gt;#1&lt;/span&gt;
    &lt;span class="no"&gt;UPDATE&lt;/span&gt; &lt;span class="c1"&gt;#2&lt;/span&gt;
    &lt;span class="n"&gt;no&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt;  →  &lt;span class="no"&gt;COMMIT&lt;/span&gt; ✅
    &lt;span class="n"&gt;exception&lt;/span&gt;     →  &lt;span class="no"&gt;ROLLBACK&lt;/span&gt; ↩️
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Heads up:&lt;/strong&gt; &lt;code&gt;session.begin()&lt;/code&gt; raises an error if a transaction has &lt;em&gt;already&lt;/em&gt; started on that session (for example, because you already ran a query on it). In FastAPI apps where a dependency provides the session, many projects instead use &lt;code&gt;await session.commit()&lt;/code&gt; / &lt;code&gt;await session.rollback()&lt;/code&gt; explicitly, or &lt;code&gt;session.begin_nested()&lt;/code&gt;. Follow whichever pattern your project already uses, consistently.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  10.2 💥 Deliberately failing
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;transfer_stock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;)&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;with&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stock&lt;/span&gt;&lt;span class="o"&gt;=&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;stock&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Something went wrong!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;          &lt;span class="c1"&gt;# never reached&lt;/span&gt;
            &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stock&lt;/span&gt;&lt;span class="o"&gt;=&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;stock&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;10&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;p&gt;What happens:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Transaction begins&lt;/li&gt;
&lt;li&gt;First stock update executes&lt;/li&gt;
&lt;li&gt;An exception occurs&lt;/li&gt;
&lt;li&gt;The second update is never reached&lt;/li&gt;
&lt;li&gt;SQLAlchemy &lt;strong&gt;rolls back&lt;/strong&gt; the transaction&lt;/li&gt;
&lt;li&gt;The first update is undone ↩️&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's &lt;strong&gt;atomicity inside your FastAPI backend!&lt;/strong&gt; 🎉&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🚨 &lt;strong&gt;Important:&lt;/strong&gt; the exception must &lt;strong&gt;escape&lt;/strong&gt; the &lt;code&gt;async with&lt;/code&gt; block for the automatic rollback. If you catch and swallow it &lt;em&gt;inside&lt;/em&gt; the block, the context manager sees a normal exit and &lt;strong&gt;commits&lt;/strong&gt;. And never keep using a failed transaction without rolling it back.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  10.3 🧰 The explicit style (very common in FastAPI)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;caption&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;          &lt;span class="c1"&gt;# get post.id without committing&lt;/span&gt;

        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PostMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&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;language&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;         &lt;span class="c1"&gt;# ✅ save everything&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rollback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;       &lt;span class="c1"&gt;# ↩️ undo everything&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;flush()&lt;/code&gt; sends pending SQL to the database &lt;strong&gt;inside the transaction&lt;/strong&gt;, but doesn't commit&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;commit()&lt;/code&gt; makes it permanent&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;rollback()&lt;/code&gt; undoes uncommitted work&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  1️⃣1️⃣ Adding indexes with SQLAlchemy models and Alembic 🧱
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;orm&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mapped_column&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;)&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="s2"&gt;"users"&lt;/span&gt;

    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&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="nc"&gt;Integer&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="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&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;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&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;150&lt;/span&gt;&lt;span class="p"&gt;)&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="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;primary_key=True&lt;/code&gt; → creates the primary key constraint (and its index)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;unique=True&lt;/code&gt; → creates a uniqueness constraint (and its unique index)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Creating a normal index
&lt;/h3&gt;

&lt;p&gt;The simplest way is &lt;code&gt;index=True&lt;/code&gt; on the column:&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;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&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;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or declare it explicitly in &lt;code&gt;__table_args__&lt;/code&gt;:&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;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;Index&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;)&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="s2"&gt;"users"&lt;/span&gt;

    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&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="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&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;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;__table_args__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nc"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"idx_users_username"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;)&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;h3&gt;
  
  
  Creating a composite index
&lt;/h3&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;Order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;)&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="s2"&gt;"orders"&lt;/span&gt;

    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&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="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&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="nc"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&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;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;__table_args__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nc"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"idx_orders_user_status"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;)&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;blockquote&gt;
&lt;p&gt;💡 A bare &lt;code&gt;Index("name", User.username)&lt;/code&gt; statement on its own line only works when the columns are already attached to a table. Putting indexes inside &lt;code&gt;__table_args__&lt;/code&gt; is the cleanest, most reliable pattern.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  🧬 Using Alembic migrations
&lt;/h3&gt;

&lt;p&gt;In real projects, schema changes go through &lt;strong&gt;Alembic migrations&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;alembic revision --autogenerate -m &lt;span class="s2"&gt;"add user indexes"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Review the generated file, then apply it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;alembic upgrade head
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also write a migration by hand:&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;from&lt;/span&gt; &lt;span class="n"&gt;alembic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;upgrade&lt;/span&gt;&lt;span class="p"&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;op&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;"idx_users_username"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;downgrade&lt;/span&gt;&lt;span class="p"&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;op&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drop_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;"idx_users_username"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;table_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"users"&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;ul&gt;
&lt;li&gt;
&lt;code&gt;upgrade()&lt;/code&gt; → applies the change&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;downgrade()&lt;/code&gt; → reverses it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Schema changes should be &lt;strong&gt;version-controlled&lt;/strong&gt; just like application code. 📜&lt;/p&gt;




&lt;h2&gt;
  
  
  1️⃣2️⃣ Where are transactions and indexes used in real life? 🌍
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Application&lt;/th&gt;
&lt;th&gt;Transactions&lt;/th&gt;
&lt;th&gt;Indexes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🏦 Banking&lt;/td&gt;
&lt;td&gt;Money transfers&lt;/td&gt;
&lt;td&gt;Account ID lookup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🛒 E-commerce&lt;/td&gt;
&lt;td&gt;Stock and order updates&lt;/td&gt;
&lt;td&gt;Product ID, category&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;📸 Instagram-like app&lt;/td&gt;
&lt;td&gt;Creating a post with related records&lt;/td&gt;
&lt;td&gt;User ID, post ID, &lt;code&gt;created_at&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🍕 Food delivery&lt;/td&gt;
&lt;td&gt;Order creation + payment records&lt;/td&gt;
&lt;td&gt;Restaurant ID, order status&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🏥 Hospital management&lt;/td&gt;
&lt;td&gt;Updating patient records&lt;/td&gt;
&lt;td&gt;Patient ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🔐 Authentication&lt;/td&gt;
&lt;td&gt;Creating sessions&lt;/td&gt;
&lt;td&gt;Email, token hash&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  📸 In a social-feed backend
&lt;/h3&gt;

&lt;p&gt;When creating a post:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Insert the post record&lt;/li&gt;
&lt;li&gt;Insert media records&lt;/li&gt;
&lt;li&gt;Insert related metadata&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Commit&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If any step fails, you want the &lt;strong&gt;entire&lt;/strong&gt; database operation rolled back.&lt;/p&gt;

&lt;p&gt;And if you frequently run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;a composite index designed around that query helps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_posts_user_created&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="nf"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;DESC&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;blockquote&gt;
&lt;p&gt;✅ Always confirm with &lt;code&gt;EXPLAIN ANALYZE&lt;/code&gt; on realistic data. Don't guess.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  1️⃣3️⃣ Common beginner mistakes ⚠️
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;❌ Mistake 1: thinking every SQL statement is automatically a big safe transaction.&lt;/strong&gt;&lt;br&gt;
Without an explicit &lt;code&gt;BEGIN&lt;/code&gt;, PostgreSQL treats each statement as its own transaction (autocommit). Database libraries often manage transactions for you, so know what yours does.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;❌ Mistake 2: thinking ROLLBACK can undo everything.&lt;/strong&gt;&lt;br&gt;
It only undoes &lt;strong&gt;uncommitted&lt;/strong&gt; changes in the current transaction. Once committed, an ordinary rollback can't reverse it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;❌ Mistake 3: indexing every column.&lt;/strong&gt;&lt;br&gt;
Indexes consume storage and slow down writes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;❌ Mistake 4: thinking an index always makes queries faster.&lt;/strong&gt;&lt;br&gt;
The planner may choose a Seq Scan if it's cheaper.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;❌ Mistake 5: thinking COMMIT covers external actions.&lt;/strong&gt;&lt;br&gt;
A database transaction can't roll back an &lt;strong&gt;email&lt;/strong&gt;, an &lt;strong&gt;external payment gateway call&lt;/strong&gt; or an &lt;strong&gt;already-uploaded image&lt;/strong&gt;. Those need extra patterns such as &lt;em&gt;compensating actions&lt;/em&gt; or the &lt;em&gt;transactional outbox pattern&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;❌ Mistake 6: confusing consistency with atomicity.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Atomicity = all or nothing&lt;/li&gt;
&lt;li&gt;Consistency = valid data before and after&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  1️⃣4️⃣ Quick revision sheet 🧾
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Transactions
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BEGIN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start a transaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;COMMIT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Save changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ROLLBACK&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Undo uncommitted changes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  ACID
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;⚛️ &lt;strong&gt;Atomicity:&lt;/strong&gt; all or nothing&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Consistency:&lt;/strong&gt; maintain valid database states&lt;/li&gt;
&lt;li&gt;🚦 &lt;strong&gt;Isolation:&lt;/strong&gt; concurrent transactions don't interfere incorrectly&lt;/li&gt;
&lt;li&gt;💾 &lt;strong&gt;Durability:&lt;/strong&gt; committed changes survive failures covered by the database's durability guarantees&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Indexes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🆔 &lt;strong&gt;Primary key index:&lt;/strong&gt; created automatically&lt;/li&gt;
&lt;li&gt;🦄 &lt;strong&gt;Unique index:&lt;/strong&gt; prevents duplicate indexed values&lt;/li&gt;
&lt;li&gt;🧩 &lt;strong&gt;Composite index:&lt;/strong&gt; indexes multiple columns together (order matters!)&lt;/li&gt;
&lt;li&gt;🌳 &lt;strong&gt;B-tree:&lt;/strong&gt; PostgreSQL's general-purpose default&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  EXPLAIN
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;📖 &lt;strong&gt;Seq Scan:&lt;/strong&gt; reads rows sequentially&lt;/li&gt;
&lt;li&gt;🎯 &lt;strong&gt;Index Scan:&lt;/strong&gt; uses an index to find rows&lt;/li&gt;
&lt;li&gt;⚡ &lt;strong&gt;Index Only Scan:&lt;/strong&gt; answers from the index alone&lt;/li&gt;
&lt;li&gt;📏 &lt;strong&gt;EXPLAIN ANALYZE:&lt;/strong&gt; executes and measures the query&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  1️⃣5️⃣ Test your understanding! 🧪
&lt;/h2&gt;

&lt;p&gt;Answer without scrolling up. (Answers are at the bottom.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. You subtract ₹1,000 from Account A, but adding it to Account B fails. What should happen?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Commit the first operation&lt;/li&gt;
&lt;li&gt;B) Roll back the entire transaction&lt;/li&gt;
&lt;li&gt;C) Ignore the error&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Which SQL command permanently saves a successful transaction?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) &lt;code&gt;BEGIN&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;B) &lt;code&gt;ROLLBACK&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;C) &lt;code&gt;COMMIT&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. What does &lt;code&gt;CHECK (stock &amp;gt;= 0)&lt;/code&gt; help ensure?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Stock cannot become negative&lt;/li&gt;
&lt;li&gt;B) Stock is always increasing&lt;/li&gt;
&lt;li&gt;C) Every product has a unique name&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Which index suits a frequent query filtering by &lt;code&gt;user_id&lt;/code&gt; and &lt;code&gt;status&lt;/code&gt; together?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Composite index&lt;/li&gt;
&lt;li&gt;B) Only a primary key index&lt;/li&gt;
&lt;li&gt;C) No index can do this&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;5. What does &lt;code&gt;EXPLAIN ANALYZE&lt;/code&gt; do?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Only displays the SQL query&lt;/li&gt;
&lt;li&gt;B) Executes the query and reports actual execution details&lt;/li&gt;
&lt;li&gt;C) Creates an index automatically&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👀 Click to reveal answers&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;B&lt;/strong&gt;: roll back the entire transaction (atomicity)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C&lt;/strong&gt;: &lt;code&gt;COMMIT&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A&lt;/strong&gt;: stock can't go negative (consistency)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A&lt;/strong&gt;: a composite index on &lt;code&gt;(user_id, status)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;B&lt;/strong&gt;: it runs the query and measures real timings&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  🎯 What to remember as a backend developer
&lt;/h2&gt;

&lt;p&gt;When you build a real application, you use these concepts &lt;strong&gt;together&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Transactions&lt;/strong&gt; protect multiple related database operations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;COMMIT&lt;/strong&gt; saves successful operations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ROLLBACK&lt;/strong&gt; undoes failed operations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Atomicity&lt;/strong&gt; prevents partial transactions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistency&lt;/strong&gt; ensures database rules are maintained&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Indexes&lt;/strong&gt; help retrieve records efficiently&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EXPLAIN ANALYZE&lt;/strong&gt; helps investigate query performance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQLAlchemy transactions&lt;/strong&gt; manage database operations safely in FastAPI&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alembic&lt;/strong&gt; manages index and schema changes over time&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;One final distinction:&lt;/strong&gt; transactions protect the &lt;strong&gt;correctness&lt;/strong&gt; of your database operations, while indexes help the database &lt;strong&gt;retrieve&lt;/strong&gt; information efficiently. They solve two different problems, and both are essential for reliable, scalable backend systems.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  🙌 Enjoyed this post?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;❤️ Drop a reaction&lt;/li&gt;
&lt;li&gt;🦄 Save it for revision&lt;/li&gt;
&lt;li&gt;💬 Comment with questions, and I'll answer them!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy coding! 👩‍💻✨&lt;/p&gt;

&lt;h1&gt;
  
  
  postgres #sql #database #backend #sqlalchemy
&lt;/h1&gt;

</description>
      <category>backend</category>
      <category>database</category>
      <category>postgres</category>
      <category>sql</category>
    </item>
    <item>
      <title>Relational Databases, SQL, Normalization &amp; ACID: A Beginner-Friendly Guide 🗄️</title>
      <dc:creator>Deepika Pusala</dc:creator>
      <pubDate>Wed, 11 Nov 2026 07:31:03 +0000</pubDate>
      <link>https://dev.to/deepika_pusala/relational-databases-sql-normalization-acid-a-beginner-friendly-guide-5cj5</link>
      <guid>https://dev.to/deepika_pusala/relational-databases-sql-normalization-acid-a-beginner-friendly-guide-5cj5</guid>
      <description>&lt;h1&gt;
  
  
  🗄️ Relational Databases, SQL, Normalization &amp;amp; ACID
&lt;/h1&gt;

&lt;p&gt;This is one of the most important topics to understand &lt;strong&gt;before&lt;/strong&gt; working with SQLAlchemy, PostgreSQL, FastAPI and backend development. 💪&lt;/p&gt;

&lt;p&gt;We'll learn &lt;strong&gt;relational database concepts, SQL, normalization and ACID properties&lt;/strong&gt; from absolute zero. No prior database knowledge is assumed, no skipped fundamentals, and no terminology without explanation.&lt;/p&gt;

&lt;p&gt;Along the way we'll connect everything to a real &lt;strong&gt;Instagram-like Social Feed project&lt;/strong&gt; (React + FastAPI + PostgreSQL), so you can see how these ideas are actually used. 🚀&lt;/p&gt;




&lt;h2&gt;
  
  
  📚 Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;What is a database?&lt;/li&gt;
&lt;li&gt;Tables, rows and columns&lt;/li&gt;
&lt;li&gt;Keys (Primary, Foreign, Composite...)&lt;/li&gt;
&lt;li&gt;Relationships (1:1, 1:N, N:N)&lt;/li&gt;
&lt;li&gt;SQL (DDL, DML, DQL)&lt;/li&gt;
&lt;li&gt;JOINs&lt;/li&gt;
&lt;li&gt;Aggregations, GROUP BY and HAVING&lt;/li&gt;
&lt;li&gt;Normalization (1NF, 2NF, 3NF)&lt;/li&gt;
&lt;li&gt;ACID properties and transactions&lt;/li&gt;
&lt;li&gt;Constraints&lt;/li&gt;
&lt;li&gt;Designing an Instagram database&lt;/li&gt;
&lt;li&gt;SQL → SQLAlchemy → FastAPI&lt;/li&gt;
&lt;li&gt;Interview questions&lt;/li&gt;
&lt;li&gt;Quiz and final revision&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1️⃣ First, what is a database?
&lt;/h2&gt;

&lt;p&gt;Imagine you're building &lt;strong&gt;Instagram&lt;/strong&gt; 📸. Your app needs to store:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;👤 Users&lt;/li&gt;
&lt;li&gt;🖼️ Posts&lt;/li&gt;
&lt;li&gt;💬 Comments&lt;/li&gt;
&lt;li&gt;❤️ Likes&lt;/li&gt;
&lt;li&gt;👥 Followers&lt;/li&gt;
&lt;li&gt;🔑 Login sessions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Where does all this go? Into a &lt;strong&gt;database&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📦 A &lt;strong&gt;database&lt;/strong&gt; is an organized collection of data that lets applications &lt;strong&gt;store, retrieve, update and delete&lt;/strong&gt; information.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;React (Frontend)         → user clicks "Like"
      │  HTTP request / response
      ▼
FastAPI (Backend)        → validates, applies business logic
      │  SQL via SQLAlchemy
      ▼
PostgreSQL (Database)    → stores users, posts, likes, comments...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a user creates a post:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User types a caption in React&lt;/li&gt;
&lt;li&gt;React sends a request to FastAPI&lt;/li&gt;
&lt;li&gt;FastAPI validates it&lt;/li&gt;
&lt;li&gt;FastAPI talks to PostgreSQL&lt;/li&gt;
&lt;li&gt;PostgreSQL stores the post&lt;/li&gt;
&lt;li&gt;The backend returns a response&lt;/li&gt;
&lt;li&gt;React shows the new post ✅&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ The frontend does &lt;strong&gt;not&lt;/strong&gt; normally talk directly to your database. The &lt;strong&gt;backend&lt;/strong&gt; is the middleman.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  1.1 🧩 What is a Relational Database?
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;relational database&lt;/strong&gt; stores data in &lt;strong&gt;tables&lt;/strong&gt; and defines &lt;strong&gt;relationships&lt;/strong&gt; between those tables.&lt;/p&gt;

&lt;p&gt;Popular ones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🐘 PostgreSQL&lt;/li&gt;
&lt;li&gt;🐬 MySQL&lt;/li&gt;
&lt;li&gt;🪶 SQLite&lt;/li&gt;
&lt;li&gt;🏢 Microsoft SQL Server&lt;/li&gt;
&lt;li&gt;🏛️ Oracle Database&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We'll use &lt;strong&gt;PostgreSQL&lt;/strong&gt; in our examples.&lt;/p&gt;

&lt;p&gt;Why "relational"? Because tables connect to each other:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One user can create &lt;strong&gt;many&lt;/strong&gt; posts&lt;/li&gt;
&lt;li&gt;One post can have &lt;strong&gt;many&lt;/strong&gt; comments&lt;/li&gt;
&lt;li&gt;One post can receive &lt;strong&gt;many&lt;/strong&gt; likes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of one giant table, we split information into &lt;strong&gt;logically related tables&lt;/strong&gt;. That's the central idea of the relational model.&lt;/p&gt;




&lt;h2&gt;
  
  
  2️⃣ Tables, Rows and Columns
&lt;/h2&gt;

&lt;p&gt;Here's a simple &lt;code&gt;users&lt;/code&gt; table:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;user_id&lt;/th&gt;
&lt;th&gt;username&lt;/th&gt;
&lt;th&gt;email&lt;/th&gt;
&lt;th&gt;age&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;ishu&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:ishu@gmail.com"&gt;ishu@gmail.com&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;rahul&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:rahul@gmail.com"&gt;rahul@gmail.com&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;23&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;priya&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:priya@gmail.com"&gt;priya@gmail.com&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;21&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Term&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;📋 &lt;strong&gt;Table&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Collection of related data&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;users&lt;/code&gt;, &lt;code&gt;posts&lt;/code&gt;, &lt;code&gt;comments&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;➡️ &lt;strong&gt;Row&lt;/strong&gt; (record / tuple)&lt;/td&gt;
&lt;td&gt;One complete entry&lt;/td&gt;
&lt;td&gt;One user's information&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;⬇️ &lt;strong&gt;Column&lt;/strong&gt; (attribute)&lt;/td&gt;
&lt;td&gt;One property of the data&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;username&lt;/code&gt;, &lt;code&gt;email&lt;/code&gt;, &lt;code&gt;age&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🔲 &lt;strong&gt;Cell&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;A single value at a row/column intersection&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ishu@gmail.com&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;📐 &lt;strong&gt;Schema&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;The blueprint: tables, columns, types, constraints, relationships&lt;/td&gt;
&lt;td&gt;The whole structure&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  2.1 🔤 Data types
&lt;/h3&gt;

&lt;p&gt;Every column has a &lt;strong&gt;data type&lt;/strong&gt; that decides what it can store.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Data type&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;INTEGER&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Whole numbers&lt;/td&gt;
&lt;td&gt;&lt;code&gt;25&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;VARCHAR(n)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Variable-length text (max n)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;'Ishu'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TEXT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Text of any length&lt;/td&gt;
&lt;td&gt;A post caption&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BOOLEAN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;True or false&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TRUE&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DATE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Calendar date&lt;/td&gt;
&lt;td&gt;&lt;code&gt;2026-11-11&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TIMESTAMP&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Date and time&lt;/td&gt;
&lt;td&gt;&lt;code&gt;2026-11-11 12:30:00&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DECIMAL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Exact decimal numbers&lt;/td&gt;
&lt;td&gt;&lt;code&gt;99.50&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UUID&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unique identifier&lt;/td&gt;
&lt;td&gt;&lt;code&gt;550e8400-...&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt;   &lt;span class="kt"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;  &lt;span class="kt"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;     &lt;span class="kt"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;age&lt;/span&gt;       &lt;span class="kt"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;is_active&lt;/span&gt; &lt;span class="kt"&gt;BOOLEAN&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;ul&gt;
&lt;li&gt;
&lt;code&gt;users&lt;/code&gt; → the table name&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;username&lt;/code&gt; → text, max 50 characters&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;email&lt;/code&gt; → text, max 100 characters&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;is_active&lt;/code&gt; → a Boolean&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2.2 🕳️ What is &lt;code&gt;NULL&lt;/code&gt;?
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;NULL&lt;/code&gt; means a value is &lt;strong&gt;missing or unknown&lt;/strong&gt;. It is &lt;strong&gt;NOT&lt;/strong&gt; the same as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ &lt;code&gt;0&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;❌ an empty string &lt;code&gt;''&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;❌ &lt;code&gt;FALSE&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'ishu'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'ishu@gmail.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;NULL&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;p&gt;To find users whose age is missing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="kc"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🚨 You &lt;strong&gt;cannot&lt;/strong&gt; write &lt;code&gt;age = NULL&lt;/code&gt;. NULL is an unknown value, not an ordinary comparable value. Always use &lt;code&gt;IS NULL&lt;/code&gt; / &lt;code&gt;IS NOT NULL&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  3️⃣ Keys in Relational Databases 🔑
&lt;/h2&gt;

&lt;p&gt;Imagine two users with the same name:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;user_id&lt;/th&gt;
&lt;th&gt;username&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Rahul&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Rahul&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you want to update the &lt;strong&gt;first&lt;/strong&gt; Rahul's email, how does the database know which one? We need something that &lt;strong&gt;uniquely identifies each row&lt;/strong&gt;. That's what &lt;strong&gt;keys&lt;/strong&gt; do.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.1 🆔 Primary Key (PK)
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;Primary Key&lt;/strong&gt; is a column (or combination of columns) that &lt;strong&gt;uniquely identifies every row&lt;/strong&gt; in a table.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt;  &lt;span class="kt"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="kt"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;    &lt;span class="kt"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&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;p&gt;Rules:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Every primary key value must be &lt;strong&gt;unique&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;A primary key can &lt;strong&gt;never be NULL&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;A table has &lt;strong&gt;only one&lt;/strong&gt; primary key constraint (though it may span multiple columns)&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;user_id (PK)&lt;/th&gt;
&lt;th&gt;username&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Rahul&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Rahul&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Priya&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Usernames can repeat, but IDs can't.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🎓 &lt;strong&gt;Analogy:&lt;/strong&gt; your college USN. Many students share a name, but every student has a distinct USN.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3.2 🔗 Foreign Key (FK)
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;Foreign Key&lt;/strong&gt; is a column (or group of columns) that &lt;strong&gt;references a key in another table&lt;/strong&gt; (usually its primary key or a UNIQUE key).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Users&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;user_id (PK)&lt;/th&gt;
&lt;th&gt;username&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Ishu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Rahul&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Priya&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Posts&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;post_id (PK)&lt;/th&gt;
&lt;th&gt;caption&lt;/th&gt;
&lt;th&gt;user_id (FK)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;My first post&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;Travel memories&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;Coding journey&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;user_id&lt;/code&gt; in &lt;code&gt;posts&lt;/code&gt; tells us &lt;strong&gt;who created the post&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Post 101 → user 1&lt;/li&gt;
&lt;li&gt;Post 102 → user 1&lt;/li&gt;
&lt;li&gt;Post 103 → user 2
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;post_id&lt;/span&gt; &lt;span class="kt"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;caption&lt;/span&gt; &lt;span class="kt"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="kt"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;FOREIGN&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="nf"&gt;users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&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;h4&gt;
  
  
  🛡️ Why is a Foreign Key important?
&lt;/h4&gt;

&lt;p&gt;What if someone tries this?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;104&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Hello'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;999&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;p&gt;User &lt;code&gt;999&lt;/code&gt; doesn't exist, so the foreign key constraint &lt;strong&gt;blocks the insert&lt;/strong&gt;. This is called &lt;strong&gt;Referential Integrity&lt;/strong&gt;: relationships between tables always stay valid.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.3 🗝️ Other important keys
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Key&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Super Key&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Any set of columns that uniquely identifies a row&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Candidate Key&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A &lt;em&gt;minimal&lt;/em&gt; super key (no unnecessary columns)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Alternate Key&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A candidate key not chosen as the primary key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Composite Key&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A key made of multiple columns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Unique Key&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A constraint that prevents duplicate values&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt;  &lt;span class="kt"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="kt"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;    &lt;span class="kt"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&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;p&gt;Here &lt;code&gt;user_id&lt;/code&gt; is the primary key, while &lt;code&gt;username&lt;/code&gt; and &lt;code&gt;email&lt;/code&gt; are unique (and are also candidate keys).&lt;/p&gt;

&lt;h3&gt;
  
  
  3.4 🧱 Composite Primary Key
&lt;/h3&gt;

&lt;p&gt;Think of a &lt;code&gt;likes&lt;/code&gt; table:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;user_id&lt;/th&gt;
&lt;th&gt;post_id&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;One user can like many posts, and one post can be liked by many users. But the &lt;strong&gt;same user shouldn't like the same post twice&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;likes&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="kt"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;post_id&lt;/span&gt; &lt;span class="kt"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="p"&gt;)&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;p&gt;The &lt;strong&gt;combination&lt;/strong&gt; &lt;code&gt;(user_id, post_id)&lt;/code&gt; is unique. Neither column needs to be unique on its own.&lt;/p&gt;




&lt;h2&gt;
  
  
  4️⃣ Relationships in Relational Databases 🤝
&lt;/h2&gt;

&lt;p&gt;There are three main types:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;One-to-One (1:1)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;One-to-Many (1:N)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Many-to-Many (N:N)&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  4.1 1️⃣ One-to-One (1:1)
&lt;/h3&gt;

&lt;p&gt;One record in Table A relates to &lt;strong&gt;at most one&lt;/strong&gt; record in Table B, and vice versa. Example: &lt;strong&gt;one user has one profile&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;users&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;user_id&lt;/th&gt;
&lt;th&gt;username&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Ishu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Rahul&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;profiles&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;profile_id&lt;/th&gt;
&lt;th&gt;user_id (FK)&lt;/th&gt;
&lt;th&gt;bio&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Developer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Student&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;profiles&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;profile_id&lt;/span&gt; &lt;span class="kt"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt;    &lt;span class="kt"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="nf"&gt;users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;bio&lt;/span&gt;        &lt;span class="kt"&gt;TEXT&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;p&gt;Why &lt;code&gt;UNIQUE&lt;/code&gt; on &lt;code&gt;user_id&lt;/code&gt;? Without it, multiple profiles could point to the same user. &lt;code&gt;UNIQUE&lt;/code&gt; guarantees &lt;strong&gt;at most one profile per user&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Common uses: user profiles, employee details, passport info, account settings.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.2 🌳 One-to-Many (1:N)
&lt;/h3&gt;

&lt;p&gt;One record in Table A can relate to &lt;strong&gt;many&lt;/strong&gt; records in Table B, but each B record belongs to &lt;strong&gt;one&lt;/strong&gt; A record. Example: &lt;strong&gt;one user creates many posts&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;users&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;user_id&lt;/th&gt;
&lt;th&gt;username&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Ishu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Rahul&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;posts&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;post_id&lt;/th&gt;
&lt;th&gt;user_id&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;104&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;Golden rule:&lt;/strong&gt; the foreign key goes on the &lt;strong&gt;MANY&lt;/strong&gt; side. So &lt;code&gt;posts.user_id&lt;/code&gt; references &lt;code&gt;users.user_id&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;More examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One teacher → many students&lt;/li&gt;
&lt;li&gt;One customer → many orders&lt;/li&gt;
&lt;li&gt;One post → many comments&lt;/li&gt;
&lt;li&gt;One department → many employees&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4.3 🕸️ Many-to-Many (N:N)
&lt;/h3&gt;

&lt;p&gt;Many records in A relate to many records in B. Example: &lt;strong&gt;students and courses&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One student can enroll in many courses&lt;/li&gt;
&lt;li&gt;One course can have many students&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A single foreign key in either table can't model this. So we add a third table called a &lt;strong&gt;junction table&lt;/strong&gt; (also: &lt;em&gt;bridge table&lt;/em&gt; or &lt;em&gt;associative table&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;students&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;student_id&lt;/th&gt;
&lt;th&gt;name&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Ishu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Rahul&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;courses&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;course_id&lt;/th&gt;
&lt;th&gt;course_name&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;SQL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;enrollments&lt;/strong&gt; (junction)&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;student_id&lt;/th&gt;
&lt;th&gt;course_id&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;enrollments&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;student_id&lt;/span&gt; &lt;span class="kt"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="nf"&gt;students&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;student_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;course_id&lt;/span&gt;  &lt;span class="kt"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="nf"&gt;courses&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;course_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;student_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;course_id&lt;/span&gt;&lt;span class="p"&gt;)&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;p&gt;This turns &lt;strong&gt;one many-to-many&lt;/strong&gt; relationship into &lt;strong&gt;two one-to-many&lt;/strong&gt; relationships. 🎯&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Instagram example:&lt;/strong&gt; Users ↔ Posts is many-to-many through &lt;strong&gt;Likes&lt;/strong&gt;. One user can like many posts, and one post can be liked by many users.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.4 ⚖️ Quick comparison
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Relationship&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;Implementation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1:1&lt;/td&gt;
&lt;td&gt;User → Profile&lt;/td&gt;
&lt;td&gt;FK + &lt;code&gt;UNIQUE&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1:N&lt;/td&gt;
&lt;td&gt;User → Posts&lt;/td&gt;
&lt;td&gt;FK on the many side&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;N:N&lt;/td&gt;
&lt;td&gt;Students ↔ Courses&lt;/td&gt;
&lt;td&gt;Junction table&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  5️⃣ SQL: Structured Query Language 💬
&lt;/h2&gt;

&lt;p&gt;How do we talk to a relational database? With &lt;strong&gt;SQL&lt;/strong&gt; (Structured Query Language). With SQL we can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create tables&lt;/li&gt;
&lt;li&gt;Insert, retrieve, update and delete records&lt;/li&gt;
&lt;li&gt;Create relationships and apply constraints&lt;/li&gt;
&lt;li&gt;Manage transactions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SQL commands are grouped into categories.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.1 🏗️ DDL: Data Definition Language
&lt;/h3&gt;

&lt;p&gt;DDL &lt;strong&gt;defines or changes the structure&lt;/strong&gt; of database objects.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CREATE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Creates a table or other object&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ALTER&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Modifies an existing table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DROP&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Deletes an object&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TRUNCATE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Removes &lt;strong&gt;all rows&lt;/strong&gt;, keeps the table&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- CREATE&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt;  &lt;span class="kt"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="kt"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;    &lt;span class="kt"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- ALTER: add an age column&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="k"&gt;COLUMN&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="kt"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- TRUNCATE: remove all rows, keep structure&lt;/span&gt;
&lt;span class="k"&gt;TRUNCATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- DROP: remove the table itself (structure + data)&lt;/span&gt;
&lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5.2 ✏️ DML: Data Manipulation Language
&lt;/h3&gt;

&lt;p&gt;DML &lt;strong&gt;manipulates the data&lt;/strong&gt; inside tables: &lt;code&gt;INSERT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- INSERT: add data&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'ishu'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'ishu@gmail.com'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- UPDATE: modify existing data&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'deepika'&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- DELETE: remove data&lt;/span&gt;
&lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🚨 &lt;strong&gt;Never forget &lt;code&gt;WHERE&lt;/code&gt;!&lt;/strong&gt; Without it, &lt;code&gt;UPDATE&lt;/code&gt; changes &lt;strong&gt;every row&lt;/strong&gt; and &lt;code&gt;DELETE&lt;/code&gt; removes &lt;strong&gt;every row&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  5.3 🔎 DQL: Data Query Language
&lt;/h3&gt;

&lt;p&gt;DQL retrieves data. The main command is &lt;code&gt;SELECT&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- all columns&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- specific columns&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- filter rows&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Useful comparison operators:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operator&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Equal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;!=&lt;/code&gt; or &lt;code&gt;&amp;lt;&amp;gt;&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Not equal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Greater than&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Less than&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Greater than or equal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Less than or equal&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;More filtering tools:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- AND / OR&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;is_active&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- IN&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ishu'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'rahul'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- BETWEEN&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="k"&gt;BETWEEN&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- LIKE (pattern matching)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%@gmail.com'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- ORDER BY + LIMIT&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🧠 &lt;code&gt;%&lt;/code&gt; in &lt;code&gt;LIKE&lt;/code&gt; means "any number of characters".&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  5.4 🔐 Other SQL categories
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Examples&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DDL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Define structure&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;CREATE&lt;/code&gt;, &lt;code&gt;ALTER&lt;/code&gt;, &lt;code&gt;DROP&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DML&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Change data&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;INSERT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DQL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Query data&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SELECT&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TCL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Control transactions&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;BEGIN&lt;/code&gt;, &lt;code&gt;COMMIT&lt;/code&gt;, &lt;code&gt;ROLLBACK&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DCL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Control permissions&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;GRANT&lt;/code&gt;, &lt;code&gt;REVOKE&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  6️⃣ SQL JOINs 🔀
&lt;/h2&gt;

&lt;p&gt;Data lives in separate tables, so how do we combine it? With &lt;strong&gt;JOINs&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We'll use this sample data throughout:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;users&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;user_id&lt;/th&gt;
&lt;th&gt;username&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Ishu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Rahul&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Priya&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Arjun&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;posts&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;post_id&lt;/th&gt;
&lt;th&gt;caption&lt;/th&gt;
&lt;th&gt;user_id&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;Hello World&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;My Travel&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;Coding&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;104&lt;/td&gt;
&lt;td&gt;Photography&lt;/td&gt;
&lt;td&gt;NULL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Post 104 has no owner (&lt;code&gt;user_id&lt;/code&gt; is &lt;code&gt;NULL&lt;/code&gt;). In a well-designed schema &lt;code&gt;posts.user_id&lt;/code&gt; would be &lt;code&gt;NOT NULL&lt;/code&gt;, but this orphan row helps us see how each JOIN behaves.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  6.1 🤝 INNER JOIN
&lt;/h3&gt;

&lt;p&gt;Returns &lt;strong&gt;only rows that have a match in both tables&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;caption&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;username&lt;/th&gt;
&lt;th&gt;caption&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ishu&lt;/td&gt;
&lt;td&gt;Hello World&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ishu&lt;/td&gt;
&lt;td&gt;My Travel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rahul&lt;/td&gt;
&lt;td&gt;Coding&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Why no Priya or Arjun? They have &lt;strong&gt;no matching posts&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Why no post 104? Its &lt;code&gt;user_id&lt;/code&gt; &lt;strong&gt;matches no user&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 &lt;strong&gt;INNER JOIN = only matching records.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  6.2 ⬅️ LEFT JOIN (LEFT OUTER JOIN)
&lt;/h3&gt;

&lt;p&gt;Returns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;All&lt;/strong&gt; rows from the &lt;strong&gt;left&lt;/strong&gt; table&lt;/li&gt;
&lt;li&gt;Matching rows from the right table&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NULL&lt;/code&gt; for right-side columns when there's no match
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;caption&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;username&lt;/th&gt;
&lt;th&gt;caption&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ishu&lt;/td&gt;
&lt;td&gt;Hello World&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ishu&lt;/td&gt;
&lt;td&gt;My Travel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rahul&lt;/td&gt;
&lt;td&gt;Coding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Priya&lt;/td&gt;
&lt;td&gt;NULL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Arjun&lt;/td&gt;
&lt;td&gt;NULL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Priya and Arjun appear because LEFT JOIN &lt;strong&gt;protects the left table&lt;/strong&gt; (&lt;code&gt;users&lt;/code&gt;).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 &lt;strong&gt;LEFT JOIN = everything from the left + matching data from the right.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Real use case: &lt;em&gt;"Show all users, including those who haven't posted anything."&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  6.3 ➡️ RIGHT JOIN (RIGHT OUTER JOIN)
&lt;/h3&gt;

&lt;p&gt;The mirror image of LEFT JOIN. Returns &lt;strong&gt;all rows from the right table&lt;/strong&gt; plus matching left rows (&lt;code&gt;NULL&lt;/code&gt; where there's no match).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;caption&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;RIGHT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;username&lt;/th&gt;
&lt;th&gt;caption&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ishu&lt;/td&gt;
&lt;td&gt;Hello World&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ishu&lt;/td&gt;
&lt;td&gt;My Travel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rahul&lt;/td&gt;
&lt;td&gt;Coding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NULL&lt;/td&gt;
&lt;td&gt;Photography&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;"Photography" is included because RIGHT JOIN protects every row of &lt;code&gt;posts&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  6.4 ↔️ FULL OUTER JOIN
&lt;/h3&gt;

&lt;p&gt;Returns &lt;strong&gt;all matches + all unmatched rows from both sides&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;caption&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;FULL&lt;/span&gt; &lt;span class="k"&gt;OUTER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;username&lt;/th&gt;
&lt;th&gt;caption&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ishu&lt;/td&gt;
&lt;td&gt;Hello World&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ishu&lt;/td&gt;
&lt;td&gt;My Travel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rahul&lt;/td&gt;
&lt;td&gt;Coding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Priya&lt;/td&gt;
&lt;td&gt;NULL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Arjun&lt;/td&gt;
&lt;td&gt;NULL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NULL&lt;/td&gt;
&lt;td&gt;Photography&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  6.5 📊 JOIN comparison
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;JOIN&lt;/th&gt;
&lt;th&gt;Returns&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;INNER JOIN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Only matching rows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LEFT JOIN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;All left rows + matching right rows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RIGHT JOIN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;All right rows + matching left rows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FULL OUTER JOIN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;All rows from both tables&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Easy memory trick:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;INNER&lt;/strong&gt; = match only&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LEFT&lt;/strong&gt; = protect the left side&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RIGHT&lt;/strong&gt; = protect the right side&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FULL&lt;/strong&gt; = protect both sides&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 PostgreSQL supports all four. Some other databases have limited support or different syntax (for example, MySQL has no &lt;code&gt;FULL OUTER JOIN&lt;/code&gt;).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  6.6 🧮 Multiple-table JOINs
&lt;/h3&gt;

&lt;p&gt;Real apps often need three or more tables. Let's show the username, post caption and comment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;comments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;
&lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;comments&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;comments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Users connect to Posts&lt;/li&gt;
&lt;li&gt;Posts connect to Comments&lt;/li&gt;
&lt;li&gt;We retrieve information from all three tables ✅&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  7️⃣ SQL Aggregations 📈
&lt;/h2&gt;

&lt;p&gt;Your mentor might ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How many users are registered?&lt;/li&gt;
&lt;li&gt;How many posts exist?&lt;/li&gt;
&lt;li&gt;What's the average number of likes?&lt;/li&gt;
&lt;li&gt;Which user has the most posts?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For this we need &lt;strong&gt;aggregate functions&lt;/strong&gt;: calculations that turn many rows into a single result.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;COUNT()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Counts rows / non-NULL values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SUM()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Adds numeric values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AVG()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Calculates the average&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MIN()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Finds the smallest value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MAX()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Finds the largest value&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  7.1 🔢 COUNT()
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="nb"&gt;COUNT&lt;/span&gt;&lt;span class="cm"&gt;(*) FROM users;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Difference:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;COUNT(*)&lt;/code&gt; → counts &lt;strong&gt;all rows&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;COUNT(age)&lt;/code&gt; → counts only rows where &lt;code&gt;age&lt;/code&gt; is &lt;strong&gt;NOT NULL&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  7.2 ➕ SUM, AVG, MIN, MAX
&lt;/h3&gt;

&lt;p&gt;Imagine a &lt;code&gt;payments&lt;/code&gt; table:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;payment_id&lt;/th&gt;
&lt;th&gt;amount&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1500&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="nb"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;payments&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;-- 3000&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="nb"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;payments&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;-- 1000&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="nb"&gt;MIN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;payments&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;-- 500&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="nb"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;payments&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;-- 1500&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  7.3 🗂️ GROUP BY
&lt;/h3&gt;

&lt;p&gt;How many posts has &lt;strong&gt;each user&lt;/strong&gt; created?&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;post_id&lt;/th&gt;
&lt;th&gt;user_id&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;104&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;105&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;106&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;COUNT&lt;/span&gt;&lt;span class="cm"&gt;(*) AS total_posts
FROM posts
GROUP BY user_id;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;user_id&lt;/th&gt;
&lt;th&gt;total_posts&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;GROUP BY user_id&lt;/code&gt; puts rows with the same &lt;code&gt;user_id&lt;/code&gt; into one group, and &lt;code&gt;COUNT(*)&lt;/code&gt; counts rows in each group. &lt;code&gt;AS total_posts&lt;/code&gt; gives the result a readable name (an &lt;strong&gt;alias&lt;/strong&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  7.4 🚧 HAVING
&lt;/h3&gt;

&lt;p&gt;We only want users with &lt;strong&gt;more than two&lt;/strong&gt; posts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;COUNT&lt;/span&gt;&lt;span class="cm"&gt;(*) AS total_posts
FROM posts
GROUP BY user_id
HAVING COUNT(*)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;user_id&lt;/th&gt;
&lt;th&gt;total_posts&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  ❓ WHERE vs HAVING (a classic interview question!)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;WHERE&lt;/code&gt; filters &lt;strong&gt;individual rows, before&lt;/strong&gt; grouping&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HAVING&lt;/code&gt; filters &lt;strong&gt;groups, after&lt;/strong&gt; &lt;code&gt;GROUP BY&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;COUNT&lt;/span&gt;&lt;span class="cm"&gt;(*) AS total_posts
FROM posts
WHERE post_id &amp;gt; 100
GROUP BY user_id
HAVING COUNT(*)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Logical processing order:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;FROM&lt;/code&gt;: get the source table&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;WHERE&lt;/code&gt;: filter rows&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GROUP BY&lt;/code&gt;: form groups&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HAVING&lt;/code&gt;: filter groups&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SELECT&lt;/code&gt;: produce columns&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ORDER BY&lt;/code&gt;: sort&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;LIMIT&lt;/code&gt;: restrict output&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;📝 This is the &lt;em&gt;logical&lt;/em&gt; order, not necessarily the database's physical execution order.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  8️⃣ Database Normalization 🧹 (VERY IMPORTANT!)
&lt;/h2&gt;

&lt;p&gt;First, the &lt;strong&gt;problem&lt;/strong&gt; normalization solves. A beginner might design one giant table:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;user_id&lt;/th&gt;
&lt;th&gt;username&lt;/th&gt;
&lt;th&gt;post_id&lt;/th&gt;
&lt;th&gt;caption&lt;/th&gt;
&lt;th&gt;comment&lt;/th&gt;
&lt;th&gt;commenter&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Ishu&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;Hello&lt;/td&gt;
&lt;td&gt;Nice!&lt;/td&gt;
&lt;td&gt;Rahul&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Ishu&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;Hello&lt;/td&gt;
&lt;td&gt;Amazing!&lt;/td&gt;
&lt;td&gt;Priya&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Ishu&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;Travel&lt;/td&gt;
&lt;td&gt;Beautiful!&lt;/td&gt;
&lt;td&gt;Rahul&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Rahul&lt;/td&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;Coding&lt;/td&gt;
&lt;td&gt;Great!&lt;/td&gt;
&lt;td&gt;Ishu&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;See the problems? 👀&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔁 &lt;strong&gt;Data redundancy:&lt;/strong&gt; Ishu's username and her post caption repeat in multiple rows. This wastes storage and complicates maintenance.&lt;/li&gt;
&lt;li&gt;✏️ &lt;strong&gt;Update anomaly:&lt;/strong&gt; if Ishu changes her username, you must update &lt;em&gt;every&lt;/em&gt; row. Miss one and your data becomes inconsistent.&lt;/li&gt;
&lt;li&gt;➕ &lt;strong&gt;Insertion anomaly:&lt;/strong&gt; a new user who hasn't posted yet can't be stored cleanly, because the table expects post data.&lt;/li&gt;
&lt;li&gt;🗑️ &lt;strong&gt;Deletion anomaly:&lt;/strong&gt; if Rahul's only post is deleted, you may accidentally lose his user details too.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are called &lt;strong&gt;database anomalies&lt;/strong&gt;, and normalization prevents them.&lt;/p&gt;

&lt;h3&gt;
  
  
  8.1 What is Normalization?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Normalization&lt;/strong&gt; is organizing data into well-structured tables to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Reduce unnecessary duplication&lt;/li&gt;
&lt;li&gt;✅ Prevent insertion, update and deletion anomalies&lt;/li&gt;
&lt;li&gt;✅ Keep data consistent&lt;/li&gt;
&lt;li&gt;✅ Improve data integrity&lt;/li&gt;
&lt;li&gt;✅ Represent relationships correctly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We split large, poorly structured tables into smaller related ones. The foundational normal forms are &lt;strong&gt;1NF, 2NF and 3NF&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  8.2 1️⃣ First Normal Form (1NF)
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt; every cell holds a &lt;strong&gt;single atomic value&lt;/strong&gt;. No repeating groups or multi-valued columns.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;❌ Not in 1NF&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;student_id&lt;/th&gt;
&lt;th&gt;name&lt;/th&gt;
&lt;th&gt;phone_numbers&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Ishu&lt;/td&gt;
&lt;td&gt;9876, 8765&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Rahul&lt;/td&gt;
&lt;td&gt;7654, 6543&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Multiple phone numbers are crammed into one cell.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅ Fixed (1NF)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;students&lt;/code&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;student_id&lt;/th&gt;
&lt;th&gt;name&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Ishu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Rahul&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;student_phones&lt;/code&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;student_id (FK)&lt;/th&gt;
&lt;th&gt;phone_number&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;9876&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;8765&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;7654&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;6543&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Now every cell has exactly one value.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 &lt;strong&gt;1NF = one value per cell, no repeating groups.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  8.3 2️⃣ Second Normal Form (2NF)
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt; the table must be in 1NF, &lt;strong&gt;and&lt;/strong&gt; every non-key attribute must depend on the &lt;strong&gt;whole&lt;/strong&gt; candidate key, not just part of it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Two terms first:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Non-key attribute:&lt;/strong&gt; a column that isn't part of the candidate key&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Partial dependency:&lt;/strong&gt; a non-key attribute depends on only &lt;em&gt;part&lt;/em&gt; of a composite key&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;❌ Not in 2NF&lt;/strong&gt; — &lt;code&gt;enrollment&lt;/code&gt; with composite key &lt;code&gt;(student_id, course_id)&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;student_id&lt;/th&gt;
&lt;th&gt;course_id&lt;/th&gt;
&lt;th&gt;student_name&lt;/th&gt;
&lt;th&gt;course_name&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;Ishu&lt;/td&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;Ishu&lt;/td&gt;
&lt;td&gt;SQL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;Rahul&lt;/td&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;student_name&lt;/code&gt; depends only on &lt;code&gt;student_id&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;course_name&lt;/code&gt; depends only on &lt;code&gt;course_id&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Neither depends on the &lt;strong&gt;complete&lt;/strong&gt; key. That's a partial dependency, and it causes repeated names.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅ Fixed (2NF)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;students&lt;/code&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;student_id (PK)&lt;/th&gt;
&lt;th&gt;student_name&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Ishu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Rahul&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;courses&lt;/code&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;course_id (PK)&lt;/th&gt;
&lt;th&gt;course_name&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;SQL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;enrollments&lt;/code&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;student_id (PK, FK)&lt;/th&gt;
&lt;th&gt;course_id (PK, FK)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 &lt;strong&gt;2NF = 1NF + no partial dependency.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;💡 &lt;em&gt;Nuance:&lt;/em&gt; if a table's candidate key is a &lt;strong&gt;single column&lt;/strong&gt;, partial dependency is impossible.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  8.4 3️⃣ Third Normal Form (3NF)
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt; the table must be in 2NF, &lt;strong&gt;and&lt;/strong&gt; have no &lt;strong&gt;transitive dependency&lt;/strong&gt; of non-key attributes on the key.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A &lt;strong&gt;transitive dependency&lt;/strong&gt; means a non-key attribute depends on &lt;em&gt;another non-key attribute&lt;/em&gt; instead of directly on the key.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;❌ Not in 3NF&lt;/strong&gt; — &lt;code&gt;employees&lt;/code&gt; (PK: &lt;code&gt;employee_id&lt;/code&gt;):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;employee_id&lt;/th&gt;
&lt;th&gt;employee_name&lt;/th&gt;
&lt;th&gt;department_id&lt;/th&gt;
&lt;th&gt;department_name&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Ishu&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;Engineering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Rahul&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;Engineering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Priya&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;HR&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;employee_id   → employee_name
employee_id   → department_id
department_id → department_name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So &lt;code&gt;employee_id → department_id → department_name&lt;/code&gt;. The department name depends on the department, not directly on the employee. If "Engineering" becomes "Software Engineering", you must update many rows and might miss one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅ Fixed (3NF)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;employees&lt;/code&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;employee_id (PK)&lt;/th&gt;
&lt;th&gt;employee_name&lt;/th&gt;
&lt;th&gt;department_id (FK)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Ishu&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Rahul&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Priya&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;departments&lt;/code&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;department_id (PK)&lt;/th&gt;
&lt;th&gt;department_name&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;Engineering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;HR&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Now the department name is stored &lt;strong&gt;once&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 &lt;strong&gt;3NF = 2NF + no transitive dependency.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;📖 &lt;em&gt;Formal definition:&lt;/em&gt; for every non-trivial functional dependency X → Y, either X is a superkey, or Y is a prime attribute (part of some candidate key).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  8.5 🧬 Functional Dependency (FD)
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;functional dependency&lt;/strong&gt; means one attribute &lt;strong&gt;determines&lt;/strong&gt; another. It's written:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A → B        (read: "A determines B")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;employee_id   → employee_name
department_id → department_name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we know &lt;code&gt;employee_id&lt;/code&gt;, we can determine the &lt;code&gt;employee_name&lt;/code&gt; (assuming &lt;code&gt;employee_id&lt;/code&gt; is unique). FDs are the foundation for spotting partial and transitive dependencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  8.6 📊 1NF vs 2NF vs 3NF
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Normal form&lt;/th&gt;
&lt;th&gt;Main rule&lt;/th&gt;
&lt;th&gt;Problem eliminated&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;1NF&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Atomic values, no repeating groups&lt;/td&gt;
&lt;td&gt;Multi-valued cells&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;2NF&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No partial dependency&lt;/td&gt;
&lt;td&gt;Partial dependency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;3NF&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No transitive dependency of non-key attributes&lt;/td&gt;
&lt;td&gt;Transitive dependency&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Think of it as three stages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;1NF: organize the cells.&lt;/strong&gt; Store single values, not lists.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2NF: organize the dependencies.&lt;/strong&gt; Every non-key column depends on the &lt;em&gt;whole&lt;/em&gt; key.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3NF: remove indirect dependencies.&lt;/strong&gt; Each entity gets its own table.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  8.7 🛡️ Why normalization is a data-integrity principle
&lt;/h3&gt;

&lt;p&gt;Normalization isn't just about saving space. It's about storing &lt;strong&gt;each fact in exactly one appropriate place&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A username belongs to a &lt;strong&gt;user&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;A caption belongs to a &lt;strong&gt;post&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;A department name belongs to a &lt;strong&gt;department&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;A like belongs to a &lt;strong&gt;user-post combination&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When each fact lives in one place, inconsistent data becomes much harder to create.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚖️ &lt;strong&gt;Note:&lt;/strong&gt; normalization doesn't mean "split everything always". Real systems sometimes use &lt;strong&gt;controlled denormalization&lt;/strong&gt; for performance, but only when the trade-offs are understood.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  9️⃣ ACID Properties and Transactions ⚛️
&lt;/h2&gt;

&lt;p&gt;Imagine a banking app. You transfer &lt;strong&gt;₹1,000&lt;/strong&gt; to a friend. Two operations must happen:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Deduct ₹1,000 from &lt;strong&gt;your&lt;/strong&gt; account&lt;/li&gt;
&lt;li&gt;Add ₹1,000 to &lt;strong&gt;your friend's&lt;/strong&gt; account&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What if the first succeeds, but the app crashes before the second? 😱 Your money is gone and your friend never receives it.&lt;/p&gt;

&lt;p&gt;We need guarantees. That's what &lt;strong&gt;ACID&lt;/strong&gt; provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A&lt;/strong&gt;: Atomicity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C&lt;/strong&gt;: Consistency&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I&lt;/strong&gt;: Isolation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;D&lt;/strong&gt;: Durability&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  9.1 📦 What is a transaction?
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;transaction&lt;/strong&gt; is a group of database operations treated as &lt;strong&gt;one logical unit of work&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;accounts&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;account_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;accounts&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;account_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;COMMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;BEGIN&lt;/code&gt; → starts the transaction&lt;/li&gt;
&lt;li&gt;first &lt;code&gt;UPDATE&lt;/code&gt; → deducts money&lt;/li&gt;
&lt;li&gt;second &lt;code&gt;UPDATE&lt;/code&gt; → adds money&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;COMMIT&lt;/code&gt; → makes the changes &lt;strong&gt;permanent&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If something goes wrong:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ROLLBACK&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ROLLBACK&lt;/code&gt; cancels all uncommitted changes in the transaction.&lt;/p&gt;

&lt;h3&gt;
  
  
  9.2 ⚛️ A: Atomicity ("all or nothing")
&lt;/h3&gt;

&lt;p&gt;A transaction either &lt;strong&gt;fully completes&lt;/strong&gt; or has &lt;strong&gt;no effect at all&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✅ SUCCESS (both operations succeed)
   Account A: ₹5,000 → ₹4,000
   Account B: ₹2,000 → ₹3,000

❌ FAILURE (everything is rolled back)
   Account A: ₹5,000 → ₹5,000
   Account B: ₹2,000 → ₹2,000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We must &lt;strong&gt;never&lt;/strong&gt; end up with money deducted from A but not added to B.&lt;/p&gt;

&lt;h3&gt;
  
  
  9.3 ✅ C: Consistency ("valid state to valid state")
&lt;/h3&gt;

&lt;p&gt;A transaction must keep the database's &lt;strong&gt;defined integrity rules&lt;/strong&gt; intact. The database is valid before &lt;em&gt;and&lt;/em&gt; after.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;accounts&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;account_id&lt;/span&gt; &lt;span class="kt"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;balance&lt;/span&gt;    &lt;span class="kt"&gt;DECIMAL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&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="k"&gt;CHECK&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&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;p&gt;If account A has ₹500 and someone runs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;accounts&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;account_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the balance would become &lt;strong&gt;-₹500&lt;/strong&gt;, so the database &lt;strong&gt;rejects&lt;/strong&gt; it because it violates the &lt;code&gt;CHECK&lt;/code&gt; constraint.&lt;/p&gt;

&lt;p&gt;Consistency rules include primary key uniqueness, foreign key validity, &lt;code&gt;NOT NULL&lt;/code&gt;, &lt;code&gt;UNIQUE&lt;/code&gt; and &lt;code&gt;CHECK&lt;/code&gt; constraints.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ ACID consistency doesn't mean the database magically understands every business rule. &lt;strong&gt;Developers must define the constraints and transaction logic.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  9.4 🚦 I: Isolation ("transactions don't interfere incorrectly")
&lt;/h3&gt;

&lt;p&gt;Concurrent transactions should behave correctly without corrupting each other's intermediate work.&lt;/p&gt;

&lt;p&gt;Your balance is ₹5,000. Two requests arrive at nearly the same time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Transaction A withdraws ₹4,000&lt;/li&gt;
&lt;li&gt;Transaction B withdraws ₹4,000&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If both read ₹5,000 and both proceed, you could withdraw more than you have. That's a &lt;strong&gt;concurrency problem&lt;/strong&gt;, and isolation mechanisms control it.&lt;/p&gt;

&lt;p&gt;PostgreSQL supports these isolation levels:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Isolation level&lt;/th&gt;
&lt;th&gt;General protection&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;READ UNCOMMITTED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Lowest level; PostgreSQL treats it like Read Committed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;READ COMMITTED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Prevents dirty reads (PostgreSQL default)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;REPEATABLE READ&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Consistent snapshot for the whole transaction; prevents non-repeatable reads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SERIALIZABLE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Strongest; concurrent transactions behave as if run one after another&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Key terms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🧟 &lt;strong&gt;Dirty read:&lt;/strong&gt; reading another transaction's &lt;strong&gt;uncommitted&lt;/strong&gt; changes&lt;/li&gt;
&lt;li&gt;🔄 &lt;strong&gt;Non-repeatable read:&lt;/strong&gt; reading the same row twice and getting different committed values&lt;/li&gt;
&lt;li&gt;👻 &lt;strong&gt;Phantom read:&lt;/strong&gt; repeating a query and getting extra or missing rows because another transaction inserted/deleted matching rows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;PostgreSQL uses &lt;strong&gt;MVCC (Multi-Version Concurrency Control)&lt;/strong&gt;, which keeps multiple row versions so readers and writers don't block each other unnecessarily.&lt;/p&gt;

&lt;p&gt;Isolation matters most in banking, ticket booking and inventory systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  9.5 💾 D: Durability ("committed means persisted")
&lt;/h3&gt;

&lt;p&gt;Once a transaction is &lt;strong&gt;committed&lt;/strong&gt;, its changes survive crashes and restarts.&lt;/p&gt;

&lt;p&gt;You transfer ₹1,000, the database confirms the commit, and the server crashes right after. After recovery, the transfer must &lt;strong&gt;still be there&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;PostgreSQL supports durability through &lt;strong&gt;Write-Ahead Logging (WAL)&lt;/strong&gt;: changes are recorded in a log &lt;em&gt;before&lt;/em&gt; the corresponding data pages are necessarily written to their final location, so the database can recover after a crash.&lt;/p&gt;

&lt;h3&gt;
  
  
  9.6 🧾 ACID together
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Simple meaning&lt;/th&gt;
&lt;th&gt;Banking example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Atomicity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;All or nothing&lt;/td&gt;
&lt;td&gt;Both debit and credit happen&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Consistency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Rules remain valid&lt;/td&gt;
&lt;td&gt;Balance can't go negative&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Isolation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Concurrent operations are controlled&lt;/td&gt;
&lt;td&gt;Two withdrawals don't interfere incorrectly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Durability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Committed changes survive failures&lt;/td&gt;
&lt;td&gt;A successful transfer stays recorded&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Easy memory aid:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A&lt;/strong&gt;: &lt;strong&gt;A&lt;/strong&gt;ll or nothing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C&lt;/strong&gt;: &lt;strong&gt;C&lt;/strong&gt;orrect database state&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I&lt;/strong&gt;: &lt;strong&gt;I&lt;/strong&gt;ndependent execution&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;D&lt;/strong&gt;: &lt;strong&gt;D&lt;/strong&gt;ata stays committed&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🔟 Database Constraints 🚧
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;constraint&lt;/strong&gt; is a rule on a column or table that blocks invalid data.&lt;/p&gt;

&lt;p&gt;Imagine a table that allows this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;user_id&lt;/th&gt;
&lt;th&gt;email&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:ishu@gmail.com"&gt;ishu@gmail.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:rahul@gmail.com"&gt;rahul@gmail.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;NULL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Duplicate IDs and missing emails! Constraints prevent this.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Constraint&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PRIMARY KEY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unique row identifier, no NULL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;user_id INTEGER PRIMARY KEY&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FOREIGN KEY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Referenced record must exist&lt;/td&gt;
&lt;td&gt;&lt;code&gt;user_id INTEGER REFERENCES users(user_id)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UNIQUE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No duplicate values&lt;/td&gt;
&lt;td&gt;&lt;code&gt;email VARCHAR(100) UNIQUE&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NOT NULL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A value is required&lt;/td&gt;
&lt;td&gt;&lt;code&gt;username VARCHAR(50) NOT NULL&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CHECK&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enforces a condition&lt;/td&gt;
&lt;td&gt;&lt;code&gt;age INTEGER CHECK (age &amp;gt;= 18)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DEFAULT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Value used when none is given&lt;/td&gt;
&lt;td&gt;&lt;code&gt;is_active BOOLEAN DEFAULT TRUE&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt;   &lt;span class="kt"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;  &lt;span class="kt"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="kc"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;     &lt;span class="kt"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="kc"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;age&lt;/span&gt;       &lt;span class="kt"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;CHECK&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;is_active&lt;/span&gt; &lt;span class="kt"&gt;BOOLEAN&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="kc"&gt;TRUE&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;p&gt;This is how we enforce &lt;strong&gt;data quality at the database level&lt;/strong&gt;. 🛡️&lt;/p&gt;

&lt;h3&gt;
  
  
  🧹 Bonus: &lt;code&gt;ON DELETE&lt;/code&gt; behavior
&lt;/h3&gt;

&lt;p&gt;What happens to a user's posts when the user is deleted? You decide:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;post_id&lt;/span&gt; &lt;span class="kt"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="kt"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="kc"&gt;NULL&lt;/span&gt;
        &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="nf"&gt;users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;CASCADE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;caption&lt;/span&gt; &lt;span class="kt"&gt;TEXT&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;ul&gt;
&lt;li&gt;
&lt;code&gt;ON DELETE CASCADE&lt;/code&gt; → deleting a user also deletes their posts&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ON DELETE RESTRICT&lt;/code&gt; / &lt;code&gt;NO ACTION&lt;/code&gt; → block the delete while posts exist&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ON DELETE SET NULL&lt;/code&gt; → keep posts but clear &lt;code&gt;user_id&lt;/code&gt; (column must be nullable)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  1️⃣1️⃣ Putting it together: design an Instagram database 📸
&lt;/h2&gt;

&lt;p&gt;Your mentor says: &lt;em&gt;"Design the database for an Instagram-like app."&lt;/em&gt; How do you approach it?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: identify the entities&lt;/strong&gt; (real-world things we store data about):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;👤 User&lt;/li&gt;
&lt;li&gt;🖼️ Post&lt;/li&gt;
&lt;li&gt;💬 Comment&lt;/li&gt;
&lt;li&gt;❤️ Like&lt;/li&gt;
&lt;li&gt;👥 Follow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 2: create the tables.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. users&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Column&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Constraint&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;user_id&lt;/td&gt;
&lt;td&gt;INTEGER&lt;/td&gt;
&lt;td&gt;PK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;username&lt;/td&gt;
&lt;td&gt;VARCHAR&lt;/td&gt;
&lt;td&gt;UNIQUE, NOT NULL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;email&lt;/td&gt;
&lt;td&gt;VARCHAR&lt;/td&gt;
&lt;td&gt;UNIQUE, NOT NULL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;password_hash&lt;/td&gt;
&lt;td&gt;TEXT&lt;/td&gt;
&lt;td&gt;NOT NULL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;2. posts&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Column&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Constraint&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;post_id&lt;/td&gt;
&lt;td&gt;INTEGER&lt;/td&gt;
&lt;td&gt;PK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;user_id&lt;/td&gt;
&lt;td&gt;INTEGER&lt;/td&gt;
&lt;td&gt;FK, NOT NULL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;caption&lt;/td&gt;
&lt;td&gt;TEXT&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;created_at&lt;/td&gt;
&lt;td&gt;TIMESTAMP&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;3. comments&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Column&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Constraint&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;comment_id&lt;/td&gt;
&lt;td&gt;INTEGER&lt;/td&gt;
&lt;td&gt;PK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;post_id&lt;/td&gt;
&lt;td&gt;INTEGER&lt;/td&gt;
&lt;td&gt;FK, NOT NULL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;user_id&lt;/td&gt;
&lt;td&gt;INTEGER&lt;/td&gt;
&lt;td&gt;FK, NOT NULL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;content&lt;/td&gt;
&lt;td&gt;TEXT&lt;/td&gt;
&lt;td&gt;NOT NULL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;4. likes&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Column&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Constraint&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;user_id&lt;/td&gt;
&lt;td&gt;INTEGER&lt;/td&gt;
&lt;td&gt;PK, FK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;post_id&lt;/td&gt;
&lt;td&gt;INTEGER&lt;/td&gt;
&lt;td&gt;PK, FK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;created_at&lt;/td&gt;
&lt;td&gt;TIMESTAMP&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;5. follows&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Column&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Constraint&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;follower_id&lt;/td&gt;
&lt;td&gt;INTEGER&lt;/td&gt;
&lt;td&gt;PK, FK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;following_id&lt;/td&gt;
&lt;td&gt;INTEGER&lt;/td&gt;
&lt;td&gt;PK, FK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;created_at&lt;/td&gt;
&lt;td&gt;TIMESTAMP&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Notice that in &lt;code&gt;follows&lt;/code&gt;, &lt;strong&gt;both&lt;/strong&gt; &lt;code&gt;follower_id&lt;/code&gt; and &lt;code&gt;following_id&lt;/code&gt; reference the &lt;code&gt;users&lt;/code&gt; table. That's a &lt;strong&gt;self-referencing relationship&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;follower_id&lt;/th&gt;
&lt;th&gt;following_id&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;User 1 follows users 2 and 3&lt;/li&gt;
&lt;li&gt;User 2 follows user 1&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The composite primary key prevents following the same person twice, and we add a &lt;code&gt;CHECK&lt;/code&gt; so nobody follows themselves:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CHECK&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;follower_id&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;following_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1️⃣1️⃣.1 🗺️ How the tables relate
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;USERS (PK: user_id)
   │ 1 : N
   ▼
POSTS (FK: user_id)
   │ 1 : N
   ▼
COMMENTS (FK: post_id, user_id)

USERS ◄── N : N via LIKES (user_id + post_id) ──► POSTS

USERS ◄── self N : N via FOLLOWS (follower_id + following_id) ──► USERS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Users → Posts: &lt;strong&gt;one-to-many&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Posts → Comments: &lt;strong&gt;one-to-many&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Users → Comments: &lt;strong&gt;one-to-many&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Posts → Likes: &lt;strong&gt;one-to-many&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Users ↔ Posts through Likes: &lt;strong&gt;many-to-many&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Users ↔ Users through Follows: &lt;strong&gt;self-referencing many-to-many&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  1️⃣1️⃣.2 🧪 Real SQL queries
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Display a post with its creator:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
&lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Count posts per user (including users with zero posts):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;total_posts&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🤔 &lt;em&gt;Why &lt;code&gt;LEFT JOIN&lt;/code&gt;?&lt;/em&gt; To include users with no posts.&lt;br&gt;
🤔 &lt;em&gt;Why &lt;code&gt;COUNT(posts.post_id)&lt;/code&gt; and not `COUNT(&lt;/em&gt;)&lt;code&gt;?* For a user with no posts, LEFT JOIN still creates a row with NULL post columns. &lt;/code&gt;COUNT(*)&lt;code&gt; would count that row as &lt;/code&gt;1&lt;code&gt;, but &lt;/code&gt;COUNT(posts.post_id)&lt;code&gt; correctly returns &lt;/code&gt;0`.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Top 5 most-liked posts:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;likes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;total_likes&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;likes&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;likes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post_id&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;caption&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;total_likes&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Start with posts&lt;/li&gt;
&lt;li&gt;Join likes&lt;/li&gt;
&lt;li&gt;Group likes by post&lt;/li&gt;
&lt;li&gt;Count likes per post&lt;/li&gt;
&lt;li&gt;Sort by like count&lt;/li&gt;
&lt;li&gt;Return the top five 🏆&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Who does user 1 follow? (bonus)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;follows&lt;/span&gt;
&lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;follows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;following_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;follows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;follower_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the kind of query you'll meet in real backend apps.&lt;/p&gt;




&lt;h2&gt;
  
  
  1️⃣2️⃣ How this connects to SQLAlchemy and FastAPI 🔌
&lt;/h2&gt;

&lt;p&gt;You might wonder: &lt;em&gt;"If SQL talks to PostgreSQL, why learn SQLAlchemy?"&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SQL&lt;/strong&gt; → the language for querying relational databases&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PostgreSQL&lt;/strong&gt; → the database system that stores and manages the data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQLAlchemy&lt;/strong&gt; → a Python library that lets us work with databases using Python objects and SQL expressions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The SQL table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt;  &lt;span class="kt"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="kt"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&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;p&gt;becomes this SQLAlchemy 2.0 ORM model (simplified):&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;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;orm&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;DeclarativeBase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mapped_column&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DeclarativeBase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;)&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="s2"&gt;"users"&lt;/span&gt;

    &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&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="nc"&gt;Integer&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="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&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;50&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;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;SQL concept&lt;/th&gt;
&lt;th&gt;SQLAlchemy equivalent&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Table&lt;/td&gt;
&lt;td&gt;ORM model (a class)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Column&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mapped_column&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Primary Key&lt;/td&gt;
&lt;td&gt;&lt;code&gt;primary_key=True&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Foreign Key&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ForeignKey()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UNIQUE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;unique=True&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NOT NULL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nullable=False&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Relationship&lt;/td&gt;
&lt;td&gt;&lt;code&gt;relationship()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;INSERT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;session.add()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UPDATE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Modify object attributes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DELETE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;session.delete()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;COMMIT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;session.commit()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ROLLBACK&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;session.rollback()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Creating a user (with an async session):&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;new_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ishu"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&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;ul&gt;
&lt;li&gt;
&lt;code&gt;session.add()&lt;/code&gt; registers the object with the session&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;session.commit()&lt;/code&gt; commits the transaction (that's ACID in action! ⚛️)&lt;/li&gt;
&lt;li&gt;SQLAlchemy generates and runs the SQL for you&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 SQLAlchemy &lt;strong&gt;doesn't replace SQL&lt;/strong&gt;. It's a Pythonic way to work with relational databases. Understand the underlying SQL, because SQLAlchemy still talks to the database using SQL.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  🔄 How a request flows through your backend
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;React frontend       → user clicks "Create Post"
      ▼
FastAPI endpoint     → receives and validates the request
      ▼
Service layer        → applies business logic
      ▼
Repository layer     → runs database operations via SQLAlchemy
      ▼
PostgreSQL           → stores the row in the posts table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's why relational database fundamentals come &lt;strong&gt;before&lt;/strong&gt; SQLAlchemy and Alembic. 🎯&lt;/p&gt;




&lt;h2&gt;
  
  
  1️⃣3️⃣ Common interview questions 🎤
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q1. Primary Key vs Foreign Key?&lt;/strong&gt;&lt;br&gt;
A primary key uniquely identifies a row in its own table. A foreign key references a key in another table and maintains referential integrity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q2. Can a table have multiple primary keys?&lt;/strong&gt;&lt;br&gt;
Only one &lt;code&gt;PRIMARY KEY&lt;/code&gt; constraint, but it can span multiple columns (a composite primary key).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q3. Can a foreign key contain NULL?&lt;/strong&gt;&lt;br&gt;
Yes, unless the column is &lt;code&gt;NOT NULL&lt;/code&gt;. A NULL foreign key means no related record is specified.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q4. DELETE vs TRUNCATE vs DROP?&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;DELETE&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;TRUNCATE&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;DROP&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Removes selected rows&lt;/td&gt;
&lt;td&gt;Removes all rows&lt;/td&gt;
&lt;td&gt;Removes the whole table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Supports &lt;code&gt;WHERE&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;No &lt;code&gt;WHERE&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Removes structure too&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Table remains&lt;/td&gt;
&lt;td&gt;Table remains&lt;/td&gt;
&lt;td&gt;Table is gone&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Q5. WHERE vs HAVING?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;WHERE&lt;/code&gt; filters rows before grouping. &lt;code&gt;HAVING&lt;/code&gt; filters groups after grouping.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q6. Why do we need normalization?&lt;/strong&gt;&lt;br&gt;
To reduce redundancy, prevent insertion/update/deletion anomalies and improve data integrity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q7. What is a junction table?&lt;/strong&gt;&lt;br&gt;
A table implementing a many-to-many relationship using foreign keys to the two related tables.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q8. 2NF vs 3NF?&lt;/strong&gt;&lt;br&gt;
2NF removes partial dependencies. 3NF removes transitive dependencies of non-key attributes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q9. What happens when a transaction fails?&lt;/strong&gt;&lt;br&gt;
Uncommitted changes can be rolled back, preventing partial application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q10. Atomicity vs Durability?&lt;/strong&gt;&lt;br&gt;
Atomicity: all operations succeed together or none do. Durability: once committed, changes survive failures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q11. What is referential integrity?&lt;/strong&gt;&lt;br&gt;
A guarantee that foreign key values always point to existing rows (or are NULL).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q12. INNER JOIN vs LEFT JOIN?&lt;/strong&gt;&lt;br&gt;
INNER JOIN returns only matching rows. LEFT JOIN returns all left rows plus matches, with NULLs where there's no match.&lt;/p&gt;




&lt;h2&gt;
  
  
  1️⃣4️⃣ Practice quiz 🧪
&lt;/h2&gt;

&lt;p&gt;Answer without scrolling up! (Answers are at the bottom.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Which key uniquely identifies each row in a table?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Foreign Key&lt;/li&gt;
&lt;li&gt;B) Primary Key&lt;/li&gt;
&lt;li&gt;C) Alternate Table&lt;/li&gt;
&lt;li&gt;D) Index&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. One user creates multiple posts. What relationship is this?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) One-to-One&lt;/li&gt;
&lt;li&gt;B) One-to-Many&lt;/li&gt;
&lt;li&gt;C) Many-to-Many&lt;/li&gt;
&lt;li&gt;D) None&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Which JOIN returns all records from the left table?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) INNER JOIN&lt;/li&gt;
&lt;li&gt;B) RIGHT JOIN&lt;/li&gt;
&lt;li&gt;C) LEFT JOIN&lt;/li&gt;
&lt;li&gt;D) CROSS JOIN&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Which normal form eliminates partial dependency?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) 1NF&lt;/li&gt;
&lt;li&gt;B) 2NF&lt;/li&gt;
&lt;li&gt;C) 3NF&lt;/li&gt;
&lt;li&gt;D) None&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;5. Which normal form eliminates transitive dependencies of non-key attributes?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) 1NF&lt;/li&gt;
&lt;li&gt;B) 2NF&lt;/li&gt;
&lt;li&gt;C) 3NF&lt;/li&gt;
&lt;li&gt;D) BCNF only&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;6. Which SQL clause filters aggregated groups?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) WHERE&lt;/li&gt;
&lt;li&gt;B) ORDER BY&lt;/li&gt;
&lt;li&gt;C) HAVING&lt;/li&gt;
&lt;li&gt;D) LIMIT&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;7. Which ACID property means all operations happen or none happen?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Consistency&lt;/li&gt;
&lt;li&gt;B) Atomicity&lt;/li&gt;
&lt;li&gt;C) Isolation&lt;/li&gt;
&lt;li&gt;D) Durability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;8. What does COMMIT do?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Deletes a table&lt;/li&gt;
&lt;li&gt;B) Starts a transaction&lt;/li&gt;
&lt;li&gt;C) Makes transaction changes permanent&lt;/li&gt;
&lt;li&gt;D) Reads records&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;9. What implements a many-to-many relationship?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Only a Primary Key&lt;/li&gt;
&lt;li&gt;B) A junction table&lt;/li&gt;
&lt;li&gt;C) A single text column&lt;/li&gt;
&lt;li&gt;D) DROP TABLE&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;10. Why do we use a Foreign Key?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) To sort records&lt;/li&gt;
&lt;li&gt;B) To maintain referential integrity&lt;/li&gt;
&lt;li&gt;C) To encrypt passwords&lt;/li&gt;
&lt;li&gt;D) To create indexes automatically in every database&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👀 Click to reveal answers&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;B&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;B&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;C&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;B&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;C&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;C&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;B&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;C&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;B&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;B&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  🧾 Final revision sheet
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;What to remember&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Relational model&lt;/td&gt;
&lt;td&gt;Data organized into related tables&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Table&lt;/td&gt;
&lt;td&gt;Collection of related records&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Row&lt;/td&gt;
&lt;td&gt;One record&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Column&lt;/td&gt;
&lt;td&gt;One attribute&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Primary Key&lt;/td&gt;
&lt;td&gt;Unique row identifier&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Foreign Key&lt;/td&gt;
&lt;td&gt;References another table's key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1:1&lt;/td&gt;
&lt;td&gt;One record relates to at most one other (FK + UNIQUE)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1:N&lt;/td&gt;
&lt;td&gt;One record relates to many (FK on the many side)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;N:N&lt;/td&gt;
&lt;td&gt;Many relate to many through a junction table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;INNER JOIN&lt;/td&gt;
&lt;td&gt;Only matching rows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LEFT JOIN&lt;/td&gt;
&lt;td&gt;All left rows + matching right rows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RIGHT JOIN&lt;/td&gt;
&lt;td&gt;All right rows + matching left rows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FULL JOIN&lt;/td&gt;
&lt;td&gt;All rows from both sides&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GROUP BY&lt;/td&gt;
&lt;td&gt;Groups similar records&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HAVING&lt;/td&gt;
&lt;td&gt;Filters grouped results&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Normalization&lt;/td&gt;
&lt;td&gt;Organizes data and reduces anomalies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1NF&lt;/td&gt;
&lt;td&gt;Atomic values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2NF&lt;/td&gt;
&lt;td&gt;No partial dependency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3NF&lt;/td&gt;
&lt;td&gt;No transitive dependency of non-key attributes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Atomicity&lt;/td&gt;
&lt;td&gt;All or nothing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consistency&lt;/td&gt;
&lt;td&gt;Preserves database rules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Isolation&lt;/td&gt;
&lt;td&gt;Controls concurrent transactions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Durability&lt;/td&gt;
&lt;td&gt;Committed changes persist&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🚀 What should you learn next?
&lt;/h2&gt;

&lt;p&gt;You now have the conceptual foundation of relational database development. Learn in this order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;SQL CRUD operations and filtering&lt;/li&gt;
&lt;li&gt;Joins and subqueries&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GROUP BY&lt;/code&gt;, &lt;code&gt;HAVING&lt;/code&gt; and aggregate queries&lt;/li&gt;
&lt;li&gt;Constraints and indexes&lt;/li&gt;
&lt;li&gt;Transactions and isolation levels&lt;/li&gt;
&lt;li&gt;SQLAlchemy Core and ORM&lt;/li&gt;
&lt;li&gt;Alembic migrations&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;My biggest advice:&lt;/strong&gt; don't just memorize definitions of INNER JOIN, 1NF, 2NF, 3NF and ACID. &lt;strong&gt;Create real tables, insert rows, join them and watch what happens.&lt;/strong&gt; That's when it all clicks. ❤️&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  🙌 Enjoyed this post?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;❤️ Drop a reaction&lt;/li&gt;
&lt;li&gt;🦄 Save it for revision&lt;/li&gt;
&lt;li&gt;💬 Comment with questions, and I'll answer them!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Follow me so you don't miss it! 🔔&lt;br&gt;
Happy coding! 👩‍💻✨&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>sql</category>
      <category>backend</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Authentication Basics (JWT) &amp; FastAPI Security: A Beginner-Friendly Guide 🔐</title>
      <dc:creator>Deepika Pusala</dc:creator>
      <pubDate>Wed, 11 Nov 2026 07:23:56 +0000</pubDate>
      <link>https://dev.to/deepika_pusala/authentication-basics-jwt-fastapi-security-a-beginner-friendly-guide-2nlc</link>
      <guid>https://dev.to/deepika_pusala/authentication-basics-jwt-fastapi-security-a-beginner-friendly-guide-2nlc</guid>
      <description>&lt;h1&gt;
  
  
  🔐 Authentication Basics (JWT) &amp;amp; FastAPI Security
&lt;/h1&gt;

&lt;p&gt;Authentication is &lt;strong&gt;not&lt;/strong&gt; just a login page. 🙅‍♀️&lt;/p&gt;

&lt;p&gt;It is about understanding how a backend &lt;strong&gt;identifies a user, verifies their identity, issues a token, and protects private API endpoints&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this guide we'll learn everything from absolute zero:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ How JWT works internally&lt;/li&gt;
&lt;li&gt;✅ What &lt;code&gt;OAuth2PasswordBearer&lt;/code&gt; &lt;em&gt;actually&lt;/em&gt; does&lt;/li&gt;
&lt;li&gt;✅ Why password hashing is necessary&lt;/li&gt;
&lt;li&gt;✅ How &lt;code&gt;Depends()&lt;/code&gt; makes authentication reusable&lt;/li&gt;
&lt;li&gt;✅ How all these pieces connect in a real &lt;strong&gt;React + FastAPI + PostgreSQL&lt;/strong&gt; project (my Social Feed app)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Simple examples, real-world analogies, Python code and execution flows. &lt;strong&gt;No assumed knowledge.&lt;/strong&gt; Let's go! 🚀&lt;/p&gt;




&lt;h2&gt;
  
  
  📚 Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Authentication vs Authorization&lt;/li&gt;
&lt;li&gt;Why do we need authentication?&lt;/li&gt;
&lt;li&gt;Token-based authentication&lt;/li&gt;
&lt;li&gt;What is JWT?&lt;/li&gt;
&lt;li&gt;JWT issuing and verification&lt;/li&gt;
&lt;li&gt;PyJWT and python-jose&lt;/li&gt;
&lt;li&gt;What is OAuth2?&lt;/li&gt;
&lt;li&gt;What is &lt;code&gt;OAuth2PasswordBearer&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;What is &lt;code&gt;Depends()&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;Password hashing (and why plaintext is a crime)&lt;/li&gt;
&lt;li&gt;Passlib, bcrypt and Argon2id&lt;/li&gt;
&lt;li&gt;Building the complete FastAPI auth system&lt;/li&gt;
&lt;li&gt;Full execution flow&lt;/li&gt;
&lt;li&gt;Expiry, access tokens, refresh tokens and logout&lt;/li&gt;
&lt;li&gt;JWT vs session-based authentication&lt;/li&gt;
&lt;li&gt;HTTP status codes&lt;/li&gt;
&lt;li&gt;Common security mistakes&lt;/li&gt;
&lt;li&gt;Architecture of a real FastAPI project&lt;/li&gt;
&lt;li&gt;Interview questions&lt;/li&gt;
&lt;li&gt;Quick quiz and final revision&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1️⃣ Authentication vs Authorization
&lt;/h2&gt;

&lt;p&gt;These two words sound similar, but they mean &lt;strong&gt;different things&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Imagine Instagram 📸. You open it and enter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Email: &lt;code&gt;deepika@gmail.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Password: &lt;code&gt;mypassword123&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instagram checks whether these credentials belong to a registered user. &lt;strong&gt;That's Authentication.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now you're logged in and you try to edit &lt;em&gt;someone else's&lt;/em&gt; profile. Instagram checks whether you have &lt;strong&gt;permission&lt;/strong&gt; to do that. &lt;strong&gt;That's Authorization.&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🪪 Authentication&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Who are you?&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Verify your login&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🚦 Authorization&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;What can you do?&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Can you delete this post?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 &lt;strong&gt;Remember:&lt;/strong&gt; Authentication = identity verification. Authorization = permission checking. Authentication usually happens &lt;strong&gt;first&lt;/strong&gt;, authorization follows.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  2️⃣ Why do we need authentication?
&lt;/h2&gt;

&lt;p&gt;Suppose you build a social media app using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⚛️ React frontend&lt;/li&gt;
&lt;li&gt;⚡ FastAPI backend&lt;/li&gt;
&lt;li&gt;🐘 PostgreSQL database&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your API has these endpoints:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="no"&gt;GET&lt;/span&gt;    &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;feed&lt;/span&gt;
&lt;span class="no"&gt;POST&lt;/span&gt;   &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt;
&lt;span class="no"&gt;DELETE&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;
&lt;span class="no"&gt;GET&lt;/span&gt;    &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What if &lt;strong&gt;anyone&lt;/strong&gt; can call &lt;code&gt;DELETE /posts/15&lt;/code&gt;? 😱 Anyone could delete anyone's post!&lt;/p&gt;

&lt;p&gt;The backend needs a way to answer:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Who is making this request?&lt;/li&gt;
&lt;li&gt;Are they logged in?&lt;/li&gt;
&lt;li&gt;Is their identity valid?&lt;/li&gt;
&lt;li&gt;Are they allowed to do this operation?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's exactly what authentication (and authorization) solves.&lt;/p&gt;




&lt;h2&gt;
  
  
  3️⃣ What is token-based authentication?
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;token&lt;/strong&gt; is a piece of data that represents a user's authenticated session or identity.&lt;/p&gt;

&lt;p&gt;Think of a &lt;strong&gt;movie theatre&lt;/strong&gt; 🎬:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You buy a ticket.&lt;/li&gt;
&lt;li&gt;The ticket is given to you.&lt;/li&gt;
&lt;li&gt;You show it at the entrance.&lt;/li&gt;
&lt;li&gt;Staff verifies it.&lt;/li&gt;
&lt;li&gt;You're allowed inside.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You don't buy a new ticket for every section of the theatre. Same idea here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User enters email and password.&lt;/li&gt;
&lt;li&gt;Backend verifies the credentials.&lt;/li&gt;
&lt;li&gt;Backend generates a token.&lt;/li&gt;
&lt;li&gt;Frontend stores the token.&lt;/li&gt;
&lt;li&gt;Frontend sends the token with every later request.&lt;/li&gt;
&lt;li&gt;Backend verifies the token before allowing access.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After a successful login the backend returns something like:&lt;br&gt;
&lt;/p&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="nl"&gt;"access_token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"eyJhbGciOiJIUzI1NiJ9.example.signature"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"token_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"bearer"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the frontend sends it back on future requests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="no"&gt;GET&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;feed&lt;/span&gt;
&lt;span class="nc"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Bearer&lt;/span&gt; &lt;span class="n"&gt;eyJhbGciOiJIUzI1NiJ9&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;signature&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.1 🏷️ What is the &lt;code&gt;Authorization&lt;/code&gt; header?
&lt;/h3&gt;

&lt;p&gt;HTTP requests can carry &lt;strong&gt;headers&lt;/strong&gt;, extra information about the request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="no"&gt;GET&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt;
&lt;span class="nc"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Bearer&lt;/span&gt; &lt;span class="n"&gt;abc123&lt;/span&gt;
&lt;span class="nc"&gt;Content&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nc"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Authorization&lt;/code&gt; → the header name&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Bearer&lt;/code&gt; → tells the server "I'm giving you a bearer token"&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;abc123&lt;/code&gt; → the token itself&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A &lt;strong&gt;bearer token&lt;/strong&gt; basically means: &lt;em&gt;whoever holds this valid token can use it.&lt;/em&gt; 🎟️&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ That's why tokens must be protected. If someone steals your token, they may be able to act as &lt;strong&gt;you&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3.2 🔄 The complete authentication flow
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;User logs in:&lt;/strong&gt; React sends email and password to &lt;code&gt;POST /login&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend verifies credentials:&lt;/strong&gt; FastAPI fetches the user from PostgreSQL and checks the password hash.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend creates a JWT:&lt;/strong&gt; it creates and &lt;em&gt;signs&lt;/em&gt; a token containing claims.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frontend receives the token:&lt;/strong&gt; the response contains &lt;code&gt;access_token&lt;/code&gt; and &lt;code&gt;token_type&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frontend makes another request:&lt;/strong&gt; React sends &lt;code&gt;Authorization: Bearer &amp;lt;token&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FastAPI verifies the token:&lt;/strong&gt; it checks the signature, expiry and claims.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Protected endpoint runs:&lt;/strong&gt; if valid, the backend identifies the user and continues.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now let's understand JWT itself. 👇&lt;/p&gt;




&lt;h2&gt;
  
  
  4️⃣ What is JWT (JSON Web Token)?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;JWT&lt;/strong&gt; stands for &lt;strong&gt;JSON Web Token&lt;/strong&gt;. It's a compact format for transferring information between parties as a JSON-based, signed token.&lt;/p&gt;

&lt;p&gt;A JWT looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;xxxxx.yyyyy.zzzzz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three parts separated by dots:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HEADER . PAYLOAD . SIGNATURE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;Contains&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🧾 Header&lt;/td&gt;
&lt;td&gt;Algorithm and token type&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;📦 Payload&lt;/td&gt;
&lt;td&gt;User identity and token claims&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;✍️ Signature&lt;/td&gt;
&lt;td&gt;Cryptographic proof of integrity&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  4.1 🧾 Header
&lt;/h3&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="nl"&gt;"alg"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"HS256"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"typ"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"JWT"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;alg&lt;/code&gt; → algorithm used to sign the token&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HS256&lt;/code&gt; → HMAC using SHA-256&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;typ&lt;/code&gt; → token type (JWT)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4.2 📦 Payload (claims)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Claims&lt;/strong&gt; are statements about the user or the token.&lt;br&gt;
&lt;/p&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="nl"&gt;"sub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"101"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"iat"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1794398200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"exp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1794400000&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Claim&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sub&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Subject, usually the user identifier&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;exp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Expiration time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;iat&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Issued-at time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;iss&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Issuer of the token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aud&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Intended audience&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;jti&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unique token identifier&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are standard &lt;em&gt;registered claims&lt;/em&gt;. You can also add custom claims like &lt;code&gt;role&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🚨 &lt;strong&gt;Very important:&lt;/strong&gt; a JWT payload is &lt;strong&gt;NOT encrypted&lt;/strong&gt; by default. Anyone holding the token can decode the header and payload!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So &lt;strong&gt;NEVER&lt;/strong&gt; put these inside a JWT:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ Passwords&lt;/li&gt;
&lt;li&gt;❌ Password hashes&lt;/li&gt;
&lt;li&gt;❌ Secret API keys&lt;/li&gt;
&lt;li&gt;❌ Sensitive personal information&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A signed JWT gives you &lt;strong&gt;integrity&lt;/strong&gt;, not &lt;strong&gt;confidentiality&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.3 ✍️ Signature (the security-critical part)
&lt;/h3&gt;

&lt;p&gt;The backend signs the token using a secret key. Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Signature = HMAC-SHA256(
    encoded_header + "." + encoded_payload,
    SECRET_KEY
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Imagine an attacker changes the payload from &lt;code&gt;"sub": "user_101"&lt;/code&gt; to &lt;code&gt;"sub": "admin_999"&lt;/code&gt;. Without the secret key they &lt;strong&gt;cannot&lt;/strong&gt; produce a matching signature, so verification &lt;strong&gt;fails&lt;/strong&gt;. 🛡️&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Encoding vs signing vs encryption:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📝 &lt;strong&gt;Encoding&lt;/strong&gt; → makes data representable in a transport format&lt;/li&gt;
&lt;li&gt;✍️ &lt;strong&gt;Signing&lt;/strong&gt; → verifies integrity and authenticity&lt;/li&gt;
&lt;li&gt;🔒 &lt;strong&gt;Encryption&lt;/strong&gt; → hides data from unauthorized readers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;JWTs using HS256 are &lt;strong&gt;signed, not encrypted&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  5️⃣ JWT issuing and verification
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🏭 Issuing
&lt;/h3&gt;

&lt;p&gt;Creating and signing a JWT, usually right after a successful login.&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;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_access_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function receives the identity, builds the payload, adds expiry, signs it and returns the JWT string.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔍 Verification
&lt;/h3&gt;

&lt;p&gt;Happens whenever a client sends the token to a protected API:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Extract the token&lt;/li&gt;
&lt;li&gt;Verify the signature&lt;/li&gt;
&lt;li&gt;Check the expiration&lt;/li&gt;
&lt;li&gt;Read the user ID from the payload&lt;/li&gt;
&lt;li&gt;Identify the corresponding user&lt;/li&gt;
&lt;li&gt;Allow or reject the request&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A token can be rejected because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ Invalid signature&lt;/li&gt;
&lt;li&gt;❌ Expired&lt;/li&gt;
&lt;li&gt;❌ Malformed structure&lt;/li&gt;
&lt;li&gt;❌ Required claims missing&lt;/li&gt;
&lt;li&gt;❌ The user or session is no longer valid&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 A valid signature does &lt;strong&gt;not&lt;/strong&gt; automatically mean the account is still active. You may also need to check the user or session in your database.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  6️⃣ PyJWT and python-jose
&lt;/h2&gt;

&lt;p&gt;You never implement signing algorithms by hand. You use a library. 📦&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Library&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PyJWT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create, encode, decode and verify JWTs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;python-jose&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create, encode, decode and verify JWTs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Both work with FastAPI. Older tutorials (and older FastAPI docs) use &lt;code&gt;python-jose&lt;/code&gt;, while the more actively maintained &lt;strong&gt;PyJWT&lt;/strong&gt; is what the current FastAPI documentation uses. I'll use PyJWT in this article:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip install pyjwt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you'd rather use python-jose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip install &lt;span class="s2"&gt;"python-jose[cryptography]"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(The function names &lt;code&gt;encode&lt;/code&gt; / &lt;code&gt;decode&lt;/code&gt; are almost identical, so everything below translates easily.)&lt;/p&gt;

&lt;h3&gt;
  
  
  6.1 🏭 Creating a JWT
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;jwt&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timezone&lt;/span&gt;

&lt;span class="no"&gt;SECRET_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-super-secret-key"&lt;/span&gt;   &lt;span class="c1"&gt;# ⚠️ demo only!&lt;/span&gt;
&lt;span class="no"&gt;ALGORITHM&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"HS256"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_access_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;expire&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;minutes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"sub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"exp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;expire&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;algorithm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="no"&gt;ALGORITHM&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Line-by-line:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;SECRET_KEY&lt;/code&gt;&lt;/strong&gt;: the secret used to sign the JWT. In real apps, never hardcode it. Load it from an environment variable. Make it strong, random and private. 🔑&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ALGORITHM = "HS256"&lt;/code&gt;&lt;/strong&gt;: HMAC SHA-256. The same secret signs and verifies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;expire&lt;/code&gt;&lt;/strong&gt;: the token dies 30 minutes from now. If a token is stolen but expires soon, the damage window is small. ⏳&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;"sub": str(user_id)&lt;/code&gt;&lt;/strong&gt;: by convention &lt;code&gt;sub&lt;/code&gt; is a &lt;strong&gt;string&lt;/strong&gt;, so we convert the ID.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;jwt.encode(...)&lt;/code&gt;&lt;/strong&gt;: encodes the header and payload, creates the signature, joins the three parts and returns the JWT string. ✨&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's JWT issuing!&lt;/p&gt;

&lt;h3&gt;
  
  
  6.2 🔍 Verifying a JWT
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;jwt&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;jwt&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;InvalidTokenError&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;verify_access_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="no"&gt;SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;algorithms&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;ALGORITHM&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# 👈 explicit allow-list!&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nc"&gt;InvalidTokenError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;

    &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"sub"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What &lt;code&gt;jwt.decode()&lt;/code&gt; does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Decodes the token&lt;/li&gt;
&lt;li&gt;Checks the signature&lt;/li&gt;
&lt;li&gt;Validates &lt;code&gt;exp&lt;/code&gt; (it raises an error if the token has expired)&lt;/li&gt;
&lt;li&gt;Rejects invalid tokens&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Notice &lt;code&gt;algorithms=[ALGORITHM]&lt;/code&gt;. &lt;strong&gt;Always&lt;/strong&gt; specify the algorithms you accept. Never blindly trust the algorithm declared by an incoming token.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  7️⃣ What is OAuth2?
&lt;/h2&gt;

&lt;p&gt;You might be thinking: &lt;em&gt;"We learned JWT, why OAuth2? Aren't they the same?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOOO!&lt;/strong&gt; 🙅 They're different things.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OAuth2&lt;/strong&gt; → an &lt;em&gt;authorization framework&lt;/em&gt;. It describes how authorization is granted and how tokens are obtained.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JWT&lt;/strong&gt; → a &lt;em&gt;token format&lt;/em&gt;. It describes how a token can be represented.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;OAuth2 doesn't require JWT. OAuth2 access tokens can also be opaque random strings.&lt;/p&gt;

&lt;h3&gt;
  
  
  7.1 🌍 Real-world example
&lt;/h3&gt;

&lt;p&gt;You see a &lt;strong&gt;"Continue with Google"&lt;/strong&gt; button and click it. Google authenticates you and your app receives authorization information through an OAuth-based flow. You never give your Google password to the third-party app. 🎉&lt;/p&gt;

&lt;p&gt;One nuance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OAuth2 primarily handles &lt;strong&gt;authorization&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenID Connect (OIDC)&lt;/strong&gt;, built on top of OAuth2, adds an identity layer for &lt;strong&gt;authentication&lt;/strong&gt;. Google Sign-In generally uses OIDC.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  7.2 ⚡ OAuth2 in FastAPI
&lt;/h3&gt;

&lt;p&gt;FastAPI ships security utilities for common OAuth2-compatible patterns. The most important one is &lt;code&gt;OAuth2PasswordBearer&lt;/code&gt;. 👇&lt;/p&gt;




&lt;h2&gt;
  
  
  8️⃣ What is &lt;code&gt;OAuth2PasswordBearer&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;OAuth2PasswordBearer&lt;/code&gt; is a &lt;strong&gt;FastAPI security dependency&lt;/strong&gt;. Its job:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;📍 Tell FastAPI where the token comes from&lt;/li&gt;
&lt;li&gt;📤 Extract the bearer token from the &lt;code&gt;Authorization&lt;/code&gt; header&lt;/li&gt;
&lt;li&gt;📥 Hand that token to your dependency or endpoint&lt;/li&gt;
&lt;li&gt;📖 Describe the OAuth2 security scheme in the OpenAPI docs (that green &lt;strong&gt;Authorize&lt;/strong&gt; button in Swagger UI!)
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&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;security&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;OAuth2PasswordBearer&lt;/span&gt;

&lt;span class="n"&gt;oauth2_scheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OAuth2PasswordBearer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokenUrl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"login"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What does &lt;code&gt;tokenUrl="login"&lt;/code&gt; mean?
&lt;/h3&gt;

&lt;p&gt;It tells FastAPI (and the docs UI) that the endpoint used to &lt;em&gt;obtain&lt;/em&gt; a token is &lt;code&gt;POST /login&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What does it actually do?
&lt;/h3&gt;

&lt;p&gt;Given this request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="no"&gt;GET&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt;
&lt;span class="nc"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Bearer&lt;/span&gt; &lt;span class="n"&gt;my&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;it extracts &lt;code&gt;my-secret-token&lt;/code&gt; (it strips the &lt;code&gt;Bearer&lt;/code&gt; prefix).&lt;/p&gt;

&lt;p&gt;It does &lt;strong&gt;NOT&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ Generate the JWT&lt;/li&gt;
&lt;li&gt;❌ Verify the JWT signature&lt;/li&gt;
&lt;li&gt;❌ Check the user's password&lt;/li&gt;
&lt;li&gt;❌ Fetch the user from PostgreSQL&lt;/li&gt;
&lt;li&gt;❌ Log the user in&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 &lt;strong&gt;&lt;code&gt;OAuth2PasswordBearer&lt;/code&gt; only extracts the token. You still have to verify it yourself.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  8.1 What if the token is missing?
&lt;/h3&gt;

&lt;p&gt;If a request has no &lt;code&gt;Authorization&lt;/code&gt; header, &lt;code&gt;OAuth2PasswordBearer&lt;/code&gt; automatically responds with &lt;strong&gt;HTTP 401 Unauthorized&lt;/strong&gt;. 🚫&lt;/p&gt;




&lt;h2&gt;
  
  
  9️⃣ What is &lt;code&gt;Depends()&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;Before building the full system we need &lt;strong&gt;dependency injection&lt;/strong&gt;, another core FastAPI concept.&lt;/p&gt;

&lt;p&gt;Imagine ten protected endpoints:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="no"&gt;GET&lt;/span&gt;    &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt;
&lt;span class="no"&gt;GET&lt;/span&gt;    &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;feed&lt;/span&gt;
&lt;span class="no"&gt;POST&lt;/span&gt;   &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt;
&lt;span class="no"&gt;DELETE&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="no"&gt;POST&lt;/span&gt;   &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;comments&lt;/span&gt;
&lt;span class="no"&gt;GET&lt;/span&gt;    &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;notifications&lt;/span&gt;
&lt;span class="p"&gt;.&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;p&gt;Every one needs authentication. Would you copy-paste the same token-checking code ten times? 😵 No way. FastAPI gives us:&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="nc"&gt;Depends&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;p&gt;It means: &lt;em&gt;"FastAPI, before running this endpoint, run this dependency and give me its result."&lt;/em&gt;&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;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&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="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;verify_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"Deepika"&lt;/span&gt;

&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/profile"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_profile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;verify_user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What happens:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A request hits &lt;code&gt;/profile&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;FastAPI sees &lt;code&gt;Depends(verify_user)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;FastAPI runs &lt;code&gt;verify_user()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;It returns &lt;code&gt;"Deepika"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;FastAPI passes that value into the &lt;code&gt;user&lt;/code&gt; parameter&lt;/li&gt;
&lt;li&gt;The endpoint runs&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Response:&lt;br&gt;
&lt;/p&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="nl"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Deepika"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's &lt;strong&gt;Dependency Injection (DI)&lt;/strong&gt;. 💉&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🔗 &lt;strong&gt;Connect the dots&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Depends()&lt;/code&gt; → the dependency injection mechanism&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;OAuth2PasswordBearer&lt;/code&gt; → a dependency that extracts a bearer token&lt;/li&gt;
&lt;li&gt;Your custom dependency (&lt;code&gt;get_current_user&lt;/code&gt;) → verifies the token and identifies the user&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Combining them
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;
&lt;span class="k"&gt;from&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;security&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;OAuth2PasswordBearer&lt;/span&gt;

&lt;span class="n"&gt;oauth2_scheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OAuth2PasswordBearer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokenUrl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"login"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/profile"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_profile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oauth2_scheme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"received_token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Incoming request
Authorization: Bearer abc123
        │
        ▼
Depends(oauth2_scheme)
        │  extracts "abc123"
        ▼
Endpoint receives token = "abc123"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But we haven't verified that &lt;code&gt;abc123&lt;/code&gt; is a valid JWT yet. That's coming up! 😉&lt;/p&gt;




&lt;h2&gt;
  
  
  🔟 Password hashing: why we can't store passwords directly
&lt;/h2&gt;

&lt;p&gt;This is a &lt;strong&gt;HUGE&lt;/strong&gt; real-world security concept. 🚨&lt;/p&gt;

&lt;p&gt;A user registers with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Email:    deepika@gmail.com
Password: deepika123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A beginner might store this directly:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;email&lt;/th&gt;
&lt;th&gt;password&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:deepika@gmail.com"&gt;deepika@gmail.com&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;deepika123&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If your database is ever compromised, the attacker reads &lt;strong&gt;everyone's&lt;/strong&gt; passwords. 💀 Applications must &lt;strong&gt;NEVER&lt;/strong&gt; store plaintext passwords. They store &lt;strong&gt;password hashes&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  10.1 What is hashing?
&lt;/h3&gt;

&lt;p&gt;Hashing is a &lt;strong&gt;one-way&lt;/strong&gt; transformation of input into a fixed-format output.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Input:  deepika123
Hash:   $argon2id$v=19$m=65536,t=3,p=4$...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(The exact output depends on the algorithm and a random salt.)&lt;/p&gt;

&lt;p&gt;You cannot practically reverse a properly designed password hash back to the original password.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Hashing&lt;/th&gt;
&lt;th&gt;Encryption&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;One-way&lt;/td&gt;
&lt;td&gt;Reversible with the key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No decryption operation&lt;/td&gt;
&lt;td&gt;Can be decrypted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Used for password verification&lt;/td&gt;
&lt;td&gt;Used for confidentiality&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Example: Argon2id&lt;/td&gt;
&lt;td&gt;Example: AES&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Password hashing is deliberately designed to make guessing &lt;strong&gt;expensive&lt;/strong&gt;. 💸&lt;/p&gt;

&lt;h3&gt;
  
  
  10.2 What is bcrypt?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;bcrypt&lt;/strong&gt; is a password-hashing algorithm built specifically for password storage. Its key property is a configurable &lt;strong&gt;computational cost&lt;/strong&gt;: the higher the cost, the more resources each hash needs, making bulk guessing slower.&lt;/p&gt;

&lt;h3&gt;
  
  
  10.3 🧂 What is a salt?
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;salt&lt;/strong&gt; is a random value added during password hashing.&lt;/p&gt;

&lt;p&gt;Two users register with the same password:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User A: deepika123
User B: deepika123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without salts, a deterministic scheme gives both the same hash. With unique random salts, their stored hashes differ.&lt;/p&gt;

&lt;p&gt;Why it matters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Identical passwords get different hashes&lt;/li&gt;
&lt;li&gt;✅ Precomputed rainbow-table attacks become much less useful&lt;/li&gt;
&lt;li&gt;✅ Bulk password guessing gets harder&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Good libraries generate and manage salts &lt;strong&gt;automatically&lt;/strong&gt;. Don't roll your own.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  10.4 ✅ How does verification work?
&lt;/h3&gt;

&lt;p&gt;You do &lt;strong&gt;not&lt;/strong&gt; "decrypt" the stored hash. Instead:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The library reads the algorithm and salt from the stored hash&lt;/li&gt;
&lt;li&gt;Hashes the submitted password with those parameters&lt;/li&gt;
&lt;li&gt;Compares the results securely&lt;/li&gt;
&lt;li&gt;Returns &lt;code&gt;True&lt;/code&gt; or &lt;code&gt;False&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REGISTRATION                      LOGIN
plain password                    entered password
     │                                 │
     ▼                                 ▼
  hashing                       verify against stored hash
     │                                 │
     ▼                                 ▼
 store the hash                    True / False
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  1️⃣1️⃣ What is Passlib?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Passlib&lt;/strong&gt; is a Python library for password hashing and verification. It supports bcrypt, Argon2, PBKDF2 and more, so you never implement hashing by hand.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip install &lt;span class="s2"&gt;"passlib[argon2]"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 I'm using &lt;strong&gt;Argon2id&lt;/strong&gt; here, a modern, memory-hard algorithm (it needs both CPU &lt;em&gt;and&lt;/em&gt; memory to compute, making guessing even more expensive). If you prefer bcrypt, use &lt;code&gt;pip install "passlib[bcrypt]"&lt;/code&gt; and &lt;code&gt;schemes=["bcrypt"]&lt;/code&gt;. Just make sure your &lt;code&gt;passlib&lt;/code&gt; and &lt;code&gt;bcrypt&lt;/code&gt; versions are compatible, since newer &lt;code&gt;bcrypt&lt;/code&gt; releases have caused warnings or errors with older &lt;code&gt;passlib&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;passlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;CryptContext&lt;/span&gt;

&lt;span class="n"&gt;pwd_context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CryptContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;schemes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"argon2"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;deprecated&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"auto"&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;ul&gt;
&lt;li&gt;
&lt;code&gt;CryptContext&lt;/code&gt; → Passlib's hashing configuration object&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;schemes=["argon2"]&lt;/code&gt; → our chosen algorithm&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;deprecated="auto"&lt;/code&gt; → lets Passlib flag older schemes that should be upgraded when users log in&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Hash a password
&lt;/h3&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;hash_password&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pwd_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;hashed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;hash_password&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"deepika123"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hashed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# a long Argon2id hash, NOT the original password&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verify a password
&lt;/h3&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;verify_password&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plain_password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hashed_password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pwd_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plain_password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hashed_password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;verify_password&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"deepika123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hashed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="c1"&gt;# True ✅&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;verify_password&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"wrongpassword"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hashed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# False ❌&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🧠 Passlib is the &lt;strong&gt;library&lt;/strong&gt;; Argon2id / bcrypt is the &lt;strong&gt;algorithm&lt;/strong&gt;. Don't mix them up!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The important lesson isn't "always use bcrypt". It's &lt;strong&gt;use a modern, slow, salted password-hashing algorithm&lt;/strong&gt;, never plaintext and never ordinary SHA-256.&lt;/p&gt;




&lt;h2&gt;
  
  
  1️⃣2️⃣ Building a complete FastAPI authentication system 🏗️
&lt;/h2&gt;

&lt;p&gt;Time to combine everything. We'll build:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;✅ User registration&lt;/li&gt;
&lt;li&gt;✅ Password hashing&lt;/li&gt;
&lt;li&gt;✅ User login&lt;/li&gt;
&lt;li&gt;✅ Password verification&lt;/li&gt;
&lt;li&gt;✅ JWT generation&lt;/li&gt;
&lt;li&gt;✅ Token extraction&lt;/li&gt;
&lt;li&gt;✅ Token verification&lt;/li&gt;
&lt;li&gt;✅ Protected endpoints&lt;/li&gt;
&lt;li&gt;✅ A reusable authentication dependency&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We'll use an &lt;strong&gt;in-memory dictionary&lt;/strong&gt; so it's beginner-friendly. In a real app, PostgreSQL and a repository layer replace it.&lt;/p&gt;

&lt;h3&gt;
  
  
  12.1 📁 Folder structure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;backend/
│
├── app/
│   ├── main.py
│   │
│   ├── api/
│   │   └── auth.py
│   │
│   ├── core/
│   │   └── security.py
│   │
│   ├── dependencies/
│   │   └── auth.py
│   │
│   ├── models/
│   │   └── user.py
│   │
│   └── schemas/
│       └── auth.py
│
└── requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Responsibility&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;core/security.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Password hashing, JWT creation and verification&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;api/auth.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Login and registration endpoints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dependencies/auth.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Extract token, verify token, identify current user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;models/user.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;User database model&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;schemas/auth.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Request and response validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;main.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Creates the FastAPI application&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This is &lt;strong&gt;separation of concerns&lt;/strong&gt;: each file has one job. 🎯&lt;/p&gt;

&lt;p&gt;Install what we need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip install fastapi uvicorn pyjwt &lt;span class="s2"&gt;"passlib[argon2]"&lt;/span&gt; python-multipart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(&lt;code&gt;python-multipart&lt;/code&gt; is required because the login endpoint receives &lt;strong&gt;form data&lt;/strong&gt;.)&lt;/p&gt;

&lt;h3&gt;
  
  
  12.2 🔐 Step 1: &lt;code&gt;core/security.py&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timezone&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;jwt&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;jwt&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;InvalidTokenError&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;passlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;CryptContext&lt;/span&gt;

&lt;span class="c1"&gt;# 🔑 Load from environment, NEVER hardcode in real projects&lt;/span&gt;
&lt;span class="no"&gt;SECRET_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"JWT_SECRET_KEY"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"dev-only-change-me"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="no"&gt;ALGORITHM&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"HS256"&lt;/span&gt;
&lt;span class="no"&gt;ACCESS_TOKEN_EXPIRE_MINUTES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;

&lt;span class="n"&gt;pwd_context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CryptContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;schemes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"argon2"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;deprecated&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"auto"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="c1"&gt;# 🔒 Password helpers&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hash_password&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pwd_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;verify_password&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plain_password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hashed_password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pwd_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plain_password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hashed_password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="c1"&gt;# 🎟️ JWT helpers&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_access_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"sub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"iat"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"exp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;minutes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="no"&gt;ACCESS_TOKEN_EXPIRE_MINUTES&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;algorithm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="no"&gt;ALGORITHM&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;decode_access_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;algorithms&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;ALGORITHM&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nc"&gt;InvalidTokenError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"sub"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Four reusable operations in one place:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;hash_password()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;verify_password()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;create_access_token()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;decode_access_token()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  12.3 🚪 Step 2: Registration and login routes (&lt;code&gt;api/auth.py&lt;/code&gt;)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;APIRouter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;HTTPException&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;
&lt;span class="k"&gt;from&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;security&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;OAuth2PasswordRequestForm&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;BaseModel&lt;/span&gt;

&lt;span class="k"&gt;from&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;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;security&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;create_access_token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;hash_password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;verify_password&lt;/span&gt;&lt;span class="p"&gt;,&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="nc"&gt;APIRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 🧪 In-memory "database" (use PostgreSQL in real life!)&lt;/span&gt;
&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RegisterRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;


&lt;span class="nd"&gt;@router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/register"&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="no"&gt;HTTP_201_CREATED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;RegisterRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;HTTPException&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="no"&gt;HTTP_400_BAD_REQUEST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"User already exists"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"hashed_password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;hash_password&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"User registered successfully"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="nd"&gt;@router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/login"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;form_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;OAuth2PasswordRequestForm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&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;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;form_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# username field = our email&lt;/span&gt;

    &lt;span class="c1"&gt;# Same error for "no such user" and "wrong password" 👇&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;verify_password&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;form_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"hashed_password"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;HTTPException&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="no"&gt;HTTP_401_UNAUTHORIZED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Incorrect email or password"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"WWW-Authenticate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Bearer"&lt;/span&gt;&lt;span class="p"&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;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_access_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"access_token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"token_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"bearer"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  🧐 What is &lt;code&gt;OAuth2PasswordRequestForm&lt;/code&gt;?
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;form_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;OAuth2PasswordRequestForm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Depends&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;p&gt;It's another FastAPI-provided dependency that extracts login credentials sent as &lt;strong&gt;form data&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;form_data.username
form_data.password
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The client submits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="no"&gt;POST&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;
&lt;span class="nc"&gt;Content&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nc"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;urlencoded&lt;/span&gt;

&lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;deepika&lt;/span&gt;@&lt;span class="n"&gt;gmail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;deepika123&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice it says &lt;code&gt;username&lt;/code&gt; even if your app identifies users by &lt;strong&gt;email&lt;/strong&gt;. Just treat the &lt;code&gt;username&lt;/code&gt; field as the email.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Heads up:&lt;/strong&gt; the OAuth2 &lt;em&gt;Resource Owner Password Credentials&lt;/em&gt; grant is discouraged by modern OAuth security guidance for new systems. This form-based pattern is perfect for learning and for a first-party login API, but for production with external identity providers prefer &lt;strong&gt;Authorization Code with PKCE&lt;/strong&gt;. A first-party login API can also simply accept a JSON request body.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  12.4 🛡️ Step 3: Reusable authentication dependency (&lt;code&gt;dependencies/auth.py&lt;/code&gt;)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;HTTPException&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;
&lt;span class="k"&gt;from&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;security&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;OAuth2PasswordBearer&lt;/span&gt;

&lt;span class="k"&gt;from&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;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;from&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;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;security&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;decode_access_token&lt;/span&gt;

&lt;span class="n"&gt;oauth2_scheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OAuth2PasswordBearer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokenUrl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"login"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_current_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oauth2_scheme&lt;/span&gt;&lt;span class="p"&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;credentials_error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HTTPException&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="no"&gt;HTTP_401_UNAUTHORIZED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Invalid or expired token"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"WWW-Authenticate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Bearer"&lt;/span&gt;&lt;span class="p"&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;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;decode_access_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;credentials_error&lt;/span&gt;

    &lt;span class="c1"&gt;# ✅ Check the user still exists (real apps: query the DB / session)&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;credentials_error&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Trace it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;oauth2_scheme&lt;/code&gt; → expects a bearer token in the &lt;code&gt;Authorization&lt;/code&gt; header&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Depends(oauth2_scheme)&lt;/code&gt; → FastAPI extracts the token and injects it&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;decode_access_token(token)&lt;/code&gt; → verifies the token and returns the user ID&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;if user_id is None&lt;/code&gt; → invalid or expired token means &lt;strong&gt;reject&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;We look the user up, because a valid signature doesn't prove the account is still active&lt;/li&gt;
&lt;li&gt;Return the authenticated user&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  12.5 🔒 Step 4: Protect an endpoint (&lt;code&gt;main.py&lt;/code&gt;)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;

&lt;span class="k"&gt;from&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;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;router&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;auth_router&lt;/span&gt;
&lt;span class="k"&gt;from&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;dependencies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;get_current_user&lt;/span&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="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;auth_router&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/profile"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_profile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_current_user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Welcome!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;]&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;p&gt;That one line:&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;current_user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_current_user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;does a LOT of work. Before &lt;code&gt;/profile&lt;/code&gt; runs, FastAPI:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Calls &lt;code&gt;get_current_user()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Extracts the token via &lt;code&gt;oauth2_scheme&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Verifies the JWT&lt;/li&gt;
&lt;li&gt;Loads the user&lt;/li&gt;
&lt;li&gt;Injects the user into your endpoint&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;✅ Success:&lt;br&gt;
&lt;/p&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="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Welcome!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"deepika@gmail.com"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;❌ Failure (the endpoint body never runs):&lt;br&gt;
&lt;/p&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="nl"&gt;"detail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Invalid or expired token"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  12.6 🧪 Try it!
&lt;/h3&gt;

&lt;p&gt;Run the app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uvicorn app.main:app --reload
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open &lt;code&gt;http://127.0.0.1:8000/docs&lt;/code&gt;, register a user, click &lt;strong&gt;Authorize&lt;/strong&gt; (🔓) and log in. Swagger UI now automatically sends your bearer token. Or test with curl:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Register&lt;/span&gt;
curl -X POST http://127.0.0.1:8000/register \
  -H &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; \
  -d &lt;span class="s1"&gt;'{"email": "deepika@gmail.com", "password": "deepika123"}'&lt;/span&gt;

&lt;span class="c"&gt;# 2. Login (form data!)&lt;/span&gt;
curl -X POST http://127.0.0.1:8000/login \
  -d &lt;span class="s2"&gt;"username=deepika@gmail.com&amp;amp;password=deepika123"&lt;/span&gt;

&lt;span class="c"&gt;# 3. Call the protected route&lt;/span&gt;
curl http://127.0.0.1:8000/profile \
  -H &lt;span class="s2"&gt;"Authorization: Bearer &amp;lt;paste_access_token_here&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ This is intentionally simplified. The in-memory &lt;code&gt;users&lt;/code&gt; dict isn't shared between processes and disappears on restart. A real implementation uses database-backed users and sessions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  12.7 🚦 Bonus: Authorization (role checks)
&lt;/h3&gt;

&lt;p&gt;Authentication tells you &lt;em&gt;who&lt;/em&gt;. Now let's check &lt;em&gt;what they can do&lt;/em&gt;, by building on &lt;code&gt;get_current_user&lt;/code&gt;:&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;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;HTTPException&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;require_admin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_current_user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;HTTPException&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="no"&gt;HTTP_403_FORBIDDEN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"You don't have permission to do this"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt;


&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/posts/{post_id}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;delete_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;require_admin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"Post {post_id} deleted"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dependencies can depend on other dependencies. 🧩 That's what makes &lt;code&gt;Depends()&lt;/code&gt; so powerful.&lt;/p&gt;




&lt;h2&gt;
  
  
  1️⃣3️⃣ Complete execution flow 🧭
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Phase A: Registration 📝
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User enters email + password
        │
        ▼
POST /register
        │
        ▼
hash_password()  →  plaintext becomes a secure hash
        │
        ▼
PostgreSQL stores: email + password hash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Phase B: Login 🔑
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /login  (username + password)
        │
        ▼
Backend finds the user, verify_password()
        │
        ▼
create_access_token()  →  JWT created and signed
        │
        ▼
Frontend receives: access_token + "bearer"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Phase C: Accessing a protected endpoint 🚪
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;React requests /profile
Authorization: Bearer &amp;lt;JWT&amp;gt;
        │
        ▼
OAuth2PasswordBearer  →  extracts the JWT
        │
        ▼
get_current_user()  →  verifies token, identifies user
        │
        ▼
Depends(get_current_user)  →  injects the user into the endpoint
        │
        ▼
✅ Endpoint returns the user's profile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you understand this flow, you understand the foundation of JWT authentication in FastAPI. 🏆&lt;/p&gt;




&lt;h2&gt;
  
  
  1️⃣4️⃣ Token expiry, refresh tokens and logout ⏰
&lt;/h2&gt;

&lt;p&gt;Our JWT contains:&lt;br&gt;
&lt;/p&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="nl"&gt;"sub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"101"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"exp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1794400000&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Suppose the token is created at &lt;strong&gt;10:00 AM&lt;/strong&gt; and expires at &lt;strong&gt;10:30 AM&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;10:15 AM request → ✅ valid, the request proceeds&lt;/li&gt;
&lt;li&gt;10:45 AM request → ❌ expired, the backend rejects it, and the user needs a new access token&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  14.1 🎫 Access token
&lt;/h3&gt;

&lt;p&gt;A credential presented to an API to access protected resources.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="no"&gt;GET&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;feed&lt;/span&gt;
&lt;span class="nc"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Bearer&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Access tokens are &lt;strong&gt;short-lived&lt;/strong&gt;, so a stolen one is only useful for a limited time (plus any extra revocation checks).&lt;/p&gt;

&lt;h3&gt;
  
  
  14.2 🔁 Refresh token
&lt;/h3&gt;

&lt;p&gt;A credential used to get a &lt;strong&gt;new access token&lt;/strong&gt; without asking for the password again.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Access token lifetime: 30 minutes&lt;/li&gt;
&lt;li&gt;Refresh token lifetime: 7 days&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When the access token expires:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Frontend sends the refresh token to a refresh endpoint&lt;/li&gt;
&lt;li&gt;Backend validates it&lt;/li&gt;
&lt;li&gt;Backend issues a new access token&lt;/li&gt;
&lt;li&gt;The user keeps using the app seamlessly ✨&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Refresh tokens are &lt;strong&gt;especially sensitive&lt;/strong&gt;. Store them carefully, rotate them when used where appropriate, and make them &lt;strong&gt;revocable&lt;/strong&gt;. For browser apps, a common approach is &lt;code&gt;HttpOnly&lt;/code&gt;, &lt;code&gt;Secure&lt;/code&gt;, properly configured &lt;code&gt;SameSite&lt;/code&gt; cookies.&lt;/p&gt;

&lt;h3&gt;
  
  
  14.3 🚪 What about logout?
&lt;/h3&gt;

&lt;p&gt;Traditional server-side sessions can be killed by deleting the session on the server. But a self-contained JWT stays &lt;strong&gt;cryptographically valid until it expires&lt;/strong&gt;, even after the user clicks "Logout". The JWT doesn't magically know the user logged out. 🤔&lt;/p&gt;

&lt;p&gt;Possible solutions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep access tokens short-lived&lt;/li&gt;
&lt;li&gt;Track or revoke sessions server-side&lt;/li&gt;
&lt;li&gt;Use a token denylist when appropriate&lt;/li&gt;
&lt;li&gt;Revoke refresh tokens&lt;/li&gt;
&lt;li&gt;Rotate session credentials&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is why real-world systems often combine tokens with &lt;strong&gt;database-backed sessions&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  1️⃣5️⃣ JWT vs Session-based authentication ⚔️
&lt;/h2&gt;

&lt;p&gt;Both are widely used in production.&lt;/p&gt;

&lt;h3&gt;
  
  
  🗄️ Session-based
&lt;/h3&gt;

&lt;p&gt;The backend creates a session in the database:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Session ID&lt;/th&gt;
&lt;th&gt;User ID&lt;/th&gt;
&lt;th&gt;Expires&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;abc123&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;7 days&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;On every request: the frontend sends the session ID → the backend looks it up → checks validity → identifies the user.&lt;/p&gt;

&lt;h3&gt;
  
  
  🎟️ JWT-based
&lt;/h3&gt;

&lt;p&gt;The backend issues a signed JWT containing identity and expiry. On every request: the frontend sends the JWT → the backend verifies the signature → checks the claims → identifies the user.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;JWT&lt;/th&gt;
&lt;th&gt;Server-side session&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Identity carried in token claims&lt;/td&gt;
&lt;td&gt;Identity retrieved via session lookup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can be verified without a DB lookup&lt;/td&gt;
&lt;td&gt;Normally requires a session lookup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Revocation needs extra mechanisms&lt;/td&gt;
&lt;td&gt;Revoked by invalidating the session&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Self-contained token&lt;/td&gt;
&lt;td&gt;Server maintains session state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Common in distributed APIs&lt;/td&gt;
&lt;td&gt;Common in traditional web apps&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;⚖️ Neither is automatically better. The right choice depends on your architecture and security requirements.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  💼 In my Social Feed project
&lt;/h3&gt;

&lt;p&gt;My implementation actually uses &lt;strong&gt;database-backed sessions&lt;/strong&gt; (7-day lifetime) with &lt;strong&gt;Argon2id&lt;/strong&gt; for passwords, and &lt;strong&gt;SHA-256 hashing of a random session token&lt;/strong&gt; for lookups. That's different from the JWT example above:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;My session implementation&lt;/th&gt;
&lt;th&gt;JWT implementation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Random session token&lt;/td&gt;
&lt;td&gt;Structured JWT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hash token for DB lookup&lt;/td&gt;
&lt;td&gt;Cryptographically sign token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database session lookup&lt;/td&gt;
&lt;td&gt;Signature verification&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Easy server-side revocation&lt;/td&gt;
&lt;td&gt;Needs extra revocation strategy&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 &lt;strong&gt;Why is SHA-256 okay here?&lt;/strong&gt; Because it's hashing a &lt;strong&gt;high-entropy random token&lt;/strong&gt;, not a human-chosen password. Passwords need slow, salted algorithms like Argon2id or bcrypt. Random tokens don't, since they can't be guessed from a dictionary.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  1️⃣6️⃣ HTTP status codes in authentication 📟
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Code&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;200&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;OK&lt;/td&gt;
&lt;td&gt;Successful login&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;201&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Created&lt;/td&gt;
&lt;td&gt;Successful registration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;400&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bad Request&lt;/td&gt;
&lt;td&gt;Invalid request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;401&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unauthorized&lt;/td&gt;
&lt;td&gt;Missing or invalid authentication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;403&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Forbidden&lt;/td&gt;
&lt;td&gt;Authenticated but lacks permission&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;404&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Not Found&lt;/td&gt;
&lt;td&gt;User doesn't exist&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;422&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Validation Error&lt;/td&gt;
&lt;td&gt;Invalid request schema&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;500&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Internal Server Error&lt;/td&gt;
&lt;td&gt;Unexpected backend failure&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The most important distinction: &lt;code&gt;401&lt;/code&gt; vs &lt;code&gt;403&lt;/code&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🪪 You try to enter an office &lt;strong&gt;without an ID card&lt;/strong&gt; → &lt;code&gt;401&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;🚫 You show a &lt;strong&gt;valid ID card&lt;/strong&gt; but try to enter the server room → &lt;code&gt;403&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Authentication asks: &lt;em&gt;is your identity valid?&lt;/em&gt; Authorization asks: &lt;em&gt;do you have permission?&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  1️⃣7️⃣ Common security mistakes ⚠️
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mistake&lt;/th&gt;
&lt;th&gt;Why it's dangerous&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Storing plaintext passwords&lt;/td&gt;
&lt;td&gt;A DB leak exposes passwords&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Using plain SHA-256 for passwords&lt;/td&gt;
&lt;td&gt;Too fast, easy to brute-force&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hardcoding JWT secrets&lt;/td&gt;
&lt;td&gt;The secret leaks through source code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Never-expiring access tokens&lt;/td&gt;
&lt;td&gt;Stolen tokens stay useful forever&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Putting passwords in the JWT payload&lt;/td&gt;
&lt;td&gt;The payload can be decoded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Accepting any JWT algorithm&lt;/td&gt;
&lt;td&gt;Serious verification vulnerabilities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Not checking token expiration&lt;/td&gt;
&lt;td&gt;Expired credentials may be accepted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Detailed login errors&lt;/td&gt;
&lt;td&gt;Reveals which accounts exist&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storing tokens carelessly&lt;/td&gt;
&lt;td&gt;XSS or other attacks can steal them&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Forgetting HTTPS&lt;/td&gt;
&lt;td&gt;Credentials and tokens can be intercepted&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  📖 A few more terms worth knowing
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;HTTPS&lt;/strong&gt; → encrypts communication between browser and server&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CORS&lt;/strong&gt; → controls which browser origins may access your backend (it is &lt;strong&gt;not&lt;/strong&gt; authentication!)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;XSS&lt;/strong&gt; → malicious scripts executing in a user's browser&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CSRF&lt;/strong&gt; → tricks a browser into making an unwanted authenticated request&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Brute-force attack&lt;/strong&gt; → repeatedly guessing passwords&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate limiting&lt;/strong&gt; → restricts excessive requests, like repeated login attempts&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🍪 Where should the frontend store tokens?
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;localStorage&lt;/code&gt; is readable by JavaScript, so it's exposed if an XSS vulnerability exists. &lt;code&gt;HttpOnly&lt;/code&gt; cookies can't be read by JavaScript, but cookie-based auth needs proper &lt;strong&gt;CSRF protection&lt;/strong&gt;. There's no free lunch. Design it carefully. 🧠&lt;/p&gt;




&lt;h2&gt;
  
  
  1️⃣8️⃣ How this connects to real FastAPI architecture 🏛️
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌──────────────────────────────────────────────┐
│  API / Router layer                          │
│  Receives login and protected requests       │
│  e.g. auth.py, users.py                      │
├──────────────────────────────────────────────┤
│  Dependency layer                            │
│  Extracts token, identifies the user         │
│  e.g. get_current_user()                     │
├──────────────────────────────────────────────┤
│  Security layer                              │
│  Hash/verify passwords, issue/verify JWTs    │
│  e.g. security.py                            │
├──────────────────────────────────────────────┤
│  Service layer                               │
│  Authentication business logic               │
│  e.g. auth_service.py                        │
├──────────────────────────────────────────────┤
│  Repository / Database layer                 │
│  Fetch users, create sessions, run queries   │
│  e.g. user_repository.py, session_repository.py │
└──────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🧼 &lt;strong&gt;Principle:&lt;/strong&gt; keep the router &lt;strong&gt;thin&lt;/strong&gt;. Delegate business logic to services and database work to repositories.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  1️⃣9️⃣ Interview questions &amp;amp; answers 🎤
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q1. What is authentication?&lt;/strong&gt;&lt;br&gt;
Verifying the identity of a user before allowing access to protected resources.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q2. What is JWT?&lt;/strong&gt;&lt;br&gt;
JSON Web Token, a compact, signed token format used to transfer claims between parties.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q3. What are the three parts of a JWT?&lt;/strong&gt;&lt;br&gt;
Header, payload and signature.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q4. Is JWT encrypted?&lt;/strong&gt;&lt;br&gt;
Not by default. A typical signed JWT has an encoded header and payload plus a signature. The payload can be decoded by anyone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q5. What is &lt;code&gt;OAuth2PasswordBearer&lt;/code&gt;?&lt;/strong&gt;&lt;br&gt;
A FastAPI security dependency that extracts a bearer token from the &lt;code&gt;Authorization&lt;/code&gt; header and declares an OAuth2 security scheme in the API docs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q6. Does &lt;code&gt;OAuth2PasswordBearer&lt;/code&gt; verify JWT signatures?&lt;/strong&gt;&lt;br&gt;
No. It only extracts the token. You need separate verification logic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q7. What is &lt;code&gt;Depends()&lt;/code&gt;?&lt;/strong&gt;&lt;br&gt;
FastAPI's dependency injection mechanism, which lets reusable functions and resources be provided to endpoints.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q8. What is password hashing?&lt;/strong&gt;&lt;br&gt;
A one-way transformation of a password into a secure stored form that can be verified later without storing the original.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q9. What is bcrypt?&lt;/strong&gt;&lt;br&gt;
A password-hashing algorithm designed to make guessing computationally expensive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q10. What is a salt?&lt;/strong&gt;&lt;br&gt;
A random value included in password hashing so identical passwords produce different stored hashes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q11. Access token vs refresh token?&lt;/strong&gt;&lt;br&gt;
An access token accesses protected APIs. A refresh token obtains a new access token.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q12. What happens when a JWT expires?&lt;/strong&gt;&lt;br&gt;
The backend rejects it. The client may use a valid refresh mechanism to get a new one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q13. JWT vs session authentication?&lt;/strong&gt;&lt;br&gt;
JWT carries signed claims the server can verify. Sessions store state on the server and look it up via a session ID.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q14. Why not store passwords directly in PostgreSQL?&lt;/strong&gt;&lt;br&gt;
A database breach would expose real passwords. We store salted, computationally expensive hashes instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q15. 401 vs 403?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;401&lt;/code&gt; means valid authentication is missing. &lt;code&gt;403&lt;/code&gt; means you're authenticated but lack permission.&lt;/p&gt;




&lt;h2&gt;
  
  
  2️⃣0️⃣ Quick knowledge test 🧪
&lt;/h2&gt;

&lt;p&gt;Answer without scrolling up! (Answers are below.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. What does &lt;code&gt;OAuth2PasswordBearer&lt;/code&gt; actually do?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Generates a JWT&lt;/li&gt;
&lt;li&gt;B) Extracts the bearer token from the &lt;code&gt;Authorization&lt;/code&gt; header&lt;/li&gt;
&lt;li&gt;C) Hashes the password&lt;/li&gt;
&lt;li&gt;D) Connects to PostgreSQL&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Which three parts make up a JWT?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Username, password, signature&lt;/li&gt;
&lt;li&gt;B) Header, payload, signature&lt;/li&gt;
&lt;li&gt;C) Request, response, token&lt;/li&gt;
&lt;li&gt;D) Issuer, database, session&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Why do we hash passwords?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) To decrypt them later&lt;/li&gt;
&lt;li&gt;B) To make login faster&lt;/li&gt;
&lt;li&gt;C) To avoid storing plaintext passwords&lt;/li&gt;
&lt;li&gt;D) To create JWTs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. What does &lt;code&gt;Depends()&lt;/code&gt; do in FastAPI?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Automatically injects dependency results&lt;/li&gt;
&lt;li&gt;B) Creates database tables&lt;/li&gt;
&lt;li&gt;C) Encrypts tokens&lt;/li&gt;
&lt;li&gt;D) Starts Uvicorn&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;5. Is JWT payload encryption automatic?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Yes, always&lt;/li&gt;
&lt;li&gt;B) No, ordinary signed JWTs are not encrypted&lt;/li&gt;
&lt;li&gt;C) Only with HS256&lt;/li&gt;
&lt;li&gt;D) Only with FastAPI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;6. What happens when a JWT expires?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) It becomes valid forever&lt;/li&gt;
&lt;li&gt;B) The backend should reject it&lt;/li&gt;
&lt;li&gt;C) It refreshes itself automatically&lt;/li&gt;
&lt;li&gt;D) The database deletes the user&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👀 Click to reveal answers&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;B&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;B&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;C&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;B&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;B&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  🧾 Final revision: everything in one place
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;What to remember&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Authentication&lt;/td&gt;
&lt;td&gt;Verifying who the user is&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authorization&lt;/td&gt;
&lt;td&gt;Checking what the user can access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Token&lt;/td&gt;
&lt;td&gt;Credential representing an authenticated session or identity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JWT&lt;/td&gt;
&lt;td&gt;Signed token format containing claims&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Header&lt;/td&gt;
&lt;td&gt;Algorithm and token type&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Payload&lt;/td&gt;
&lt;td&gt;User identity and claims&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Signature&lt;/td&gt;
&lt;td&gt;Protects token integrity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OAuth2&lt;/td&gt;
&lt;td&gt;Authorization framework&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OAuth2PasswordBearer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Extracts the bearer token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Depends()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Dependency injection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Password hashing&lt;/td&gt;
&lt;td&gt;Secure password storage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;bcrypt&lt;/td&gt;
&lt;td&gt;Password-hashing algorithm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Argon2id&lt;/td&gt;
&lt;td&gt;Modern memory-hard password-hashing algorithm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Passlib&lt;/td&gt;
&lt;td&gt;Python password-hashing library&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Access token&lt;/td&gt;
&lt;td&gt;Used to access protected resources&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Refresh token&lt;/td&gt;
&lt;td&gt;Used to obtain new access tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_current_user()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reusable dependency to identify the authenticated user&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🎯 Final thoughts
&lt;/h2&gt;

&lt;p&gt;Don't just memorize definitions. Be able to explain the &lt;strong&gt;complete journey&lt;/strong&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A user clicks &lt;strong&gt;Login&lt;/strong&gt; in React → FastAPI verifies the password hash → a token is issued → React sends it on every request → &lt;code&gt;OAuth2PasswordBearer&lt;/code&gt; extracts it → &lt;code&gt;get_current_user()&lt;/code&gt; verifies it → &lt;code&gt;Depends()&lt;/code&gt; injects the user → the protected &lt;code&gt;/feed&lt;/code&gt; endpoint runs. 🚀&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you can narrate that flow confidently, you truly understand authentication.&lt;/p&gt;

&lt;p&gt;And remember: &lt;strong&gt;JWT-based&lt;/strong&gt; and &lt;strong&gt;database-session-based&lt;/strong&gt; authentication are different designs. Both are valid. Knowing which one &lt;em&gt;your&lt;/em&gt; code uses, and why, is what separates memorizing from understanding. 💡&lt;/p&gt;




&lt;h3&gt;
  
  
  🙌 Enjoyed this post?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;❤️ Drop a reaction&lt;/li&gt;
&lt;li&gt;🦄 Save it for revision&lt;/li&gt;
&lt;li&gt;💬 Comment with questions, and I'll answer them!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Follow me so you don't miss it! 🔔&lt;/p&gt;

&lt;p&gt;Happy coding! 👩‍💻✨&lt;/p&gt;

&lt;h1&gt;
  
  
  fastapi #python #jwt #security #webdev
&lt;/h1&gt;

</description>
      <category>authentication</category>
      <category>fastapi</category>
      <category>python</category>
      <category>security</category>
    </item>
    <item>
      <title>SQLAlchemy 2.0 &amp; Alembic Migrations: A Complete Beginner's Guide🗄️</title>
      <dc:creator>Deepika Pusala</dc:creator>
      <pubDate>Wed, 11 Nov 2026 07:12:43 +0000</pubDate>
      <link>https://dev.to/deepika_pusala/sqlalchemy-20-alembic-migrations-a-complete-beginners-guide-2oo2</link>
      <guid>https://dev.to/deepika_pusala/sqlalchemy-20-alembic-migrations-a-complete-beginners-guide-2oo2</guid>
      <description>&lt;h1&gt;
  
  
  🗄️ SQLAlchemy 2.0 &amp;amp; Alembic Migrations: Let's Learn from ZERO!
&lt;/h1&gt;

&lt;p&gt;Once you understand &lt;strong&gt;SQLAlchemy 2.0 + Alembic&lt;/strong&gt;, you understand how a real Python backend &lt;strong&gt;talks to PostgreSQL, stores data, retrieves records, updates them, and safely changes database structure as the app grows&lt;/strong&gt;. 🚀&lt;/p&gt;

&lt;p&gt;No assumptions here. You don't need to know ORMs, sessions, migrations, or database internals. We'll use examples from an Instagram-like &lt;strong&gt;SocialFeed&lt;/strong&gt; app throughout.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. 🖼️ The Big Picture
&lt;/h2&gt;

&lt;p&gt;SocialFeed stores &lt;strong&gt;users, posts, comments, likes, follows, notifications and sessions&lt;/strong&gt;. All of it must live permanently in &lt;strong&gt;PostgreSQL&lt;/strong&gt;. But your backend is written in &lt;strong&gt;Python&lt;/strong&gt;, while PostgreSQL understands &lt;strong&gt;SQL&lt;/strong&gt;. How do they talk?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🐍 Python / FastAPI     → your application code
        ▼
🧱 SQLAlchemy 2.0       → converts Python database operations into SQL
        ▼
🐘 PostgreSQL           → stores and retrieves your data

🔄 Alembic              → manages changes to your database structure over time
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SQLAlchemy&lt;/strong&gt; helps Python communicate with the database&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alembic&lt;/strong&gt; helps you modify the database structure safely over time&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  🧱 PART 1: SQLAlchemy Fundamentals
&lt;/h1&gt;

&lt;h2&gt;
  
  
  2. 🤔 What exactly is SQLAlchemy?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;SQLAlchemy&lt;/strong&gt; is a Python library for interacting with relational databases.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It lets you create tables, insert, retrieve, update and delete records, manage connections and transactions, and work with relationships.&lt;/p&gt;

&lt;p&gt;Raw SQL to fetch user 10:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the SQLAlchemy ORM:&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;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Raw SQL:&lt;/strong&gt; you write SQL statements yourself&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQLAlchemy ORM:&lt;/strong&gt; you work with Python classes and objects&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ SQLAlchemy doesn't eliminate SQL. It helps you work with SQL &lt;em&gt;through Python&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  3. 🪄 What is an ORM?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;ORM&lt;/strong&gt; = &lt;strong&gt;O&lt;/strong&gt;bject &lt;strong&gt;R&lt;/strong&gt;elational &lt;strong&gt;M&lt;/strong&gt;apping.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Object:&lt;/strong&gt; Python objects&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Relational:&lt;/strong&gt; relational databases with tables and relationships&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mapping:&lt;/strong&gt; connecting Python objects to database tables&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;users&lt;/code&gt; table:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;username&lt;/th&gt;
&lt;th&gt;email&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;deepika&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:deepika@gmail.com"&gt;deepika@gmail.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;rahul&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:rahul@gmail.com"&gt;rahul@gmail.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;priya&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:priya@gmail.com"&gt;priya@gmail.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A Python class representing it:&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;User&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&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;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;


&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"deepika"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"deepika@gmail.com"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Python class  User   (id, username, email)
        ⇅  ORM mapping
Database table  users   (id, username, email)

One User object = one row in the users table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SQLAlchemy ORM makes this mapping automatic. Instead of writing SQL for every operation you write Python:&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;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"new_username"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SQLAlchemy detects the change and generates an SQL &lt;code&gt;UPDATE&lt;/code&gt; when the transaction is flushed.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. ⚔️ SQLAlchemy Core vs SQLAlchemy ORM
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;🎯 A very important interview and practical concept.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;SQLAlchemy offers two ways of working with a database. Both talk to the same database. The difference is &lt;em&gt;how you express operations&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  🅰️ SQLAlchemy Core
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;SQL expression toolkit&lt;/strong&gt;. You work with tables, columns, SQL expressions and statements, with no Python object per row.&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;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;select&lt;/span&gt;

&lt;span class="n"&gt;statement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;users_table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;statement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;all&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;ul&gt;
&lt;li&gt;
&lt;code&gt;select()&lt;/code&gt; creates a SELECT statement&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;users_table&lt;/code&gt; represents the database table&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;execute()&lt;/code&gt; runs it&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;all()&lt;/code&gt; retrieves all matching rows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Inserting with Core:&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;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;insert&lt;/span&gt;

&lt;span class="n"&gt;statement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;users_table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"deepika"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"deepika@gmail.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;statement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&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;h3&gt;
  
  
  🅱️ SQLAlchemy ORM
&lt;/h3&gt;

&lt;p&gt;You work with &lt;strong&gt;Python classes representing tables&lt;/strong&gt;:&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;statement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scalars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;statement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&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;username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&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;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;User&lt;/code&gt; is an ORM model, and SQLAlchemy returns &lt;code&gt;User&lt;/code&gt; &lt;strong&gt;objects&lt;/strong&gt; instead of raw column-oriented rows.&lt;/p&gt;

&lt;h3&gt;
  
  
  📊 Core vs ORM
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Core&lt;/th&gt;
&lt;th&gt;ORM&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Main approach&lt;/td&gt;
&lt;td&gt;Tables and SQL expressions&lt;/td&gt;
&lt;td&gt;Python classes and objects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Uses&lt;/td&gt;
&lt;td&gt;Table definitions&lt;/td&gt;
&lt;td&gt;ORM mapped classes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Returns&lt;/td&gt;
&lt;td&gt;Rows / column results&lt;/td&gt;
&lt;td&gt;ORM objects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Abstraction&lt;/td&gt;
&lt;td&gt;Lower-level&lt;/td&gt;
&lt;td&gt;Higher-level&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Relationships&lt;/td&gt;
&lt;td&gt;Explicit SQL joins&lt;/td&gt;
&lt;td&gt;ORM relationships supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best for&lt;/td&gt;
&lt;td&gt;SQL-heavy operations&lt;/td&gt;
&lt;td&gt;Application development&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Neither is universally better, and you can combine them. A FastAPI app might use the ORM for normal user and post operations, and Core for bulk inserts or complex reports. &lt;strong&gt;Learn the ORM thoroughly first&lt;/strong&gt;, while understanding Core enough to recognize its SQL expression API.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  🏗️ PART 2: Declarative Models
&lt;/h1&gt;

&lt;h2&gt;
  
  
  5. 📐 What are Declarative Models?
&lt;/h2&gt;

&lt;p&gt;In SQL you'd write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="kt"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="kc"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="kt"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="kc"&gt;NULL&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;p&gt;SQLAlchemy lets you define the same table with a &lt;strong&gt;Python class&lt;/strong&gt;. That's a &lt;strong&gt;declarative model&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 A &lt;strong&gt;declarative model&lt;/strong&gt; is a Python class that describes a database table, including its columns, data types, constraints and relationships.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 1: install&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip install sqlalchemy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: the imports&lt;/strong&gt;&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;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;orm&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;DeclarativeBase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mapped_column&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Import&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;String&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Defines a string database column&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DeclarativeBase&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Base class for declarative models&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Mapped&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Type annotation for mapped attributes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mapped_column&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Defines a mapped database column&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 3: create a &lt;code&gt;Base&lt;/code&gt; class&lt;/strong&gt;&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;Base&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DeclarativeBase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Base&lt;/code&gt; is the &lt;strong&gt;common parent class&lt;/strong&gt; for your models. Every model inherits from it:&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;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Comment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;.&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;p&gt;Because they inherit from &lt;code&gt;Base&lt;/code&gt;, SQLAlchemy knows they're ORM models and tracks their table definitions in shared &lt;strong&gt;metadata&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: create the &lt;code&gt;User&lt;/code&gt; model&lt;/strong&gt;&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;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;orm&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mapped_column&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;)&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="s2"&gt;"users"&lt;/span&gt;

    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&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="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&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;50&lt;/span&gt;&lt;span class="p"&gt;)&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="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&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;100&lt;/span&gt;&lt;span class="p"&gt;)&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="kc"&gt;False&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="kc"&gt;True&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;p&gt;Line by line:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;class User(Base)&lt;/code&gt;&lt;/strong&gt;: a Python class named &lt;code&gt;User&lt;/code&gt; that inherits from &lt;code&gt;Base&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;__tablename__ = "users"&lt;/code&gt;&lt;/strong&gt;: the actual database table name (class &lt;code&gt;User&lt;/code&gt; ↔ table &lt;code&gt;users&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;id: Mapped[int]&lt;/code&gt;&lt;/strong&gt;: &lt;code&gt;id&lt;/code&gt; is a mapped Python attribute of type &lt;code&gt;int&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;mapped_column(primary_key=True)&lt;/code&gt;&lt;/strong&gt;: defines the column and makes it the &lt;strong&gt;primary key&lt;/strong&gt;, which uniquely identifies each row. No two rows can share one&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;nullable=False&lt;/code&gt;&lt;/strong&gt;: the database doesn't allow &lt;code&gt;NULL&lt;/code&gt; here&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;unique=True&lt;/code&gt;&lt;/strong&gt;: duplicate values aren't allowed (two users can't share an email)&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  🎨 Common column types
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;SQLAlchemy type&lt;/th&gt;
&lt;th&gt;Python type&lt;/th&gt;
&lt;th&gt;Database meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Integer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;int&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Whole number&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;String(100)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;str&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Limited-length text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Text&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;str&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Longer text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Boolean&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bool&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;True / False&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DateTime&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;datetime&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Date and time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Float&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;float&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Floating-point number&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Numeric&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Decimal&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Precise numeric value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;JSON&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;dict&lt;/code&gt; / &lt;code&gt;list&lt;/code&gt; etc.&lt;/td&gt;
&lt;td&gt;JSON data&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;📝 Python annotations and database types are related, but not exactly the same thing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  6. 🔍 &lt;code&gt;Mapped&lt;/code&gt; and &lt;code&gt;mapped_column&lt;/code&gt; in Depth
&lt;/h2&gt;

&lt;p&gt;You'll see both &lt;strong&gt;everywhere&lt;/strong&gt; in SQLAlchemy 2.0. Why do both exist?&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;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&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;50&lt;/span&gt;&lt;span class="p"&gt;)&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="kc"&gt;False&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;p&gt;&lt;strong&gt;&lt;code&gt;Mapped[str]&lt;/code&gt;&lt;/strong&gt; is the &lt;strong&gt;Python-side&lt;/strong&gt; typing information. It tells developers what type to expect, tells IDEs what to suggest, lets static type checkers check, and tells SQLAlchemy the attribute is mapped.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;mapped_column()&lt;/code&gt;&lt;/strong&gt; describes the &lt;strong&gt;actual database column&lt;/strong&gt;: its type, primary key, foreign key, nullability, unique constraints and defaults.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 &lt;code&gt;Mapped[str]&lt;/code&gt; = Python-side declaration. &lt;code&gt;mapped_column()&lt;/code&gt; = database column configuration.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  ❓ Optional columns
&lt;/h3&gt;

&lt;p&gt;A user's bio isn't compulsory:&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;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;Optional&lt;/span&gt;

&lt;span class="n"&gt;bio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&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;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;300&lt;/span&gt;&lt;span class="p"&gt;)&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="kc"&gt;True&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;p&gt;In modern Python you can write:&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;bio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;None&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;300&lt;/span&gt;&lt;span class="p"&gt;)&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="kc"&gt;True&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;p&gt;The attribute can hold a string or &lt;code&gt;None&lt;/code&gt;, and the database column allows &lt;code&gt;NULL&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  🎚️ Default values
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;is_active&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;bool&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="kc"&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="kc"&gt;False&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;p&gt;You can also configure a &lt;strong&gt;database-side&lt;/strong&gt; default:&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;server_default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"true"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Who applies it?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;default&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;SQLAlchemy&lt;/strong&gt;, when constructing an INSERT without an explicit value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;server_default&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;The database itself&lt;/strong&gt;, when an INSERT omits that column&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h1&gt;
  
  
  🔗 PART 3: Relationships Between Models
&lt;/h1&gt;

&lt;h2&gt;
  
  
  7. 🗝️ What are Foreign Keys?
&lt;/h2&gt;

&lt;p&gt;A user creates multiple posts.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;users&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;username&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;deepika&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;rahul&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;posts&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;content&lt;/th&gt;
&lt;th&gt;user_id&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;Hello world&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;Beautiful day&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;Learning SQLAlchemy&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;user_id&lt;/code&gt; tells us who created the post. It's a &lt;strong&gt;foreign key&lt;/strong&gt; referencing the &lt;code&gt;id&lt;/code&gt; column of &lt;code&gt;users&lt;/code&gt;:&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;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;ForeignKey&lt;/span&gt;

&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&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="nc"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"users.id"&lt;/span&gt;&lt;span class="p"&gt;)&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="kc"&gt;False&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;p&gt;This creates a database-level reference and helps maintain &lt;strong&gt;referential integrity&lt;/strong&gt;. For example, you normally can't insert a post with &lt;code&gt;user_id=999&lt;/code&gt; if no such user exists (assuming foreign key enforcement is on).&lt;/p&gt;

&lt;h2&gt;
  
  
  8. 🔄 What is &lt;code&gt;relationship()&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;A foreign key connects &lt;em&gt;tables&lt;/em&gt;. But what if you want convenient access to related &lt;em&gt;Python objects&lt;/em&gt;? That's &lt;code&gt;relationship()&lt;/code&gt;:&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;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;orm&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mapped_column&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;relationship&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;)&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="s2"&gt;"users"&lt;/span&gt;

    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&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="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&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;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Post"&lt;/span&gt;&lt;span class="p"&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="n"&gt;back_populates&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;)&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="s2"&gt;"posts"&lt;/span&gt;

    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&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="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&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;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&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="nc"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"users.id"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"User"&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="n"&gt;back_populates&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"posts"&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;ul&gt;
&lt;li&gt;
&lt;code&gt;User.posts&lt;/code&gt; → a user can access their posts&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Post.author&lt;/code&gt; → a post can access its author
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&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;username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="k"&gt;in&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;posts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;One-to-many relationship

User (id = 1)  ──▶  Post 101
                ──▶  Post 102
                ──▶  Post 103

One User → many Posts. Each Post → one User.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🧠 &lt;code&gt;ForeignKey&lt;/code&gt; defines the &lt;strong&gt;database-level&lt;/strong&gt; relationship. &lt;code&gt;relationship()&lt;/code&gt; defines the convenient &lt;strong&gt;Python-level&lt;/strong&gt; object relationship. You usually need &lt;strong&gt;both&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  9. 🧩 Other Relationship Types
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;One-to-one:&lt;/strong&gt; one user has one profile.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User 1 ───── Profile 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;One-to-many:&lt;/strong&gt; one user has many posts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User 1 ───── Post 1
        ├─── Post 2
        └─── Post 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Many-to-many:&lt;/strong&gt; many users like many posts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User 1 ───┬── Post 1
          └── Post 2

User 2 ───┬── Post 1
          └── Post 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Many-to-many relationships generally use an &lt;strong&gt;association table&lt;/strong&gt;. For SocialFeed, a &lt;code&gt;likes&lt;/code&gt; table is a natural example:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;user_id&lt;/th&gt;
&lt;th&gt;post_id&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h1&gt;
  
  
  🔌 PART 4: Engine and Database Connections
&lt;/h1&gt;

&lt;h2&gt;
  
  
  10. ⚙️ What is a SQLAlchemy Engine?
&lt;/h2&gt;

&lt;p&gt;Before Python can execute queries, it needs a way to connect to PostgreSQL. SQLAlchemy uses an &lt;strong&gt;Engine&lt;/strong&gt;, the central database connection manager. It knows which database to connect to, which driver to use, how to create connections, and how to manage the &lt;strong&gt;connection pool&lt;/strong&gt;.&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;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;create_engine&lt;/span&gt;

&lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_engine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;"postgresql+psycopg://user:password@localhost/mydb"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;postgresql&lt;/code&gt; → database&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;psycopg&lt;/code&gt; → driver&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;user&lt;/code&gt; / &lt;code&gt;password&lt;/code&gt; → credentials&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;localhost&lt;/code&gt; → host&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mydb&lt;/code&gt; → database name&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;📝 Creating the engine doesn't necessarily open a connection right away. Connections are generally obtained when needed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  🏊 What is a connection pool?
&lt;/h3&gt;

&lt;p&gt;100 API requests each need the database. Would you create a brand-new connection every time? That's inefficient. SQLAlchemy uses &lt;strong&gt;connection pooling&lt;/strong&gt;: a collection of &lt;strong&gt;reusable connections&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FastAPI requests:  A · B · C
        ▼
SQLAlchemy Engine  → manages reusable connections
        ▼
Connection 1 · Connection 2 · Connection 3
        ▼
🐘 PostgreSQL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A connection is checked out when needed and returned to the pool when its transaction or usage ends. Pool settings include &lt;code&gt;pool_size&lt;/code&gt;, &lt;code&gt;max_overflow&lt;/code&gt;, &lt;code&gt;pool_timeout&lt;/code&gt; and &lt;code&gt;pool_recycle&lt;/code&gt;. You don't need to tune them yet. First understand &lt;strong&gt;why the pool exists&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  🧑‍💻 PART 5: Session and AsyncSession
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;🎯 One of the MOST IMPORTANT sections!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  11. 🗂️ What is a Session?
&lt;/h2&gt;

&lt;p&gt;A user calls &lt;code&gt;POST /posts&lt;/code&gt;. Your backend must connect to the database, insert the post, commit the transaction, and release resources.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 A &lt;strong&gt;Session&lt;/strong&gt; is a workspace for interacting with the database through the ORM. It manages ORM objects, database operations, pending changes, transactions and the identity map.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  🅰️ Creating a Session
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;orm&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sessionmaker&lt;/span&gt;

&lt;span class="nc"&gt;SessionLocal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sessionmaker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SessionLocal&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;p&gt;&lt;code&gt;sessionmaker&lt;/code&gt; is a &lt;strong&gt;factory&lt;/strong&gt; for creating sessions.&lt;/p&gt;

&lt;h3&gt;
  
  
  🅱️ Adding a record
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"deepika"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"deepika@gmail.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&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;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&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;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create a Python object.&lt;/strong&gt; It exists only in Python memory and isn't necessarily in PostgreSQL yet&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;session.add(user)&lt;/code&gt;.&lt;/strong&gt; SQLAlchemy tracks the object as &lt;em&gt;pending&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;session.commit()&lt;/code&gt;.&lt;/strong&gt; SQLAlchemy flushes pending changes and commits the transaction. Now the database stores the record&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  🅲 What is &lt;code&gt;flush()&lt;/code&gt;?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;🚨 A VERY important distinction!&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&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;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flush&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;p&gt;SQLAlchemy sends the &lt;code&gt;INSERT&lt;/code&gt; to the database &lt;strong&gt;within the current transaction&lt;/strong&gt;, but does &lt;strong&gt;NOT commit&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The SQL has been executed&lt;/li&gt;
&lt;li&gt;The transaction remains uncommitted&lt;/li&gt;
&lt;li&gt;Other transactions generally can't see the change&lt;/li&gt;
&lt;li&gt;You can still roll back
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&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;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="c1"&gt;# the generated ID is available!&lt;/span&gt;

&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rollback&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;p&gt;Why is &lt;code&gt;flush()&lt;/code&gt; useful? Sometimes you need the &lt;strong&gt;generated ID before committing&lt;/strong&gt;. For example, insert a user, get their ID, then create another record that references it, all in one transaction.&lt;/p&gt;

&lt;h3&gt;
  
  
  🅳 &lt;code&gt;commit()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Commits the current transaction and makes changes permanent (subject to database durability).&lt;/p&gt;

&lt;h3&gt;
  
  
  🅴 &lt;code&gt;rollback()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Something went wrong mid-way and you don't want a half-finished transaction:&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;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rollback&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;h3&gt;
  
  
  🅵 &lt;code&gt;close()&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&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;p&gt;Closes the Session and releases its transactional resources. It doesn't destroy your connection pool.&lt;/p&gt;

&lt;h3&gt;
  
  
  📋 Remember these five methods
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;add()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Track an object for persistence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;flush()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Send pending SQL changes without committing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;commit()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Commit the transaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rollback()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Undo uncommitted transaction changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;close()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Close the Session and release resources&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  12. 🪪 What is the Identity Map?
&lt;/h2&gt;

&lt;p&gt;A Session maintains an &lt;strong&gt;identity map&lt;/strong&gt;: within a Session, the same database identity is represented by the &lt;strong&gt;same Python object&lt;/strong&gt;.&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;user1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;user2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user1&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;user2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# typically True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the object is still in the identity map and hasn't expired, SQLAlchemy can return the same Python object. This keeps things consistent: you don't get two separate in-memory objects for the same row in the same Session.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📝 An identity map is &lt;strong&gt;not&lt;/strong&gt; a general-purpose query cache. A new query with different filters may still execute SQL.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  13. ⚡ What is &lt;code&gt;AsyncSession&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;FastAPI supports async endpoints:&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="nd"&gt;@app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/users"&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;get_users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;.&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;p&gt;But using a &lt;em&gt;synchronous&lt;/em&gt; database driver inside an async endpoint can &lt;strong&gt;block the event loop&lt;/strong&gt;. SQLAlchemy therefore provides &lt;code&gt;AsyncEngine&lt;/code&gt;, &lt;code&gt;AsyncSession&lt;/code&gt; and asynchronous execution.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Session&lt;/th&gt;
&lt;th&gt;AsyncSession&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Synchronous&lt;/td&gt;
&lt;td&gt;Asynchronous&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Session&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;AsyncSession&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;create_engine()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;create_async_engine()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;session.execute()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;await session.execute()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Blocking DB I/O&lt;/td&gt;
&lt;td&gt;Non-blocking DB I/O (with an async driver)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  🛠️ Creating an AsyncEngine
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip install sqlalchemy asyncpg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;asyncpg&lt;/strong&gt; is an asynchronous PostgreSQL driver.&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;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;asyncio&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;create_async_engine&lt;/span&gt;

&lt;span class="no"&gt;DATABASE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"postgresql+asyncpg://user:password@localhost/mydb"&lt;/span&gt;

&lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_async_engine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="no"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&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;ul&gt;
&lt;li&gt;
&lt;code&gt;postgresql&lt;/code&gt; → database type&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;asyncpg&lt;/code&gt; → async driver&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;echo=True&lt;/code&gt; → prints generated SQL to the console (great for development; &lt;strong&gt;don't leave sensitive SQL parameters exposed in production logs&lt;/strong&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🏭 Creating an AsyncSession factory
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;asyncio&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;AsyncSession&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;async_sessionmaker&lt;/span&gt;

&lt;span class="nc"&gt;AsyncSessionLocal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;async_sessionmaker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;AsyncSession&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;expire_on_commit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&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;p&gt;&lt;code&gt;expire_on_commit=False&lt;/code&gt; means ORM objects keep their already-loaded attribute values after a commit instead of automatically expiring. This matters in async code because accessing an expired attribute could require an implicit database load. It does &lt;strong&gt;not&lt;/strong&gt; automatically load relationships or guarantee every attribute is available.&lt;/p&gt;

&lt;h3&gt;
  
  
  ▶️ Using it
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;AsyncSessionLocal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scalars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;all&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;p&gt;Why &lt;code&gt;await&lt;/code&gt;? Executing a query requires I/O, which takes time. &lt;code&gt;await&lt;/code&gt; lets the coroutine suspend while waiting so other tasks can run on the event loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  14. 🚪 What is &lt;code&gt;async with&lt;/code&gt;?
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;AsyncSessionLocal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;.&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;p&gt;This is an &lt;strong&gt;asynchronous context manager&lt;/strong&gt;. It manages the Session's lifecycle, and the Session is closed when the block finishes. It's like:&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;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AsyncSessionLocal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Perform database operations&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&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;blockquote&gt;
&lt;p&gt;⚠️ Closing a session does &lt;strong&gt;not&lt;/strong&gt; automatically commit your changes. Commit explicitly when appropriate.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  🧪 A complete AsyncSession example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&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;with&lt;/span&gt; &lt;span class="nc"&gt;AsyncSessionLocal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"deepika"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"deepika@gmail.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;refresh&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;refresh()&lt;/code&gt;&lt;/strong&gt; reloads an ORM object's attributes from the database. The database may have generated values such as auto-generated IDs, server-side timestamps or database defaults, and refreshing retrieves them.&lt;/p&gt;

&lt;h2&gt;
  
  
  15. ⏳ What is &lt;code&gt;expire_on_commit=False&lt;/code&gt;?
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&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;username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, SQLAlchemy &lt;strong&gt;expires&lt;/strong&gt; ORM object attributes after a commit. Expiration means SQLAlchemy may need to &lt;strong&gt;reload them from the database&lt;/strong&gt; when you access them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In &lt;strong&gt;sync&lt;/strong&gt; code this reload happens transparently&lt;/li&gt;
&lt;li&gt;In &lt;strong&gt;async&lt;/strong&gt; code implicit database I/O is a problem, because it needs to be awaited&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So many async applications configure &lt;code&gt;expire_on_commit=False&lt;/code&gt;. But remember: it doesn't make unloaded relationships or deferred columns magically available.&lt;/p&gt;

&lt;h2&gt;
  
  
  16. 🚨 Don't Share a Session Across Concurrent Tasks
&lt;/h2&gt;

&lt;p&gt;A common beginner mistake is sharing &lt;strong&gt;one Session across multiple requests or tasks&lt;/strong&gt;. Don't!&lt;/p&gt;

&lt;p&gt;A SQLAlchemy Session is &lt;strong&gt;mutable and stateful&lt;/strong&gt;. It isn't safe to share concurrently between threads. Likewise, an &lt;code&gt;AsyncSession&lt;/code&gt; isn't safe to share across concurrent asyncio tasks:&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="c1"&gt;# ❌ Avoid: both functions use the SAME AsyncSession concurrently&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;do_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nf"&gt;do_another_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;)&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;p&gt;Use &lt;strong&gt;separate AsyncSession instances&lt;/strong&gt; for independent concurrent tasks. This is especially important in real FastAPI apps.&lt;/p&gt;




&lt;h1&gt;
  
  
  🏗️ PART 6: A Real Async Database Setup
&lt;/h1&gt;

&lt;h2&gt;
  
  
  17. 🗂️ The project structure
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;backend/
│
├── app/
│   ├── api/
│   ├── models/
│   │   ├── user.py
│   │   └── post.py
│   │
│   ├── repositories/
│   │   ├── user_repository.py
│   │   └── post_repository.py
│   │
│   ├── services/
│   ├── schemas/
│   ├── database.py
│   └── main.py
│
├── alembic/
├── alembic.ini
└── tests/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;database.py&lt;/code&gt;&lt;/strong&gt;&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;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;asyncio&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nc"&gt;AsyncSession&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;async_sessionmaker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;create_async_engine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="no"&gt;DATABASE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"postgresql+asyncpg://user:password@localhost/mydb"&lt;/span&gt;

&lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_async_engine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="no"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nc"&gt;AsyncSessionLocal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;async_sessionmaker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;AsyncSession&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;expire_on_commit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&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;p&gt;Now a dependency:&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;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_db&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&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;with&lt;/span&gt; &lt;span class="nc"&gt;AsyncSessionLocal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;yield&lt;/code&gt; provides the Session to FastAPI and keeps it open while the request uses it. When the request's dependency scope finishes, the context manager closes the Session.&lt;/p&gt;

&lt;h3&gt;
  
  
  ⚡ Using it in FastAPI
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;asyncio&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;AsyncSession&lt;/span&gt;


&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/users"&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;get_users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;AsyncSession&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;.&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;ul&gt;
&lt;li&gt;
&lt;code&gt;db&lt;/code&gt; → the variable holding the session&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AsyncSession&lt;/code&gt; → the expected type&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Depends(get_db)&lt;/code&gt; → FastAPI calls the dependency and injects the resulting session&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's &lt;strong&gt;dependency injection&lt;/strong&gt;. You don't manually create a session in every endpoint.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🏛️ &lt;strong&gt;Architecture point:&lt;/strong&gt; one request generally gets &lt;strong&gt;its own Session&lt;/strong&gt;. The Session should not be a global shared object.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  🔄 PART 7: Alembic Migrations
&lt;/h1&gt;

&lt;h2&gt;
  
  
  18. 🤔 What is Alembic?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;Alembic&lt;/strong&gt; is a database migration tool designed to work with SQLAlchemy. Its purpose is to manage &lt;strong&gt;changes to your database schema over time&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A &lt;strong&gt;schema&lt;/strong&gt; describes the structure of your database: tables, columns, data types, primary keys, foreign keys, constraints and indexes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Version 1: users(id, username)
        ▼  new feature added
Version 2: users(id, username, email)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You could run this by hand:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="k"&gt;COLUMN&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="kt"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&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;p&gt;But after hundreds of changes over several months, how do you track:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which changes have been made?&lt;/li&gt;
&lt;li&gt;Which have been applied to &lt;strong&gt;production&lt;/strong&gt;?&lt;/li&gt;
&lt;li&gt;Which are pending?&lt;/li&gt;
&lt;li&gt;How do you &lt;strong&gt;reverse&lt;/strong&gt; one?&lt;/li&gt;
&lt;li&gt;How do your &lt;strong&gt;teammates&lt;/strong&gt; apply the same changes?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Alembic solves this by keeping a &lt;strong&gt;history of schema changes through migration files&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  19. 📜 What is a migration?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 A &lt;strong&gt;migration&lt;/strong&gt; is a Python file containing instructions to change your database schema.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example, to add &lt;code&gt;email&lt;/code&gt; to &lt;code&gt;users&lt;/code&gt;, Alembic creates a migration with:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Upgrade:&lt;/strong&gt; add the &lt;code&gt;email&lt;/code&gt; column&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Downgrade:&lt;/strong&gt; remove the &lt;code&gt;email&lt;/code&gt; column&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  🌿 Migrations are like Git commits
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Git&lt;/th&gt;
&lt;th&gt;Alembic&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Commit&lt;/td&gt;
&lt;td&gt;Migration revision&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Commit history&lt;/td&gt;
&lt;td&gt;Migration history&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Checkout / revision navigation&lt;/td&gt;
&lt;td&gt;Migration version tracking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apply code changes&lt;/td&gt;
&lt;td&gt;Apply schema changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Revert a commit&lt;/td&gt;
&lt;td&gt;Write/run a downgrade where appropriate&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Migrations can modify &lt;strong&gt;real production data and structures&lt;/strong&gt;. Downgrades aren't always perfectly reversible.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  20. 📥 Installing Alembic
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip install alembic
alembic init alembic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;backend/
│
├── app/
│   ├── models/
│   ├── database.py
│   └── main.py
│
├── alembic/
│   ├── versions/
│   ├── env.py
│   └── script.py.mako
│
└── alembic.ini
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File / folder&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;alembic.ini&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Alembic configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;alembic/env.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Environment and migration configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;alembic/versions/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stores migration files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;script.py.mako&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Template used to generate migration files&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;env.py&lt;/code&gt;&lt;/strong&gt; is one of the most important Alembic files. It tells Alembic which database to connect to, where the SQLAlchemy model metadata is, whether migrations run synchronously or asynchronously, and how to compare the existing schema with your model definitions.&lt;/p&gt;

&lt;h2&gt;
  
  
  21. 📄 What is &lt;code&gt;alembic.ini&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;Alembic's main configuration file, containing the migration script location, database connection configuration and logging settings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="k"&gt;[alembic]&lt;/span&gt;

&lt;span class="na"&gt;script_location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;here&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;alembic&lt;/span&gt;

&lt;span class="na"&gt;prepend_sys_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="na"&gt;sqlalchemy.url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;postgresql&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//user:password@localhost/mydb&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But our app uses &lt;code&gt;postgresql+asyncpg://...&lt;/code&gt; and is asynchronous. We can configure the migration environment to use an &lt;strong&gt;async SQLAlchemy engine&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🔐 In a real app, &lt;strong&gt;don't hardcode credentials&lt;/strong&gt; in a committed config file. Read them from environment variables or your settings module:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&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;config&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;

&lt;span class="n"&gt;database_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="no"&gt;DATABASE_URL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  22. 🎯 What is &lt;code&gt;target_metadata&lt;/code&gt;?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;🚨 A VERY important concept!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Remember our &lt;code&gt;Base&lt;/code&gt; class?&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;Base&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DeclarativeBase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SQLAlchemy stores information about every model that inherits from it in &lt;code&gt;Base.metadata&lt;/code&gt;, which holds the &lt;strong&gt;table definitions&lt;/strong&gt; SQLAlchemy knows about:&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;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;)&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="s2"&gt;"users"&lt;/span&gt;

    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&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="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alembic needs to compare two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Your current &lt;strong&gt;SQLAlchemy model definitions&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Your &lt;strong&gt;actual database schema&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The model metadata provides the first side of that comparison. Inside &lt;code&gt;alembic/env.py&lt;/code&gt;:&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;target_metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ Alembic must also be able to &lt;strong&gt;import your model classes&lt;/strong&gt; so they're registered in &lt;code&gt;Base.metadata&lt;/code&gt;. If you forget to import a model, its table may be missing from the metadata, and autogenerate might not detect it.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  🧭 PART 8: The Alembic Migration Workflow
&lt;/h1&gt;

&lt;h2&gt;
  
  
  23. 🤖 What is Autogenerate?
&lt;/h2&gt;

&lt;p&gt;You have a &lt;code&gt;User&lt;/code&gt; model, and the table already exists in your database:&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;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;)&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="s2"&gt;"users"&lt;/span&gt;

    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&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="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&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;50&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;p&gt;Now you add an &lt;code&gt;email&lt;/code&gt; column to the model:&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;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;)&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="s2"&gt;"users"&lt;/span&gt;

    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&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="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&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;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&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;100&lt;/span&gt;&lt;span class="p"&gt;)&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="kc"&gt;False&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;blockquote&gt;
&lt;p&gt;🚨 &lt;strong&gt;The key point:&lt;/strong&gt; PostgreSQL doesn't change just because you edited Python code! &lt;strong&gt;SQLAlchemy models describe the desired structure. Alembic migrations change the actual database structure.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;alembic revision --autogenerate -m &lt;span class="s2"&gt;"add email to users"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;alembic&lt;/code&gt; → the command-line tool&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;revision&lt;/code&gt; → create a new migration revision&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--autogenerate&lt;/code&gt; → compare database schema with model metadata and propose changes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-m&lt;/code&gt; → a descriptive message&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Alembic generates a file in &lt;code&gt;alembic/versions/&lt;/code&gt;:&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="s2"&gt;"""add email to users"""&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;sa&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;alembic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;

&lt;span class="n"&gt;revision&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"abc123"&lt;/span&gt;
&lt;span class="n"&gt;down_revision&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"previous_revision"&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;upgrade&lt;/span&gt;&lt;span class="p"&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;op&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;sa&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;sa&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="n"&gt;length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&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="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;downgrade&lt;/span&gt;&lt;span class="p"&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;op&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drop_column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;revision&lt;/code&gt;&lt;/strong&gt; is the migration's unique ID&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;down_revision&lt;/code&gt;&lt;/strong&gt; identifies the migration that came before it, which establishes the order:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Migration A
    ▼
Migration B
    ▼
Migration C      (C's down_revision points to B)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;upgrade()&lt;/code&gt;&lt;/strong&gt; contains the instructions to move the database &lt;strong&gt;forward&lt;/strong&gt; (add the column)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;downgrade()&lt;/code&gt;&lt;/strong&gt; contains instructions to &lt;strong&gt;reverse&lt;/strong&gt; it (drop the column)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Old schema: users(id, username)
      │  upgrade()  ──▶  move forward
      │  downgrade() ◀──  move backward
New schema: users(id, username, email)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ Adding a &lt;code&gt;nullable=False&lt;/code&gt; column to a table that already has rows needs a default (or a multi-step migration), or PostgreSQL will reject it. Always review the generated file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  24. ⬆️ &lt;code&gt;alembic upgrade head&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Creating a migration file doesn't apply it. You must run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;alembic upgrade head
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;upgrade&lt;/code&gt; → apply migrations in the forward direction&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;head&lt;/code&gt; → move to the &lt;strong&gt;latest&lt;/strong&gt; available migration revision&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have migrations A, B and C and your database is at A, then &lt;code&gt;alembic upgrade head&lt;/code&gt; applies B and C.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 &lt;code&gt;revision --autogenerate&lt;/code&gt; &lt;strong&gt;creates&lt;/strong&gt; a migration file. &lt;code&gt;upgrade head&lt;/code&gt; &lt;strong&gt;applies&lt;/strong&gt; it to the database. They're two separate steps.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  25. ⬇️ &lt;code&gt;alembic downgrade&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Applied a migration by accident?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;alembic downgrade -1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This moves back one revision using the current migration's &lt;code&gt;downgrade()&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before:  A → B → C
                 ^ current DB

After:   A → B → C
             ^ current DB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;alembic downgrade abc123    &lt;span class="c"&gt;# to a specific revision&lt;/span&gt;
alembic downgrade base      &lt;span class="c"&gt;# back to the original empty base&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🚨 A downgrade may delete columns, tables or data. &lt;strong&gt;Never blindly downgrade a production database.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  26. 📋 Alembic Command Cheat Sheet
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;alembic init alembic&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Initialize Alembic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;alembic revision -m "message"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create an empty migration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;alembic revision --autogenerate -m "message"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generate a migration from detected schema differences&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;alembic upgrade head&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Apply all pending migrations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;alembic upgrade +1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Apply the next migration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;alembic downgrade -1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reverse one migration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;alembic downgrade base&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reverse all migrations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;alembic current&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the current database revision&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;alembic history&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show migration history&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;alembic heads&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show migration head revisions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;alembic stamp head&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mark the database at head without executing migrations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  🏷️ What is &lt;code&gt;stamp&lt;/code&gt;?
&lt;/h3&gt;

&lt;p&gt;Say you already have a database with tables, but Alembic isn't tracking its migration history. You can tell Alembic the database should be considered to be at a particular revision:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;alembic stamp head
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This updates Alembic's version tracking &lt;strong&gt;without running the migration scripts&lt;/strong&gt;. It does &lt;strong&gt;NOT&lt;/strong&gt; create missing tables or modify columns. Only use it when you've verified the actual schema matches the revision you're stamping.&lt;/p&gt;

&lt;h2&gt;
  
  
  27. 📒 What is &lt;code&gt;alembic_version&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;How does Alembic know which migrations have already been applied? It uses a special table called &lt;code&gt;alembic_version&lt;/code&gt;. For a simple single-head history it holds the current revision ID:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;version_num&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;abc123&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;When you run &lt;code&gt;alembic upgrade head&lt;/code&gt;, Alembic:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Checks the current revision&lt;/li&gt;
&lt;li&gt;Determines which migrations are pending&lt;/li&gt;
&lt;li&gt;Executes the required &lt;code&gt;upgrade&lt;/code&gt; functions&lt;/li&gt;
&lt;li&gt;Updates the version tracking table&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That prevents applied migrations from re-running. (With multiple heads, the tracking can involve multiple revision IDs.)&lt;/p&gt;

&lt;h2&gt;
  
  
  28. 🔬 How does Autogenerate actually work?
&lt;/h2&gt;

&lt;p&gt;Your database has &lt;code&gt;users(id, username)&lt;/code&gt;, but your Python model defines &lt;code&gt;users(id, username, email)&lt;/code&gt;. Alembic performs a &lt;strong&gt;schema comparison&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SQLAlchemy metadata:  id, username, email
Actual database:      id, username
        ▼
Schema comparison
        ▼
Detected difference: new column `email VARCHAR(100)`
        ▼
Alembic proposes a migration operation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Autogenerate is not perfect.&lt;/strong&gt; It detects many common differences but can't understand every database change or your business intention:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Some table renames appear as a &lt;strong&gt;drop + create&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Some column renames appear as &lt;strong&gt;drop + add&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Data transformations need &lt;strong&gt;manual logic&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Some constraint and database-specific changes need manual handling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Always inspect the generated migration before applying it.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  ⚡ PART 9: Async Alembic Configuration
&lt;/h1&gt;

&lt;h2&gt;
  
  
  29. 🤝 How does Alembic work with asyncpg?
&lt;/h2&gt;

&lt;p&gt;Your FastAPI app uses &lt;code&gt;postgresql+asyncpg://...&lt;/code&gt;, but Alembic's migration environment must be configured appropriately. A common setup uses &lt;code&gt;async_engine_from_config&lt;/code&gt; and runs Alembic's synchronous migration operations through &lt;code&gt;connection.run_sync()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Inside &lt;code&gt;alembic/env.py&lt;/code&gt;:&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;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;

&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;alembic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;asyncio&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;async_engine_from_config&lt;/span&gt;

&lt;span class="k"&gt;from&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;database&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;Base&lt;/span&gt;
&lt;span class="k"&gt;from&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;models&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;  &lt;span class="c1"&gt;# noqa: F401  (registers models)&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;

&lt;span class="n"&gt;target_metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why import &lt;code&gt;user&lt;/code&gt; and &lt;code&gt;post&lt;/code&gt;? So SQLAlchemy &lt;strong&gt;registers these models&lt;/strong&gt; in &lt;code&gt;Base.metadata&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📝 This example imports &lt;code&gt;Base&lt;/code&gt; from &lt;code&gt;app.database&lt;/code&gt;. Import it from wherever you defined your &lt;code&gt;Base&lt;/code&gt; class.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The synchronous migration operation:&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;do_run_migrations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;target_metadata&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;target_metadata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;begin_transaction&lt;/span&gt;&lt;span class="p"&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;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_migrations&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;p&gt;Getting the asynchronous connection:&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;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_async_migrations&lt;/span&gt;&lt;span class="p"&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;connectable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;async_engine_from_config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_section&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config_ini_section&lt;/span&gt;&lt;span class="p"&gt;)&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="s2"&gt;"sqlalchemy."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;poolclass&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NullPool&lt;/span&gt;&lt;span class="p"&gt;,&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;with&lt;/span&gt; &lt;span class="n"&gt;connectable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_sync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;do_run_migrations&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;connectable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispose&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;p&gt;Finally:&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;run_migrations_online&lt;/span&gt;&lt;span class="p"&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;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;run_async_migrations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nf"&gt;run_migrations_online&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;p&gt;&lt;strong&gt;Why &lt;code&gt;run_sync()&lt;/code&gt; if we're using asyncpg?&lt;/strong&gt; Alembic's migration context and operations are designed around synchronous-style operations. &lt;code&gt;run_sync()&lt;/code&gt; is a &lt;strong&gt;bridge&lt;/strong&gt;: it lets Alembic run its synchronous migration functions on the underlying async connection through SQLAlchemy's async integration. It doesn't mean your app switched to a synchronous PostgreSQL driver.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📝 This is a simplified &lt;strong&gt;online-migration&lt;/strong&gt; setup. Production Alembic environments may add offline migrations, logging, URL loading and transaction handling.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  🚫 What is &lt;code&gt;pool.NullPool&lt;/code&gt;?
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;NullPool&lt;/code&gt; doesn't keep a pool of reusable connections. For a migration command that runs and exits, that's sensible: it opens a connection when needed and closes it when finished. Your FastAPI app can still use its normal connection pool.&lt;/p&gt;




&lt;h1&gt;
  
  
  🏛️ PART 10: The Repository Pattern
&lt;/h1&gt;

&lt;h2&gt;
  
  
  30. 🤔 What is the Repository Pattern?
&lt;/h2&gt;

&lt;p&gt;A user clicks &lt;strong&gt;Login&lt;/strong&gt; in SocialFeed and your backend must find them in PostgreSQL. Where should that query live? You could write it directly in the route:&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="nd"&gt;@app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/users/{user_id}"&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;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;AsyncSession&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scalar_one_or_none&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That works. But with 50 endpoints, many needing to retrieve users, you'd repeat it, which causes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔁 Duplicate code&lt;/li&gt;
&lt;li&gt;😩 Difficult maintenance&lt;/li&gt;
&lt;li&gt;🧶 Database logic mixed with API logic&lt;/li&gt;
&lt;li&gt;🧪 Difficult testing&lt;/li&gt;
&lt;li&gt;♻️ Harder code reuse&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 A &lt;strong&gt;repository&lt;/strong&gt; is a layer responsible for communicating with the database and performing data-access operations. Think of it as the &lt;strong&gt;database specialist&lt;/strong&gt; in your backend.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❌ Without the Repository Pattern
FastAPI Route: SQL queries + business logic + response handling, all mixed together
        ▼
🐘 PostgreSQL

✅ With the Repository Pattern
🛣️ FastAPI Route     → receives the HTTP request
        ▼
🧠 Service Layer     → business rules and application logic
        ▼
📚 Repository Layer  → database queries and data access
        ▼
🐘 PostgreSQL        → stores data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🛣️ Layer 1: API / Router
&lt;/h3&gt;

&lt;p&gt;Receives HTTP requests, validates path/query parameters, obtains dependencies, calls service functions and returns HTTP responses. It shouldn't contain complicated database logic.&lt;/p&gt;

&lt;h3&gt;
  
  
  🧠 Layer 2: Service
&lt;/h3&gt;

&lt;p&gt;Contains business logic: check whether a user exists, check permissions, enforce application rules, coordinate multiple repositories, decide what happens next.&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;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user_profile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;user_repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_by_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;UserNotFoundError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deciding how the app behaves when a user doesn't exist is a &lt;strong&gt;business/application-level&lt;/strong&gt; responsibility.&lt;/p&gt;

&lt;h3&gt;
  
  
  📚 Layer 3: Repository
&lt;/h3&gt;

&lt;p&gt;Handles database access:&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;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_by_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;AsyncSession&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scalar_one_or_none&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;p&gt;It only retrieves a user. It doesn't decide the HTTP status code, how to authenticate someone, or what message to show.&lt;/p&gt;

&lt;h2&gt;
  
  
  31. 🧵 A Complete Repository Example
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app/
│
├── api/
│   └── user_routes.py
│
├── services/
│   └── user_service.py
│
├── repositories/
│   └── user_repository.py
│
├── models/
│   └── user.py
│
└── database.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 1: &lt;code&gt;repositories/user_repository.py&lt;/code&gt;&lt;/strong&gt;&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;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;select&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;asyncio&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;AsyncSession&lt;/span&gt;

&lt;span class="k"&gt;from&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;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;User&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;get_by_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;AsyncSession&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scalar_one_or_none&lt;/span&gt;&lt;span class="p"&gt;(&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;get_by_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;AsyncSession&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scalar_one_or_none&lt;/span&gt;&lt;span class="p"&gt;(&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;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;AsyncSession&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;get_by_id()&lt;/code&gt; retrieves a user by ID&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get_by_email()&lt;/code&gt; retrieves a user by email&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;create()&lt;/code&gt; creates a new &lt;code&gt;User&lt;/code&gt; object and adds it to the Session&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Notice &lt;code&gt;await db.flush()&lt;/code&gt;. &lt;strong&gt;Why not commit here?&lt;/strong&gt; Because the repository doesn't necessarily own the transaction. The service (or a higher-level unit of work) may need several operations together: create a user, create their profile, create their default settings. They might need to &lt;strong&gt;succeed or fail together&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: &lt;code&gt;services/user_service.py&lt;/code&gt;&lt;/strong&gt;&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;from&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;repositories&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;user_repository&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;get_user_profile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;user_repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_by_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;user&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;register_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;existing_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;user_repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_by_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;existing_user&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Email already registered"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;user_repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The service checks whether the email exists, decides whether registration proceeds, calls the repository, and commits.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ This is a teaching example. In production, the &lt;strong&gt;database unique constraint must still enforce email uniqueness&lt;/strong&gt;, because two simultaneous registrations can race. You'd catch the resulting &lt;code&gt;IntegrityError&lt;/code&gt;, roll back, and translate it into an appropriate application error.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 3: &lt;code&gt;api/user_routes.py&lt;/code&gt;&lt;/strong&gt;&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;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;APIRouter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;HTTPException&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;asyncio&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;AsyncSession&lt;/span&gt;

&lt;span class="k"&gt;from&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;database&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;get_db&lt;/span&gt;
&lt;span class="k"&gt;from&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;services&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;user_service&lt;/span&gt;

&lt;span class="n"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;APIRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/users/{user_id}"&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;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;AsyncSession&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&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;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;user_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_user_profile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;HTTPException&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="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"User not found"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The route doesn't know how the SQL query works. It asks the service for a user. That's clean separation of responsibilities. ✨&lt;/p&gt;

&lt;h2&gt;
  
  
  32. 🤔 Why Shouldn't Repositories Usually Commit?
&lt;/h2&gt;

&lt;p&gt;Your app performs three operations: &lt;code&gt;create_user()&lt;/code&gt;, &lt;code&gt;create_profile()&lt;/code&gt;, &lt;code&gt;create_settings()&lt;/code&gt;. If each repository function commits on its own and:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ User creation succeeds&lt;/li&gt;
&lt;li&gt;✅ Profile creation succeeds&lt;/li&gt;
&lt;li&gt;❌ Settings creation fails&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;...you've got a &lt;strong&gt;partially completed registration&lt;/strong&gt;. Use one transaction instead:&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;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;begin&lt;/span&gt;&lt;span class="p"&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;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;user_repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"deepika"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"deepika@gmail.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;profile_repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;settings_repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;async with db.begin()&lt;/code&gt; manages a transaction:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the block completes successfully, the transaction &lt;strong&gt;commits&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;If an exception occurs, the transaction is &lt;strong&gt;rolled back&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That makes several related operations &lt;strong&gt;atomic&lt;/strong&gt;: all succeed together, or the transaction is rolled back.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📝 &lt;code&gt;db.begin()&lt;/code&gt; expects the Session not to have already begun a transaction (for example, from an earlier query on the same Session). The exact transaction boundary is an architectural decision. The key is &lt;strong&gt;clear ownership&lt;/strong&gt;, rather than committing randomly inside every repository method.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  🔎 PART 11: Important SQLAlchemy Query Methods
&lt;/h1&gt;

&lt;h2&gt;
  
  
  33. 🆚 &lt;code&gt;execute()&lt;/code&gt; vs &lt;code&gt;scalars()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;You'll see these constantly in SQLAlchemy 2.0.&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;statement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;statement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;execute()&lt;/code&gt; returns a SQLAlchemy &lt;strong&gt;&lt;code&gt;Result&lt;/code&gt;&lt;/strong&gt; object containing rows from the query.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🅰️ &lt;code&gt;result.scalars()&lt;/code&gt;&lt;/strong&gt; extracts the &lt;strong&gt;first selected element&lt;/strong&gt; of each row. Since we selected &lt;code&gt;User&lt;/code&gt;, it returns &lt;code&gt;User&lt;/code&gt; ORM objects:&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;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scalars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;all&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;p&gt;&lt;strong&gt;🅱️ &lt;code&gt;result.scalar_one_or_none()&lt;/code&gt;&lt;/strong&gt; is for when you expect zero or one result:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No matching result → &lt;code&gt;None&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Exactly one result → that value&lt;/li&gt;
&lt;li&gt;More than one → raises &lt;code&gt;MultipleResultsFound&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;🅲 &lt;code&gt;result.scalar_one()&lt;/code&gt;&lt;/strong&gt; expects &lt;strong&gt;exactly one&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No result → raises &lt;code&gt;NoResultFound&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;One result → returns it&lt;/li&gt;
&lt;li&gt;Multiple → raises &lt;code&gt;MultipleResultsFound&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;🅳 &lt;code&gt;result.scalars().first()&lt;/code&gt;&lt;/strong&gt; returns the first result or &lt;code&gt;None&lt;/code&gt;, and doesn't enforce uniqueness.&lt;/p&gt;

&lt;h3&gt;
  
  
  📋 Quick comparison
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Expected result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;all()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;All rows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;first()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;First row or &lt;code&gt;None&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;scalar_one()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Exactly one scalar&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;scalar_one_or_none()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Zero or one scalar&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;scalars().all()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;All first-column values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;scalars().first()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;First scalar value&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  🔑 What is &lt;code&gt;session.get()&lt;/code&gt;?
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Retrieves a &lt;code&gt;User&lt;/code&gt; by &lt;strong&gt;primary key&lt;/strong&gt;. It's especially convenient for primary-key lookups, and it can use the Session's identity map, possibly avoiding another SQL query if the object is already present and valid in that Session.&lt;/p&gt;

&lt;h2&gt;
  
  
  34. 🎛️ Filtering, Ordering and Limiting
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Filtering&lt;/strong&gt;&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;statement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"deepika"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'deepika'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Multiple conditions&lt;/strong&gt;&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;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;and_&lt;/span&gt;

&lt;span class="n"&gt;statement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;and_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_active&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"deepika"&lt;/span&gt;&lt;span class="p"&gt;,&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;p&gt;or simply:&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;statement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_active&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"deepika"&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;p&gt;&lt;strong&gt;Ordering&lt;/strong&gt;&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;statement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;order_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;statement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;order_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;desc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# descending&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Limiting&lt;/strong&gt;&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;statement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;limit&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Offset&lt;/strong&gt;&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;statement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;offset&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;limit&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the basic idea behind &lt;strong&gt;offset pagination&lt;/strong&gt;. For a SocialFeed project, also learn &lt;strong&gt;cursor pagination&lt;/strong&gt;, which is often preferable for large, changing feeds.&lt;/p&gt;




&lt;h1&gt;
  
  
  😴 PART 12: Relationships and Async Loading
&lt;/h1&gt;

&lt;h2&gt;
  
  
  35. 🐢 What is Lazy Loading?
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;User&lt;/code&gt; is retrieved. But what about &lt;code&gt;user.posts&lt;/code&gt;? Are the posts automatically fetched? &lt;strong&gt;Not necessarily.&lt;/strong&gt; The default relationship strategy is generally &lt;code&gt;lazy="select"&lt;/code&gt;, meaning related records load &lt;strong&gt;when the relationship is accessed&lt;/strong&gt;. That's &lt;strong&gt;lazy loading&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Traditional implicit lazy loading can cause problems in &lt;strong&gt;async&lt;/strong&gt; SQLAlchemy, because accessing a relationship might require database I/O, and normal attribute access can't use &lt;code&gt;await&lt;/code&gt;. So async apps should usually &lt;strong&gt;plan relationship loading explicitly&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  🅰️ &lt;code&gt;selectinload()&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;select&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;orm&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;selectinload&lt;/span&gt;

&lt;span class="n"&gt;statement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;options&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;selectinload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;statement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scalars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;all&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;ol&gt;
&lt;li&gt;SQLAlchemy retrieves the users&lt;/li&gt;
&lt;li&gt;It retrieves their related posts with an additional query, generally using an &lt;code&gt;IN&lt;/code&gt; condition&lt;/li&gt;
&lt;li&gt;It populates the &lt;code&gt;posts&lt;/code&gt; relationships&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now you can access the already-loaded relationships without triggering implicit lazy loading.&lt;/p&gt;

&lt;h3&gt;
  
  
  🅱️ &lt;code&gt;joinedload()&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;orm&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;joinedload&lt;/span&gt;

&lt;span class="n"&gt;statement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;options&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;joinedload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;posts&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;p&gt;Loads related objects using a &lt;strong&gt;SQL JOIN&lt;/strong&gt;. When loading &lt;strong&gt;collections&lt;/strong&gt; this way, you generally need:&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;statement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scalars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;all&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;p&gt;Why &lt;code&gt;unique()&lt;/code&gt;? Joining a user with multiple posts can produce &lt;strong&gt;repeated rows for the same user&lt;/strong&gt; in the SQL result, and &lt;code&gt;unique()&lt;/code&gt; deduplicates ORM identities.&lt;/p&gt;

&lt;h3&gt;
  
  
  ⚖️ &lt;code&gt;selectinload()&lt;/code&gt; vs &lt;code&gt;joinedload()&lt;/code&gt;
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;&lt;code&gt;selectinload&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;joinedload&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Query strategy&lt;/td&gt;
&lt;td&gt;Separate SELECT&lt;/td&gt;
&lt;td&gt;SQL JOIN&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Collection loading&lt;/td&gt;
&lt;td&gt;Often efficient&lt;/td&gt;
&lt;td&gt;Can multiply result rows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Main advantage&lt;/td&gt;
&lt;td&gt;Avoids large joined result sets&lt;/td&gt;
&lt;td&gt;Related data in one SQL statement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical use&lt;/td&gt;
&lt;td&gt;One-to-many collections&lt;/td&gt;
&lt;td&gt;Many-to-one or small related loads&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Neither is universally better. Choose based on the query and the data size.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  🧠 PART 13: Important Database Concepts
&lt;/h1&gt;

&lt;h2&gt;
  
  
  36. 🔁 What is a Transaction?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 A &lt;strong&gt;transaction&lt;/strong&gt; is a group of database operations treated as &lt;strong&gt;one logical unit of work&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A user transfers ₹500 from Account A to Account B:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Deduct ₹500 from A&lt;/li&gt;
&lt;li&gt;Add ₹500 to B&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What if the first succeeds but the second fails? Money would disappear! Transactions prevent partial updates: &lt;strong&gt;both commit, or both roll back&lt;/strong&gt;. SQLAlchemy manages transactions through Session methods and transaction context managers.&lt;/p&gt;

&lt;h2&gt;
  
  
  37. 🧪 What is ACID?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;A&lt;/strong&gt;tomicity&lt;/td&gt;
&lt;td&gt;All or nothing&lt;/td&gt;
&lt;td&gt;Both transfer operations succeed or neither does&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;C&lt;/strong&gt;onsistency&lt;/td&gt;
&lt;td&gt;Valid database rules remain satisfied&lt;/td&gt;
&lt;td&gt;Foreign keys and constraints are respected&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;I&lt;/strong&gt;solation&lt;/td&gt;
&lt;td&gt;Concurrent transactions are controlled&lt;/td&gt;
&lt;td&gt;One transaction shouldn't see another's uncommitted changes under normal isolation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;D&lt;/strong&gt;urability&lt;/td&gt;
&lt;td&gt;Committed changes persist&lt;/td&gt;
&lt;td&gt;A successful transfer remains stored&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  38. 🚧 What is a Constraint?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 A &lt;strong&gt;constraint&lt;/strong&gt; is a database rule that controls what data can be stored.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Primary key&lt;/span&gt;
&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&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="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Unique constraint&lt;/span&gt;
&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&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;100&lt;/span&gt;&lt;span class="p"&gt;)&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="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Not null&lt;/span&gt;
&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&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;50&lt;/span&gt;&lt;span class="p"&gt;)&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="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Foreign key&lt;/span&gt;
&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&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="nc"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"users.id"&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;p&gt;Why do constraints matter? &lt;strong&gt;Application-level validation alone can't guarantee database consistency.&lt;/strong&gt; Two concurrent requests could both check whether an email exists, both find it doesn't, and both try to insert it. A &lt;strong&gt;unique database constraint&lt;/strong&gt; ensures only one succeeds.&lt;/p&gt;

&lt;h2&gt;
  
  
  39. ⚡ What is an Index?
&lt;/h2&gt;

&lt;p&gt;Your &lt;code&gt;users&lt;/code&gt; table has 10 million records and you run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'deepika@gmail.com'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without an appropriate index, PostgreSQL may need to scan many rows.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 An &lt;strong&gt;index&lt;/strong&gt; is a data structure that helps the database find matching records efficiently.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&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;100&lt;/span&gt;&lt;span class="p"&gt;)&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="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&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;p&gt;Or at table level:&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;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;Index&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;)&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="s2"&gt;"users"&lt;/span&gt;

    &lt;span class="c1"&gt;# columns ...&lt;/span&gt;

    &lt;span class="n"&gt;__table_args__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nc"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"ix_users_username"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;)&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;ul&gt;
&lt;li&gt;✅ Indexes can speed up certain reads&lt;/li&gt;
&lt;li&gt;💾 They consume storage&lt;/li&gt;
&lt;li&gt;🐌 They add overhead to inserts, updates and deletes&lt;/li&gt;
&lt;li&gt;🎯 Not every column needs one&lt;/li&gt;
&lt;li&gt;A unique constraint commonly creates a supporting unique index in PostgreSQL&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  ⚠️ PART 14: Common Mistakes and Best Practices
&lt;/h1&gt;

&lt;h2&gt;
  
  
  40. ❌ Common SQLAlchemy Mistakes
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mistake&lt;/th&gt;
&lt;th&gt;Correct approach&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sharing one &lt;code&gt;AsyncSession&lt;/code&gt; between concurrent tasks&lt;/td&gt;
&lt;td&gt;Use separate sessions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Forgetting &lt;code&gt;await&lt;/code&gt; on async database calls&lt;/td&gt;
&lt;td&gt;Await asynchronous operations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Forgetting to commit&lt;/td&gt;
&lt;td&gt;Commit at the intended transaction boundary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Assuming &lt;code&gt;flush()&lt;/code&gt; commits&lt;/td&gt;
&lt;td&gt;Flush only sends SQL; it doesn't commit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Forgetting to roll back after a transaction failure&lt;/td&gt;
&lt;td&gt;Roll back or use transaction context managers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Putting all queries in routers&lt;/td&gt;
&lt;td&gt;Use repositories&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Assuming model changes update PostgreSQL&lt;/td&gt;
&lt;td&gt;Generate and apply migrations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Blindly applying autogenerated migrations&lt;/td&gt;
&lt;td&gt;Review them first&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Accessing unloaded relationships in async code&lt;/td&gt;
&lt;td&gt;Use explicit loading strategies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storing DB passwords in Git&lt;/td&gt;
&lt;td&gt;Use environment configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  41. 💥 What Happens When a Database Operation Fails?
&lt;/h2&gt;

&lt;p&gt;You insert two users with the same email. PostgreSQL rejects the second insert because of the unique constraint, and SQLAlchemy raises an exception, commonly an &lt;strong&gt;&lt;code&gt;IntegrityError&lt;/code&gt;&lt;/strong&gt;. After a failed flush or commit, the Session must be &lt;strong&gt;rolled back&lt;/strong&gt; before it can be used again.&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;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exc&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;IntegrityError&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nc"&gt;IntegrityError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rollback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A failed transaction leaves the Session in a &lt;strong&gt;failed transaction state&lt;/strong&gt; until you roll back. Also, don't catch every exception and silently ignore it. Translate &lt;strong&gt;expected&lt;/strong&gt; database errors into meaningful application errors and let unexpected ones be handled appropriately.&lt;/p&gt;

&lt;h2&gt;
  
  
  42. ⚖️ SQLAlchemy vs Alembic
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;SQLAlchemy&lt;/th&gt;
&lt;th&gt;Alembic&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Database toolkit and ORM&lt;/td&gt;
&lt;td&gt;Database migration tool&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Defines ORM models&lt;/td&gt;
&lt;td&gt;Tracks schema changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Executes queries&lt;/td&gt;
&lt;td&gt;Applies migration operations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Creates, reads, updates and deletes records&lt;/td&gt;
&lt;td&gt;Adds/removes tables and columns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Manages Sessions and transactions&lt;/td&gt;
&lt;td&gt;Tracks schema revision history&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Used by application code&lt;/td&gt;
&lt;td&gt;Used during database deployment and maintenance&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 &lt;strong&gt;SQLAlchemy manages data operations. Alembic manages schema evolution.&lt;/strong&gt; They work together but have different responsibilities.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  🧵 PART 15: Putting Everything Together
&lt;/h1&gt;

&lt;h2&gt;
  
  
  43. 🛠️ A Complete Real-World Workflow
&lt;/h2&gt;

&lt;p&gt;Your mentor asks you to add a &lt;code&gt;Post&lt;/code&gt; table. Here's the full workflow:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1️⃣ Define the SQLAlchemy model&lt;/strong&gt; in &lt;code&gt;models/post.py&lt;/code&gt;&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;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;)&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="s2"&gt;"posts"&lt;/span&gt;

    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Mapped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&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="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2️⃣ Register the model.&lt;/strong&gt; Import it so its table is included in &lt;code&gt;Base.metadata&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3️⃣ Generate the migration&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;alembic revision --autogenerate -m &lt;span class="s2"&gt;"create posts table"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4️⃣ Review the migration.&lt;/strong&gt; Inspect the generated &lt;code&gt;upgrade()&lt;/code&gt; and &lt;code&gt;downgrade()&lt;/code&gt; functions&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5️⃣ Apply the migration&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;alembic upgrade head
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PostgreSQL now has the new table. ✅&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6️⃣ Create repository functions:&lt;/strong&gt; &lt;code&gt;create_post()&lt;/code&gt;, &lt;code&gt;get_post_by_id()&lt;/code&gt;, &lt;code&gt;delete_post()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7️⃣ Create service functions&lt;/strong&gt; with business rules such as checking ownership and validating permissions&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8️⃣ Create FastAPI endpoints&lt;/strong&gt;&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="nd"&gt;@router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/posts"&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;create_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;.&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;p&gt;&lt;strong&gt;9️⃣ Test the functionality.&lt;/strong&gt; Verify the endpoint works and the data is correctly stored in PostgreSQL&lt;/p&gt;

&lt;h2&gt;
  
  
  44. 🗺️ How the Whole Architecture Connects
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Responsibility&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;⚡ FastAPI&lt;/td&gt;
&lt;td&gt;Handles HTTP requests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;✔️ Pydantic&lt;/td&gt;
&lt;td&gt;Validates request and response data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🧱 SQLAlchemy ORM&lt;/td&gt;
&lt;td&gt;Maps Python objects to database tables&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🔧 SQLAlchemy Core&lt;/td&gt;
&lt;td&gt;Provides SQL expression tools&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🔌 Engine&lt;/td&gt;
&lt;td&gt;Manages database connectivity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🗂️ AsyncSession&lt;/td&gt;
&lt;td&gt;Manages asynchronous ORM operations and transactions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🚀 asyncpg&lt;/td&gt;
&lt;td&gt;Communicates asynchronously with PostgreSQL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;📚 Repository&lt;/td&gt;
&lt;td&gt;Performs database access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🧠 Service&lt;/td&gt;
&lt;td&gt;Implements business rules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🔄 Alembic&lt;/td&gt;
&lt;td&gt;Changes the database schema over time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🐘 PostgreSQL&lt;/td&gt;
&lt;td&gt;Permanently stores relational data&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h1&gt;
  
  
  🎤 PART 16: Interview Questions
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;1. What is SQLAlchemy?&lt;/strong&gt;&lt;br&gt;
A Python SQL toolkit and ORM.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. What's the main difference between Core and ORM?&lt;/strong&gt;&lt;br&gt;
Core works with tables and SQL expressions. The ORM works with mapped Python classes and objects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. What does &lt;code&gt;flush()&lt;/code&gt; do?&lt;/strong&gt;&lt;br&gt;
It sends pending SQL changes to the database within the current transaction, without committing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. What is &lt;code&gt;AsyncSession&lt;/code&gt;?&lt;/strong&gt;&lt;br&gt;
A session for asynchronous ORM database operations, used with an async engine and driver such as asyncpg.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. What is Alembic used for?&lt;/strong&gt;&lt;br&gt;
Managing database schema migrations over time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. What does &lt;code&gt;--autogenerate&lt;/code&gt; do?&lt;/strong&gt;&lt;br&gt;
It compares your model metadata with the actual database and proposes a migration. Always review the result.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. What does &lt;code&gt;alembic upgrade head&lt;/code&gt; do?&lt;/strong&gt;&lt;br&gt;
It applies all pending migrations up to the latest revision.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8. What is the Repository Pattern?&lt;/strong&gt;&lt;br&gt;
A layer that encapsulates database access, keeping queries out of routes and business logic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;9. What does &lt;code&gt;target_metadata = Base.metadata&lt;/code&gt; do?&lt;/strong&gt;&lt;br&gt;
It tells Alembic which SQLAlchemy model metadata to compare against the database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;10. Can one &lt;code&gt;AsyncSession&lt;/code&gt; be safely shared by concurrent asyncio tasks?&lt;/strong&gt;&lt;br&gt;
No. Each concurrent task should use its own &lt;code&gt;AsyncSession&lt;/code&gt;.&lt;/p&gt;


&lt;h1&gt;
  
  
  🧪 PART 17: Test Yourself!
&lt;/h1&gt;

&lt;p&gt;Try these before peeking at the answers! ❤️&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;What is SQLAlchemy?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) A Python SQL toolkit and ORM&lt;/li&gt;
&lt;li&gt;B) A database server&lt;/li&gt;
&lt;li&gt;C) A Python web framework&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;What's the main difference between Core and ORM?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Core uses tables and SQL expressions; ORM uses mapped Python objects&lt;/li&gt;
&lt;li&gt;B) Core is asynchronous; ORM is synchronous&lt;/li&gt;
&lt;li&gt;C) They're completely unrelated tools&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;What does &lt;code&gt;flush()&lt;/code&gt; do?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Permanently commits changes&lt;/li&gt;
&lt;li&gt;B) Sends pending SQL changes within the transaction without committing&lt;/li&gt;
&lt;li&gt;C) Closes the database connection&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;What is &lt;code&gt;AsyncSession&lt;/code&gt;?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) A session for asynchronous database operations&lt;/li&gt;
&lt;li&gt;B) A PostgreSQL table&lt;/li&gt;
&lt;li&gt;C) An Alembic migration&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;What is Alembic used for?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Validating HTTP request bodies&lt;/li&gt;
&lt;li&gt;B) Managing database schema migrations&lt;/li&gt;
&lt;li&gt;C) Creating FastAPI routes&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;What does &lt;code&gt;--autogenerate&lt;/code&gt; do?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Automatically deploys the database&lt;/li&gt;
&lt;li&gt;B) Compares model metadata with the database and proposes a migration&lt;/li&gt;
&lt;li&gt;C) Deletes old migrations&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;What does &lt;code&gt;alembic upgrade head&lt;/code&gt; do?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Applies pending migrations to the latest revision&lt;/li&gt;
&lt;li&gt;B) Deletes all tables&lt;/li&gt;
&lt;li&gt;C) Creates a new SQLAlchemy model&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;What is the Repository Pattern?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) A way to organize Git repositories&lt;/li&gt;
&lt;li&gt;B) A layer that encapsulates database access&lt;/li&gt;
&lt;li&gt;C) A database indexing technique&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;What does &lt;code&gt;target_metadata = Base.metadata&lt;/code&gt; do?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Tells Alembic which SQLAlchemy model metadata to compare&lt;/li&gt;
&lt;li&gt;B) Creates a PostgreSQL database&lt;/li&gt;
&lt;li&gt;C) Commits a transaction&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Can one &lt;code&gt;AsyncSession&lt;/code&gt; be safely shared by concurrent asyncio tasks?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Yes, always&lt;/li&gt;
&lt;li&gt;B) No, each concurrent task should use its own &lt;code&gt;AsyncSession&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;C) Only when using PostgreSQL&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;/p&gt;
  👀 Click to reveal answers
  &lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A&lt;/strong&gt;: SQLAlchemy is a Python SQL toolkit and ORM&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A&lt;/strong&gt;: Core is SQL-expression oriented; the ORM maps Python objects&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;B&lt;/strong&gt;: &lt;code&gt;flush()&lt;/code&gt; sends SQL in the current transaction but doesn't commit&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A&lt;/strong&gt;: &lt;code&gt;AsyncSession&lt;/code&gt; handles asynchronous ORM operations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;B&lt;/strong&gt;: Alembic manages schema migrations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;B&lt;/strong&gt;: Autogenerate compares metadata with the database and proposes changes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A&lt;/strong&gt;: It applies pending migrations up to the latest revision&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;B&lt;/strong&gt;: A repository encapsulates database access&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A&lt;/strong&gt;: It gives Alembic the model metadata to compare against the database&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;B&lt;/strong&gt;: Sessions are stateful, so use one per concurrent task&lt;/li&gt;
&lt;/ol&gt;



&lt;p&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  🗺️ PART 18: What to Learn Next
&lt;/h1&gt;

&lt;p&gt;Work through this checklist in order:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Understand SQLAlchemy Core vs ORM&lt;/li&gt;
&lt;li&gt;[ ] Create &lt;code&gt;DeclarativeBase&lt;/code&gt; and ORM models&lt;/li&gt;
&lt;li&gt;[ ] Understand &lt;code&gt;Mapped&lt;/code&gt;, &lt;code&gt;mapped_column&lt;/code&gt; and column types&lt;/li&gt;
&lt;li&gt;[ ] Create primary keys, foreign keys and relationships&lt;/li&gt;
&lt;li&gt;[ ] Understand Engine and connection pooling&lt;/li&gt;
&lt;li&gt;[ ] Master Session, &lt;code&gt;flush&lt;/code&gt;, &lt;code&gt;commit&lt;/code&gt; and &lt;code&gt;rollback&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Learn &lt;code&gt;AsyncEngine&lt;/code&gt;, &lt;code&gt;AsyncSession&lt;/code&gt; and &lt;code&gt;asyncpg&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Learn &lt;code&gt;select()&lt;/code&gt;, &lt;code&gt;execute()&lt;/code&gt;, &lt;code&gt;scalars()&lt;/code&gt; and filtering&lt;/li&gt;
&lt;li&gt;[ ] Understand relationship loading strategies&lt;/li&gt;
&lt;li&gt;[ ] Create repository and service layers&lt;/li&gt;
&lt;li&gt;[ ] Initialize Alembic and configure &lt;code&gt;target_metadata&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Generate and review autogenerated migrations&lt;/li&gt;
&lt;li&gt;[ ] Apply upgrades and perform downgrades&lt;/li&gt;
&lt;li&gt;[ ] Handle database transactions and &lt;code&gt;IntegrityError&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🏁 The 10 Most Important Things to Remember
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;🧱 SQLAlchemy is a Python SQL toolkit and ORM&lt;/li&gt;
&lt;li&gt;🪄 The ORM maps Python classes to database tables&lt;/li&gt;
&lt;li&gt;📐 Declarative models define your tables using Python classes&lt;/li&gt;
&lt;li&gt;🔧 Core works with SQL expressions and table definitions&lt;/li&gt;
&lt;li&gt;🗂️ &lt;code&gt;Session&lt;/code&gt; / &lt;code&gt;AsyncSession&lt;/code&gt; manage ORM database interactions and transactions&lt;/li&gt;
&lt;li&gt;🚀 &lt;code&gt;asyncpg&lt;/code&gt; is an asynchronous PostgreSQL driver&lt;/li&gt;
&lt;li&gt;🚿 &lt;code&gt;flush()&lt;/code&gt; executes pending SQL &lt;strong&gt;without committing&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🔄 Alembic manages database schema changes through migrations&lt;/li&gt;
&lt;li&gt;🤖 Autogenerate proposes migrations by comparing metadata with the actual database&lt;/li&gt;
&lt;li&gt;📚 The Repository Pattern separates database access from business logic and API routes&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;🎯 &lt;strong&gt;One final distinction:&lt;/strong&gt; if you add a &lt;code&gt;bio&lt;/code&gt; column to your SQLAlchemy &lt;code&gt;User&lt;/code&gt; model, PostgreSQL &lt;strong&gt;doesn't automatically get that column&lt;/strong&gt;. You need to generate and apply an Alembic migration. And when you want to retrieve a user from PostgreSQL, SQLAlchemy's &lt;code&gt;Session&lt;/code&gt; or &lt;code&gt;AsyncSession&lt;/code&gt; executes the query. These are two different responsibilities.&lt;/p&gt;
&lt;/blockquote&gt;




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

&lt;p&gt;Add a &lt;code&gt;Post&lt;/code&gt; model to a small project, generate the migration with &lt;code&gt;--autogenerate&lt;/code&gt;, &lt;strong&gt;read it before applying it&lt;/strong&gt;, run &lt;code&gt;alembic upgrade head&lt;/code&gt;, and then build the repository, service and route on top. Hands-on practice is where it all clicks! 💡&lt;/p&gt;

&lt;p&gt;If this post helped you, drop a ❤️ or 🦄 and share it with a friend learning backend development. Happy coding! 👩‍💻✨&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>database</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Pydantic v2 Deep Dive &amp; Custom Validators: Explained from Absolute Zero 🛡️</title>
      <dc:creator>Deepika Pusala</dc:creator>
      <pubDate>Wed, 11 Nov 2026 07:00:03 +0000</pubDate>
      <link>https://dev.to/deepika_pusala/pydantic-v2-deep-dive-custom-validators-explained-from-absolute-zero-4739</link>
      <guid>https://dev.to/deepika_pusala/pydantic-v2-deep-dive-custom-validators-explained-from-absolute-zero-4739</guid>
      <description>&lt;h1&gt;
  
  
  🛡️ Pydantic v2 Deep Dive &amp;amp; Custom Validators: Let's Learn from ZERO!
&lt;/h1&gt;

&lt;p&gt;Almost every API you build needs to &lt;strong&gt;validate incoming data, reject invalid values, convert data into Python objects, and control what goes back to the frontend&lt;/strong&gt;. In FastAPI, &lt;strong&gt;Pydantic&lt;/strong&gt; does all of that. 🚀&lt;/p&gt;

&lt;p&gt;No Pydantic knowledge is assumed. We'll connect every concept to a React + FastAPI social-feed backend so you see how it works in a real project, not just as definitions.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. 🤔 What problem does Pydantic solve?
&lt;/h2&gt;

&lt;p&gt;Your React frontend sends this to your FastAPI backend:&lt;br&gt;
&lt;/p&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="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"deepika"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"deepika@gmail.com"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But what if it accidentally sends this?&lt;br&gt;
&lt;/p&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="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12345&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"twenty-two"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"not-an-email"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;❌ Username is a number instead of a string&lt;/li&gt;
&lt;li&gt;❌ Age is text instead of a number&lt;/li&gt;
&lt;li&gt;❌ Email is not a valid email address&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Should your backend blindly accept it? &lt;strong&gt;Absolutely not!&lt;/strong&gt; 🙅&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;Pydantic&lt;/strong&gt; is a Python library for &lt;strong&gt;data validation, parsing, and serialization&lt;/strong&gt; using Python type annotations. In simple words: &lt;em&gt;it checks whether data follows the structure and rules you defined.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Think of Pydantic as a &lt;strong&gt;security checkpoint&lt;/strong&gt; ✈️ at the entrance of your backend:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Correct data → allowed through&lt;/li&gt;
&lt;li&gt;❌ Incorrect data → validation error&lt;/li&gt;
&lt;li&gt;🔄 Data needing conversion → converted when the rules permit&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🤝 Why do we need it in FastAPI?
&lt;/h3&gt;

&lt;p&gt;FastAPI integrates directly with Pydantic. When a request reaches an endpoint, FastAPI can use a Pydantic model to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Read incoming JSON&lt;/li&gt;
&lt;li&gt;Validate its fields&lt;/li&gt;
&lt;li&gt;Convert compatible values into Python types when allowed&lt;/li&gt;
&lt;li&gt;Give your endpoint a validated Python object&lt;/li&gt;
&lt;li&gt;Generate API documentation automatically&lt;/li&gt;
&lt;li&gt;Validate and serialize outgoing responses&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  2. 🏗️ What is a Pydantic model?
&lt;/h2&gt;

&lt;p&gt;First, a normal Python class:&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;User&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;


&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"deepika"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&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;username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# deepika&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&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;age&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;       &lt;span class="c1"&gt;# 22&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But Python doesn't validate the types here:&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;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12345&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"twenty-two"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Python happily creates this 😬&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now with Pydantic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip install pydantic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;BaseModel&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;


&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"deepika"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;username='deepika' age=22
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;BaseModel&lt;/code&gt; is the main Pydantic class that provides validation and serialization&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;username: str&lt;/code&gt; and &lt;code&gt;age: int&lt;/code&gt; are &lt;strong&gt;type annotations&lt;/strong&gt;. They tell Pydantic what to expect&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now try &lt;code&gt;User(username=12345, age="twenty-two")&lt;/code&gt; and Pydantic raises a &lt;strong&gt;&lt;code&gt;ValidationError&lt;/code&gt;&lt;/strong&gt;. 💥&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 A &lt;strong&gt;Pydantic model&lt;/strong&gt; is a Python class that inherits from &lt;code&gt;BaseModel&lt;/code&gt; and defines the expected structure and validation rules of data.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Incoming Python data / JSON
        ▼
Pydantic BaseModel   → checks fields, types and validation rules
        ├── ✅ Valid data   → Python model object
        └── ❌ Invalid data → ValidationError
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. 🆕 Pydantic v2: What's New and Important?
&lt;/h2&gt;

&lt;p&gt;Pydantic has two major versions: &lt;strong&gt;v1&lt;/strong&gt; (older API) and &lt;strong&gt;v2&lt;/strong&gt; (newer implementation, faster, with updated APIs). Focus on &lt;strong&gt;v2&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pydantic v1&lt;/th&gt;
&lt;th&gt;Pydantic v2&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@validator&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@field_validator&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@root_validator&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@model_validator&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.dict()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.model_dump()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.json()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.model_dump_json()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.parse_obj()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.model_validate()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.schema()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.model_json_schema()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.copy()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.model_copy()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;          &lt;span class="c1"&gt;# old&lt;/span&gt;
&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model_dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;# new ✅&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 In new projects, learn and use the &lt;strong&gt;v2 APIs&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  4. 🎚️ Required Fields, Optional Fields and Defaults
&lt;/h2&gt;

&lt;p&gt;Not every field in an API request is mandatory. In a registration API, username, email and password are required, but a profile &lt;strong&gt;bio&lt;/strong&gt; might be optional.&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;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;BaseModel&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;bio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;username: str&lt;/code&gt; → &lt;strong&gt;required&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bio: str | None = None&lt;/code&gt; → can be a string, can be &lt;code&gt;None&lt;/code&gt;, and &lt;strong&gt;defaults to &lt;code&gt;None&lt;/code&gt;&lt;/strong&gt; if omitted
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;UserCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"deepika"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"deepika@gmail.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"secret123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&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;bio&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ⚠️ Optional vs default value (a classic interview question!)
&lt;/h3&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;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;bio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;        &lt;span class="c1"&gt;# value may be None, but the field is STILL REQUIRED&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;bio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;  &lt;span class="c1"&gt;# the user can omit it ✅&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the first version, the caller must explicitly supply &lt;code&gt;bio&lt;/code&gt;, even if the value is &lt;code&gt;None&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  🧮 Default values
&lt;/h3&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;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;is_active&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
    &lt;span class="n"&gt;followers_count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🏭 Default factories
&lt;/h3&gt;

&lt;p&gt;Need a &lt;strong&gt;new list for every object&lt;/strong&gt;?&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;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;interests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default_factory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kt"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Field(default_factory=list)&lt;/code&gt; creates a fresh empty list whenever a default is needed, so objects never accidentally &lt;strong&gt;share the same mutable list&lt;/strong&gt;. You can also use it for timestamps:&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;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timezone&lt;/span&gt;

&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;default_factory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utc&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;p&gt;Each object gets its own creation timestamp. ⏱️&lt;/p&gt;




&lt;h2&gt;
  
  
  5. 📏 &lt;code&gt;Field()&lt;/code&gt;: Adding Validation Constraints
&lt;/h2&gt;

&lt;p&gt;Imagine someone sends &lt;code&gt;{"username": "", "age": -10}&lt;/code&gt;. Technically the username is a string and the age is an integer, but the values make no sense! We need &lt;strong&gt;extra rules&lt;/strong&gt;. That's what &lt;code&gt;Field()&lt;/code&gt; is for.&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;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ge&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;le&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🔤 String constraints
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Constraint&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;min_length=3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Minimum 3 characters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;max_length=20&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Maximum 20 characters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pattern="..."&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Must match a regular expression&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;min_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="s2"&gt;"^[a-zA-Z0-9_]+$"&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;p&gt;This means 3 to 20 characters, using only letters, numbers and underscores.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔢 Numeric constraints
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Constraint&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gt=0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;G&lt;/strong&gt;reater &lt;strong&gt;t&lt;/strong&gt;han zero&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ge=18&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;G&lt;/strong&gt;reater than or &lt;strong&gt;e&lt;/strong&gt;qual to 18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lt=100&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;L&lt;/strong&gt;ess &lt;strong&gt;t&lt;/strong&gt;han 100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;le=100&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;L&lt;/strong&gt;ess than or &lt;strong&gt;e&lt;/strong&gt;qual to 100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;multiple_of=5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Must be divisible by 5&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gt&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;# must be greater than zero&lt;/span&gt;
&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ge&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;le&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# between 18 and 100, inclusive&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  📚 Collection constraints
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The list needs at least 1 item and at most 5. Because of &lt;code&gt;list[str]&lt;/code&gt;, Pydantic also checks that &lt;strong&gt;every item is a string&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 &lt;strong&gt;Remember:&lt;/strong&gt; &lt;code&gt;Field()&lt;/code&gt; defines &lt;strong&gt;declarative constraints&lt;/strong&gt;. Validators define &lt;strong&gt;custom logic&lt;/strong&gt;. &lt;code&gt;min_length=3&lt;/code&gt; is a constraint, but "username must not contain a prohibited word" needs custom code.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  6. 🧪 What are Custom Validators?
&lt;/h2&gt;

&lt;p&gt;In your social-feed registration API you want to enforce:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Username must not contain spaces&lt;/li&gt;
&lt;li&gt;Email must be lowercase&lt;/li&gt;
&lt;li&gt;Username must not contain offensive words&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;Field()&lt;/code&gt; can't express all of that. We need Python logic.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 A &lt;strong&gt;custom validator&lt;/strong&gt; is a function you write to check whether a value (or a whole model) satisfies your application's rules.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Pydantic v2 gives you two decorators:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@field_validator&lt;/code&gt;: validates &lt;strong&gt;one field&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@model_validator&lt;/code&gt;: validates the &lt;strong&gt;whole model&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  7. 🎯 &lt;code&gt;@field_validator&lt;/code&gt;: Validating Individual Fields
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field_validator&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;

    &lt;span class="nd"&gt;@field_validator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;validate_username&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s2"&gt;" "&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Username cannot contain spaces"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Line by line:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@field_validator("username")&lt;/code&gt; → &lt;em&gt;"run this function when validating the &lt;code&gt;username&lt;/code&gt; field"&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@classmethod&lt;/code&gt; → in Pydantic v2, field validators generally take &lt;code&gt;cls&lt;/code&gt; as the first parameter&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;def validate_username(cls, value)&lt;/code&gt; → &lt;code&gt;value&lt;/code&gt; is the value being validated (e.g. &lt;code&gt;"deepika"&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;if " " in value&lt;/code&gt; → our check&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;raise ValueError(...)&lt;/code&gt; → &lt;strong&gt;rejects&lt;/strong&gt; the value. Pydantic catches it and produces a structured &lt;code&gt;ValidationError&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;return value&lt;/code&gt; → &lt;strong&gt;allows&lt;/strong&gt; the value to continue. A field validator must return the value it wants Pydantic to use&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can validate several fields with one validator:&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="nd"&gt;@field_validator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"display_name"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@classmethod&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;validate_names&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&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;p&gt;Testing it:&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="nc"&gt;UserCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"deepika"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;          &lt;span class="c1"&gt;# ✅ works&lt;/span&gt;
&lt;span class="nc"&gt;UserCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"deepika rao"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="c1"&gt;# ❌ ValidationError&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 &lt;code&gt;raise ValueError()&lt;/code&gt; rejects the input. &lt;code&gt;return value&lt;/code&gt; lets the validated value continue.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  8. 🔀 Field Validator Modes: Before, After, Plain, Wrap
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;@field_validator&lt;/code&gt; supports &lt;strong&gt;four modes&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;When does it run?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;before&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Before&lt;/strong&gt; Pydantic's normal type validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;after&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;After&lt;/strong&gt; normal type validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;plain&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Replaces&lt;/strong&gt; normal validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;wrap&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Wraps&lt;/strong&gt; normal validation and controls when it runs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  🅰️ &lt;code&gt;before&lt;/code&gt;: clean raw input
&lt;/h3&gt;

&lt;p&gt;The frontend sends &lt;code&gt;"   deepika   "&lt;/code&gt; and you want to strip the spaces first:&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;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field_validator&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;

    &lt;span class="nd"&gt;@field_validator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"before"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;clean_username&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Incoming value: "   deepika   "
        ▼
Before validator → strip() removes extra spaces
        ▼
Pydantic type validation → checks the result is a string
        ▼
Final value: "deepika"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why &lt;code&gt;isinstance(value, str)&lt;/code&gt;? Because &lt;strong&gt;before&lt;/strong&gt; validation, Pydantic hasn't checked the input yet. It could be a string, integer, list or anything. A &lt;code&gt;before&lt;/code&gt; validator must be ready for &lt;strong&gt;raw input&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;before&lt;/code&gt; to &lt;strong&gt;clean, normalize or preprocess&lt;/strong&gt; incoming data.&lt;/p&gt;

&lt;h3&gt;
  
  
  🅱️ &lt;code&gt;after&lt;/code&gt;: check validated values
&lt;/h3&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;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;

    &lt;span class="nd"&gt;@field_validator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"after"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;validate_age&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"User must be at least 18"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For &lt;code&gt;User(age="22")&lt;/code&gt;, in default (non-strict) mode Pydantic converts &lt;code&gt;"22"&lt;/code&gt; to &lt;code&gt;22&lt;/code&gt; first, and then your validator receives &lt;code&gt;22&lt;/code&gt; and checks the business rule.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;after&lt;/code&gt; for values that have &lt;strong&gt;already passed type validation&lt;/strong&gt;. It's the &lt;strong&gt;easiest and safest&lt;/strong&gt; mode for beginners. ✅&lt;/p&gt;

&lt;h3&gt;
  
  
  🅲 &lt;code&gt;plain&lt;/code&gt;: take full control
&lt;/h3&gt;

&lt;p&gt;A plain validator &lt;strong&gt;replaces&lt;/strong&gt; Pydantic's normal validation:&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;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field_validator&lt;/span&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="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;

    &lt;span class="nd"&gt;@field_validator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"quantity"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"plain"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;validate_quantity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;

        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Quantity must be a positive integer"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even though the field says &lt;code&gt;quantity: int&lt;/code&gt;, Pydantic won't run its normal integer validation after this. &lt;strong&gt;Your function is responsible for checking the input.&lt;/strong&gt; Use &lt;code&gt;plain&lt;/code&gt; only when you deliberately want to replace normal validation.&lt;/p&gt;

&lt;h3&gt;
  
  
  🅳 &lt;code&gt;wrap&lt;/code&gt;: advanced control
&lt;/h3&gt;

&lt;p&gt;A wrap validator receives the incoming value &lt;strong&gt;and a handler&lt;/strong&gt; that runs Pydantic's normal validation. You can run logic before it, after it, or catch errors:&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;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ValidatorFunctionWrapHandler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field_validator&lt;/span&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="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;

    &lt;span class="nd"&gt;@field_validator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"quantity"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"wrap"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;validate_quantity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ValidatorFunctionWrapHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Before normal validation"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"After normal validation"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For &lt;code&gt;Product(quantity="10")&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before normal validation
        ▼
handler(value) → Pydantic validates and converts "10" to 10
        ▼
After normal validation
        ▼
Return result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the input is invalid, the handler raises a validation error, which you can &lt;strong&gt;catch&lt;/strong&gt; to implement fallback behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  📊 All four modes at a glance
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Idea&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;before&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Preprocess first, then normal validation&lt;/td&gt;
&lt;td&gt;Remove extra spaces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;after&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Normal validation first, then custom logic&lt;/td&gt;
&lt;td&gt;Age must be at least 18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;plain&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Custom validator replaces normal validation&lt;/td&gt;
&lt;td&gt;Implement your own full validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;wrap&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Custom logic controls normal validation&lt;/td&gt;
&lt;td&gt;Intercept errors, fallback behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Recommendation:&lt;/strong&gt; become very comfortable with &lt;code&gt;before&lt;/code&gt; and &lt;code&gt;after&lt;/code&gt; first, then learn &lt;code&gt;plain&lt;/code&gt; and &lt;code&gt;wrap&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  9. 🧩 &lt;code&gt;@model_validator&lt;/code&gt;: Validating Multiple Fields Together
&lt;/h2&gt;

&lt;p&gt;A registration form has &lt;code&gt;password&lt;/code&gt; and &lt;code&gt;confirm_password&lt;/code&gt;:&lt;br&gt;
&lt;/p&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="nl"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"hello123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"confirm_password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"hello456"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Individually both are valid strings. Together they violate a rule: &lt;strong&gt;they must match&lt;/strong&gt;. A field validator can't conveniently solve this. Use &lt;code&gt;@model_validator&lt;/code&gt;, which validates the &lt;strong&gt;entire model&lt;/strong&gt;.&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;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model_validator&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserRegister&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;confirm_password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;

    &lt;span class="nd"&gt;@model_validator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"after"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_passwords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;confirm_password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Passwords do not match"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@field_validator("username")&lt;/code&gt; validates &lt;strong&gt;one specific field&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@model_validator(mode="after")&lt;/code&gt; validates the &lt;strong&gt;complete model&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;after&lt;/code&gt; model validator receives the &lt;strong&gt;complete model instance&lt;/strong&gt;, so you access &lt;code&gt;self.password&lt;/code&gt; and &lt;code&gt;self.confirm_password&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;return self&lt;/code&gt; returns the validated model instance&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ⏪ &lt;code&gt;before&lt;/code&gt; model validators
&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;before&lt;/code&gt; model validator runs &lt;strong&gt;before&lt;/strong&gt; field validation and receives the raw input:&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;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;Any&lt;/span&gt;

&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model_validator&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;

    &lt;span class="nd"&gt;@model_validator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"before"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;preprocess_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s2"&gt;"user_name"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="s2"&gt;"username"&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"user_name"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If someone sends &lt;code&gt;{"user_name": "deepika", "email": "deepika@gmail.com"}&lt;/code&gt;, the validator turns it into &lt;code&gt;{"username": "deepika", ...}&lt;/code&gt; and then normal field validation happens. Notice we &lt;strong&gt;copy&lt;/strong&gt; the dictionary instead of modifying the original input.&lt;/p&gt;

&lt;h3&gt;
  
  
  📊 Model validator modes
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;before&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Receives raw input before field validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;after&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Receives the validated model instance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;wrap&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Can control and wrap the entire model validation process&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;📝 Unlike field validators, model validators don't have a &lt;code&gt;plain&lt;/code&gt; mode.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  ⚔️ Field validator vs model validator
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Field validator&lt;/th&gt;
&lt;th&gt;Model validator&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Main purpose&lt;/td&gt;
&lt;td&gt;Validate individual fields&lt;/td&gt;
&lt;td&gt;Validate relationships between fields&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Decorator&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@field_validator&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@model_validator&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Example&lt;/td&gt;
&lt;td&gt;Username cannot contain spaces&lt;/td&gt;
&lt;td&gt;Passwords must match&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can access whole model?&lt;/td&gt;
&lt;td&gt;Not directly in the normal way&lt;/td&gt;
&lt;td&gt;Yes, in &lt;code&gt;after&lt;/code&gt; mode&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Modes&lt;/td&gt;
&lt;td&gt;before, after, plain, wrap&lt;/td&gt;
&lt;td&gt;before, after, wrap&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  10. ℹ️ &lt;code&gt;ValidationInfo&lt;/code&gt;: Accessing Other Field Values
&lt;/h2&gt;

&lt;p&gt;Sometimes you validate one field &lt;strong&gt;based on another&lt;/strong&gt;. For example, reject a &lt;code&gt;display_name&lt;/code&gt; identical to the &lt;code&gt;username&lt;/code&gt;:&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;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ValidationInfo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field_validator&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;display_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;

    &lt;span class="nd"&gt;@field_validator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"display_name"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;validate_display_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ValidationInfo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Display name cannot equal username"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;info.data&lt;/code&gt; contains the &lt;strong&gt;previously validated fields&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Field validation normally follows the &lt;strong&gt;order in which fields are declared&lt;/strong&gt;. Here &lt;code&gt;username&lt;/code&gt; comes before &lt;code&gt;display_name&lt;/code&gt;, so its validated value is available. Reverse the order and &lt;code&gt;username&lt;/code&gt; might not be available yet. For relationships between several fields, &lt;code&gt;model_validator(mode="after")&lt;/code&gt; is often clearer.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  11. 🌍 Custom Validation: A Real-World Example
&lt;/h2&gt;

&lt;p&gt;Registration rules for your social-feed app:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Username has 3 to 20 characters&lt;/li&gt;
&lt;li&gt;Username cannot contain spaces&lt;/li&gt;
&lt;li&gt;Email must be lowercase&lt;/li&gt;
&lt;li&gt;Age must be at least 18&lt;/li&gt;
&lt;li&gt;Password has at least 8 characters&lt;/li&gt;
&lt;li&gt;Confirm password must match password
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;field_validator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;model_validator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserRegister&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ge&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;confirm_password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;

    &lt;span class="nd"&gt;@field_validator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;validate_username&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s2"&gt;" "&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Username cannot contain spaces"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@field_validator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;validate_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s2"&gt;"@"&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Invalid email"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;

    &lt;span class="nd"&gt;@model_validator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"after"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;validate_passwords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;confirm_password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Passwords do not match"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;📝 This uses a simple email check to demonstrate custom logic. For production-grade email validation, use &lt;code&gt;EmailStr&lt;/code&gt; with the &lt;code&gt;email-validator&lt;/code&gt; package. And never store plain-text passwords. Hash them with an algorithm such as &lt;strong&gt;Argon2id&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  🧭 Where should validation happen?
&lt;/h3&gt;

&lt;p&gt;Not every business rule belongs in Pydantic:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rule&lt;/th&gt;
&lt;th&gt;Best place&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Username must be a string&lt;/td&gt;
&lt;td&gt;Pydantic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Username length must be 3 to 20&lt;/td&gt;
&lt;td&gt;Pydantic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Password confirmation must match&lt;/td&gt;
&lt;td&gt;Pydantic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Email must have valid format&lt;/td&gt;
&lt;td&gt;Pydantic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Username already exists in database&lt;/td&gt;
&lt;td&gt;Service / database&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User has permission to delete a post&lt;/td&gt;
&lt;td&gt;Authorization / service layer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Post belongs to the logged-in user&lt;/td&gt;
&lt;td&gt;Service / database&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Pydantic is for validating &lt;strong&gt;data structure and input rules&lt;/strong&gt;. It shouldn't become responsible for database operations, authorization, or application workflows.&lt;/p&gt;




&lt;h2&gt;
  
  
  12. 🔄 Request Schemas vs Response Schemas
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;🚨 &lt;strong&gt;EXTREMELY IMPORTANT&lt;/strong&gt; for FastAPI projects!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A user registers and the frontend sends:&lt;br&gt;
&lt;/p&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="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"deepika"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"deepika@gmail.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"secret123"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your database stores:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;username&lt;/th&gt;
&lt;th&gt;email&lt;/th&gt;
&lt;th&gt;password_hash&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;deepika&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:deepika@gmail.com"&gt;deepika@gmail.com&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;$argon2id$...&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Should the backend send that complete record back to React? &lt;strong&gt;Never!&lt;/strong&gt; You must never expose password hashes to the frontend. That's why we separate request and response schemas.&lt;/p&gt;

&lt;h3&gt;
  
  
  🅰️ Request schema
&lt;/h3&gt;

&lt;p&gt;What the client is allowed or expected to &lt;strong&gt;send&lt;/strong&gt;:&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;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🅱️ Response schema
&lt;/h3&gt;

&lt;p&gt;What the backend is allowed to &lt;strong&gt;return&lt;/strong&gt;:&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;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;BaseModel&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice there's &lt;strong&gt;no password field&lt;/strong&gt;. That's intentional. 🔐&lt;/p&gt;

&lt;h3&gt;
  
  
  🤔 Why separate them?
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Frontend → Backend:   UserCreate     (username, email, password)
        ▼
Backend processes and stores data
        ▼
Database:             id, username, email, password_hash
        ▼
Backend selects safe output fields
        ▼
Backend → Frontend:   UserResponse   (id, username, email)  ✅ no password hash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you security, clear API contracts, better maintainability, independent validation for input and output, and control over exposed fields.&lt;/p&gt;

&lt;h3&gt;
  
  
  📚 Other common schema types
&lt;/h3&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;UserCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;bio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;bio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserLogin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Schema&lt;/th&gt;
&lt;th&gt;Responsibility&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UserCreate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;New user registration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UserUpdate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Updating profile details&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UserResponse&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returning safe user information&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UserLogin&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Accepting login credentials&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 A &lt;strong&gt;database model&lt;/strong&gt; and a &lt;strong&gt;Pydantic schema&lt;/strong&gt; are not the same thing. A SQLAlchemy model represents database structure and behavior. A Pydantic model represents the structure and validation of data &lt;strong&gt;entering or leaving your API&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  13. ⚡ FastAPI &lt;code&gt;response_model&lt;/code&gt;: How Does It Work?
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;response_model&lt;/code&gt; tells FastAPI which schema should describe and validate the endpoint's &lt;strong&gt;output&lt;/strong&gt;.&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;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;BaseModel&lt;/span&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="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;


&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/users"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response_model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;UserResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UserCreate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"username"&lt;/span&gt;&lt;span class="p"&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;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"password"&lt;/span&gt;&lt;span class="p"&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;password&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;p&gt;The function returns a dict &lt;strong&gt;with the password&lt;/strong&gt;, but &lt;code&gt;UserResponse&lt;/code&gt; only has &lt;code&gt;id&lt;/code&gt; and &lt;code&gt;username&lt;/code&gt;. FastAPI validates the returned data against &lt;code&gt;UserResponse&lt;/code&gt; and serializes the response according to it. The extra &lt;code&gt;password&lt;/code&gt; field is &lt;strong&gt;not included&lt;/strong&gt; in the normal serialized response:&lt;br&gt;
&lt;/p&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="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"deepika"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ✅ Why is &lt;code&gt;response_model&lt;/code&gt; useful?
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Validates outgoing response data&lt;/li&gt;
&lt;li&gt;Serializes data into the expected representation&lt;/li&gt;
&lt;li&gt;Filters fields that aren't part of the response schema&lt;/li&gt;
&lt;li&gt;Generates accurate API documentation&lt;/li&gt;
&lt;li&gt;Establishes a clear contract for the frontend&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;🚨 &lt;strong&gt;Security warning:&lt;/strong&gt; don't rely on response filtering as your &lt;em&gt;only&lt;/em&gt; security measure. Never pass secrets into response construction unnecessarily. Build safe response data explicitly and keep sensitive fields out of API-facing schemas.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  🆚 Request body vs &lt;code&gt;response_model&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/users"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response_model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;UserResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UserCreate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;.&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;ul&gt;
&lt;li&gt;
&lt;code&gt;user: UserCreate&lt;/code&gt; → defines the &lt;strong&gt;incoming request body&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;response_model=UserResponse&lt;/code&gt; → defines the &lt;strong&gt;outgoing response&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🎛️ More &lt;code&gt;response_model&lt;/code&gt; options
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;"/users/{user_id}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;response_model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;UserResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;response_model_exclude_unset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&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;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;response_model_exclude_unset=True&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Excludes fields not explicitly set&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;response_model_exclude_none=True&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Excludes fields whose value is &lt;code&gt;None&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;response_model_exclude_defaults=True&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Excludes values equal to their defaults&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;response_model_include={...}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Includes selected fields&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;response_model_exclude={...}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Excludes selected fields&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Prefer creating a &lt;strong&gt;dedicated response schema&lt;/strong&gt; over relying heavily on include/exclude tricks.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  14. 📤 Serialization: Python Objects → JSON
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;UserResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"deepika"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"deepika@gmail.com"&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;p&gt;This is a Python object, but React expects JSON. Converting it is &lt;strong&gt;serialization&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;Serialization&lt;/strong&gt; means converting a Python object or structured data into a format that can be transmitted, stored, or consumed elsewhere.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  🅰️ &lt;code&gt;model_dump()&lt;/code&gt;: model → Python dict
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;user_data&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="nf"&gt;model_dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"deepika"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"deepika@gmail.com"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It produces a Python &lt;strong&gt;dictionary&lt;/strong&gt;, not a JSON string.&lt;/p&gt;

&lt;h3&gt;
  
  
  🅱️ &lt;code&gt;model_dump_json()&lt;/code&gt;: model → JSON string
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model_dump_json&lt;/span&gt;&lt;span class="p"&gt;(&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"id":1,"username":"deepika","email":"deepika@gmail.com"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🅲 &lt;code&gt;model_validate()&lt;/code&gt;: Python data → validated model
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;user_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"deepika"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"deepika@gmail.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;UserResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model_validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🅳 &lt;code&gt;model_validate_json()&lt;/code&gt;: JSON text → validated model
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;user_json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"""
{
    "id": 1,
    "username": "deepika",
    "email": "deepika@gmail.com"
}
"""&lt;/span&gt;

&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;UserResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model_validate_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  📊 Quick comparison
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;model_dump()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Model → Python dictionary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;model_dump_json()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Model → JSON string&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;model_validate()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Python data → validated model&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;model_validate_json()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;JSON string/bytes → validated model&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;model_copy()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a copy of a model&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;model_json_schema()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generate JSON Schema&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  🎛️ Serialization options
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model_dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exclude&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# {"id": 1, "username": "deepika"}&lt;/span&gt;

&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model_dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exclude_none&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;# skip fields whose value is None&lt;/span&gt;
&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model_dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exclude_unset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# skip fields not explicitly supplied/set&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;code&gt;exclude_unset&lt;/code&gt; and &lt;code&gt;exclude_defaults&lt;/code&gt; are &lt;strong&gt;different&lt;/strong&gt;. A field explicitly provided with its default value is still considered &lt;em&gt;set&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  15. 🏷️ Aliasing: Different Names in Python and JSON
&lt;/h2&gt;

&lt;p&gt;Your Python backend uses &lt;strong&gt;snake_case&lt;/strong&gt; (&lt;code&gt;created_at&lt;/code&gt;), but your React frontend uses &lt;strong&gt;camelCase&lt;/strong&gt; (&lt;code&gt;createdAt&lt;/code&gt;). Both mean the same thing. Do you rewrite your Python codebase in camelCase? &lt;strong&gt;No!&lt;/strong&gt; Use &lt;strong&gt;aliases&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 An &lt;strong&gt;alias&lt;/strong&gt; is an alternative name for a field, used when validating input or serializing output.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  🅰️ Basic alias
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alias&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"createdAt"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;createdAt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"2026-11-11"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# 2026-11-11&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Python still uses &lt;code&gt;created_at&lt;/code&gt;, but incoming data used &lt;code&gt;createdAt&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  🅱️ &lt;code&gt;alias&lt;/code&gt; vs &lt;code&gt;validation_alias&lt;/code&gt; vs &lt;code&gt;serialization_alias&lt;/code&gt;
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;alias&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Alias for &lt;strong&gt;both&lt;/strong&gt; validation and serialization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;validation_alias&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Alternative name when &lt;strong&gt;accepting input&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;serialization_alias&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Alternative name when &lt;strong&gt;producing output&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;validation_alias&lt;/code&gt;:&lt;/strong&gt; the frontend sends &lt;code&gt;{"userName": "deepika"}&lt;/code&gt; but your field is &lt;code&gt;username&lt;/code&gt;:&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;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;validation_alias&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"userName"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;serialization_alias&lt;/code&gt;:&lt;/strong&gt; you want &lt;code&gt;created_at&lt;/code&gt; to appear as &lt;code&gt;createdAt&lt;/code&gt; in outgoing JSON:&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;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serialization_alias&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"createdAt"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"2026-11-11"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model_dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;by_alias&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# {"createdAt": "2026-11-11"}&lt;/span&gt;

&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model_dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# {"created_at": "2026-11-11"}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;by_alias=True&lt;/code&gt; tells Pydantic to use the serialization alias instead of the Python field name.&lt;/p&gt;

&lt;h3&gt;
  
  
  🅲 Different input and output names
&lt;/h3&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;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;validation_alias&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"userName"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;serialization_alias&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"displayName"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"deepika"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model_dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;by_alias&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# {"displayName": "deepika"}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Incoming JSON  →  userName
Python model   →  username
Outgoing JSON  →  displayName
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🅳 Alias generators
&lt;/h3&gt;

&lt;p&gt;With 50 fields (&lt;code&gt;created_at&lt;/code&gt;, &lt;code&gt;updated_at&lt;/code&gt;, &lt;code&gt;first_name&lt;/code&gt;, ...) you don't want to write each alias by hand:&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;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ConfigDict&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;alias_generators&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;to_camel&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;model_config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ConfigDict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;alias_generator&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;to_camel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;validate_by_alias&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;validate_by_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;last_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Python field&lt;/th&gt;
&lt;th&gt;Alias&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;first_name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;firstName&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;last_name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;lastName&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;created_at&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;createdAt&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model_dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;by_alias&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# {"firstName": "Deepika", "lastName": "Rao", "createdAt": "2026-11-11"}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;📝 &lt;code&gt;validate_by_name&lt;/code&gt; and &lt;code&gt;validate_by_alias&lt;/code&gt; are the granular settings in newer Pydantic v2 releases. Older v2 examples commonly use &lt;code&gt;populate_by_name=True&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  16. ⚙️ &lt;code&gt;ConfigDict&lt;/code&gt;: Configuring Pydantic Models
&lt;/h2&gt;

&lt;p&gt;Sometimes you want rules for the &lt;strong&gt;entire model&lt;/strong&gt;: should extra fields be allowed? Should strings be auto-stripped? Should strict validation be on? Can the model be built from object attributes? Pydantic v2 uses &lt;code&gt;ConfigDict&lt;/code&gt;:&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;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ConfigDict&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;model_config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ConfigDict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;extra&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"forbid"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;str_strip_whitespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🚫 Understanding &lt;code&gt;extra&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The frontend sends:&lt;br&gt;
&lt;/p&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="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"deepika"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"deepika@gmail.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the model defines only &lt;code&gt;username&lt;/code&gt; and &lt;code&gt;email&lt;/code&gt;. What should happen?&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;extra="ignore"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ignore extra fields (&lt;strong&gt;default&lt;/strong&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;extra="forbid"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Reject&lt;/strong&gt; extra fields&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;extra="allow"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Allow extra fields and retain them&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For registration APIs, &lt;code&gt;extra="forbid"&lt;/code&gt; is useful when you want unexpected input &lt;strong&gt;rejected rather than silently ignored&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔧 Other useful settings
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;model_config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ConfigDict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strict&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Strict mode prevents many automatic type conversions. For example, an &lt;code&gt;int&lt;/code&gt; field generally won't accept &lt;code&gt;"22"&lt;/code&gt; in strict Python-object validation.&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;model_config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ConfigDict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;from_attributes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This lets Pydantic read data from &lt;strong&gt;object attributes&lt;/strong&gt;, which is handy with ORM objects (more on this in section 21).&lt;/p&gt;




&lt;h2&gt;
  
  
  17. 🪆 Nested Pydantic Models
&lt;/h2&gt;

&lt;p&gt;Real API responses are rarely flat. A post contains an ID, caption, author info and comments. Instead of one giant model, create &lt;strong&gt;smaller models and nest them&lt;/strong&gt;:&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;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;BaseModel&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CommentResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UserResponse&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UserResponse&lt;/span&gt;
    &lt;span class="n"&gt;comments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;CommentResponse&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example response:&lt;br&gt;
&lt;/p&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="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"caption"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"My first post!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"deepika"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"comments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;"id"&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="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Beautiful!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;"id"&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="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"sneha"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&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;p&gt;Pydantic validates the post, the author inside the post, every comment in the list, and the author inside each comment. This is &lt;strong&gt;nested model validation&lt;/strong&gt;. 🎯&lt;/p&gt;




&lt;h2&gt;
  
  
  18. 🎨 Enums and &lt;code&gt;Literal&lt;/code&gt; Values
&lt;/h2&gt;

&lt;p&gt;A post's visibility should only be &lt;code&gt;public&lt;/code&gt;, &lt;code&gt;private&lt;/code&gt; or &lt;code&gt;followers&lt;/code&gt;. You don't want the frontend sending &lt;code&gt;"secret_everyone"&lt;/code&gt;. Use an &lt;strong&gt;Enum&lt;/strong&gt;:&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;from&lt;/span&gt; &lt;span class="n"&gt;enum&lt;/span&gt; &lt;span class="nf"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;Enum&lt;/span&gt;

&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;BaseModel&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Visibility&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Enum&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="no"&gt;PUBLIC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"public"&lt;/span&gt;
    &lt;span class="no"&gt;PRIVATE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"private"&lt;/span&gt;
    &lt;span class="no"&gt;FOLLOWERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"followers"&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;visibility&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Visibility&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nc"&gt;PostCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;caption&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Hello"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;visibility&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"public"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="c1"&gt;# ✅&lt;/span&gt;
&lt;span class="nc"&gt;PostCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;caption&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Hello"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;visibility&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"everyone"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# ❌ rejected&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a small fixed set of strings you can use &lt;code&gt;Literal&lt;/code&gt;:&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;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;Literal&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;visibility&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Literal&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"public"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"private"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"followers"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  19. 🚨 Pydantic Validation Errors
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ValidationError&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;


&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"deepika"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"hello"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nc"&gt;ValidationError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;error.errors()&lt;/code&gt; returns structured information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;type&lt;/code&gt;: type of validation failure&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;loc&lt;/code&gt;: location of the invalid field&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;msg&lt;/code&gt;: human-readable message&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;input&lt;/code&gt;: the invalid input&lt;/li&gt;
&lt;li&gt;sometimes &lt;code&gt;ctx&lt;/code&gt;: additional error context&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ⚡ What happens in FastAPI?
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/users"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UserCreate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the frontend sends invalid JSON data, FastAPI normally returns HTTP &lt;strong&gt;422 Unprocessable Entity&lt;/strong&gt;:&lt;br&gt;
&lt;/p&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="nl"&gt;"detail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"greater_than_equal"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;"loc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;"msg"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Input should be greater than or equal to 18"&lt;/span&gt;
    &lt;span class="p"&gt;}&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;p&gt;The exact structure depends on the failure. The important distinction:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ &lt;strong&gt;Request&lt;/strong&gt; validation failure → normally HTTP &lt;strong&gt;422&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;💥 Invalid data &lt;strong&gt;returned by your endpoint&lt;/strong&gt; that violates &lt;code&gt;response_model&lt;/code&gt; → server-side response validation failure, normally HTTP &lt;strong&gt;500&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Why 500? Because the &lt;em&gt;server&lt;/em&gt; failed to produce the response promised by its own API contract.&lt;/p&gt;




&lt;h2&gt;
  
  
  20. 📦 &lt;code&gt;EmailStr&lt;/code&gt;, &lt;code&gt;HttpUrl&lt;/code&gt; and Other Built-in Types
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;EmailStr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;HttpUrl&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;EmailStr&lt;/span&gt;
    &lt;span class="n"&gt;profile_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;HttpUrl&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To use &lt;code&gt;EmailStr&lt;/code&gt;, install the email validation dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip install &lt;span class="s2"&gt;"pydantic[email]"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EmailStr&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Email validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;HttpUrl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;HTTP/HTTPS URL validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AnyUrl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;General URL validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IPvAnyAddress&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;IPv4 or IPv6 address validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PositiveInt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Integer greater than zero&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NonNegativeInt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Integer greater than or equal to zero&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PositiveFloat&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Positive floating-point number&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;PositiveInt&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PositiveInt&lt;/span&gt;
    &lt;span class="n"&gt;likes_count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 You don't need a custom validator for every simple rule. Use Pydantic's &lt;strong&gt;built-in types&lt;/strong&gt; whenever they fit.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  21. 🔌 &lt;code&gt;from_attributes&lt;/code&gt;: Connecting Pydantic with SQLAlchemy
&lt;/h2&gt;

&lt;p&gt;Especially important for a FastAPI + PostgreSQL project. Your SQLAlchemy model is a Python &lt;strong&gt;object&lt;/strong&gt; with attributes (&lt;code&gt;id&lt;/code&gt;, &lt;code&gt;username&lt;/code&gt;, &lt;code&gt;email&lt;/code&gt;), but a Pydantic model normally expects &lt;strong&gt;data&lt;/strong&gt;. How can Pydantic read the SQLAlchemy object's attributes?&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;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ConfigDict&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;model_config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ConfigDict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;from_attributes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;UserResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model_validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_object&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pydantic reads &lt;code&gt;user_object.id&lt;/code&gt;, &lt;code&gt;user_object.username&lt;/code&gt;, &lt;code&gt;user_object.email&lt;/code&gt; and creates a validated Pydantic object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🐘 PostgreSQL        → stored user record
        ▼
🗄️ SQLAlchemy ORM    → Python database object
        ▼
✔️ Pydantic model_validate()   (from_attributes=True)
        ▼
📤 UserResponse      → validated response object
        ▼
JSON response → ⚛️ React frontend
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;📌 &lt;code&gt;from_attributes=True&lt;/code&gt; does &lt;strong&gt;not&lt;/strong&gt; execute database queries. SQLAlchemy retrieves the object, and Pydantic reads its attributes.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  22. 🗂️ Pydantic Schemas in a Real FastAPI Folder Structure
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;backend/
│
├── app/
│   ├── api/
│   │   └── routes/
│   │       └── users.py
│   │
│   ├── schemas/
│   │   ├── __init__.py
│   │   └── user.py
│   │
│   ├── models/
│   │   └── user.py
│   │
│   ├── services/
│   │   └── user_service.py
│   │
│   └── main.py
│
└── tests/
    └── test_user_schemas.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Folder / file&lt;/th&gt;
&lt;th&gt;Responsibility&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;schemas/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pydantic request and response models&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;models/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SQLAlchemy database models&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;api/routes/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;FastAPI endpoint definitions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;services/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Application business logic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tests/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tests for schemas and functionality&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;schemas/user.py&lt;/code&gt;&lt;/strong&gt;&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;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;EmailStr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;EmailStr&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;EmailStr&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;api/routes/users.py&lt;/code&gt;&lt;/strong&gt;&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;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;APIRouter&lt;/span&gt;

&lt;span class="k"&gt;from&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;schemas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;UserCreate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;UserResponse&lt;/span&gt;

&lt;span class="n"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;APIRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/users"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response_model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;UserResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UserCreate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Call service layer here&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"username"&lt;/span&gt;&lt;span class="p"&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;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&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;email&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;p&gt;Your route doesn't contain every validation rule. Pydantic schemas handle &lt;strong&gt;data validation&lt;/strong&gt;, while services handle &lt;strong&gt;business logic&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔍 What happens when the frontend calls this API?
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;React sends JSON&lt;/li&gt;
&lt;li&gt;FastAPI receives the request&lt;/li&gt;
&lt;li&gt;FastAPI validates the body using &lt;code&gt;UserCreate&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;If validation fails, FastAPI returns a &lt;strong&gt;422&lt;/strong&gt; error&lt;/li&gt;
&lt;li&gt;If it succeeds, the endpoint receives a &lt;code&gt;UserCreate&lt;/code&gt; object&lt;/li&gt;
&lt;li&gt;The endpoint calls the service layer&lt;/li&gt;
&lt;li&gt;The service performs application logic and database operations&lt;/li&gt;
&lt;li&gt;The endpoint returns data&lt;/li&gt;
&lt;li&gt;FastAPI validates and serializes it using &lt;code&gt;UserResponse&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;React receives JSON&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  23. 🧪 Testing Pydantic Validators with pytest
&lt;/h2&gt;

&lt;p&gt;Knowing how to write validators is good. Knowing how to &lt;strong&gt;test&lt;/strong&gt; them is better.&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;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field_validator&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@field_validator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;validate_username&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s2"&gt;" "&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Spaces are not allowed"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;tests/test_user_schema.py&lt;/code&gt;:&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;import&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;ValidationError&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_valid_username&lt;/span&gt;&lt;span class="p"&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;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;UserCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"deepika"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;assert&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;username&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"deepika"&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_username_with_spaces&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raises&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ValidationError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nc"&gt;UserCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"deepika rao"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_username_too_short&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raises&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ValidationError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nc"&gt;UserCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ab"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;pytest.raises(ValidationError)&lt;/code&gt; means &lt;em&gt;"we expect this code to raise a validation error."&lt;/em&gt; If it does, the test passes. If the code unexpectedly accepts invalid data, the test fails.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to test:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Valid input&lt;/li&gt;
&lt;li&gt;❌ Missing required fields&lt;/li&gt;
&lt;li&gt;❌ Incorrect types&lt;/li&gt;
&lt;li&gt;📏 Minimum and maximum constraints&lt;/li&gt;
&lt;li&gt;🧪 Custom validator failures&lt;/li&gt;
&lt;li&gt;🧩 Cross-field validation&lt;/li&gt;
&lt;li&gt;🏷️ Serialization and aliases&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  24. 🚀 Important Advanced Concepts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🅰️ Strict validation
&lt;/h3&gt;

&lt;p&gt;By default, Pydantic tries to &lt;strong&gt;convert compatible values&lt;/strong&gt;:&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;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;


&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"22"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# "22" is converted into 22&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To reject conversions:&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;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strict&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# "22" is now rejected&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or for the whole model: &lt;code&gt;model_config = ConfigDict(strict=True)&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  🅱️ Computed fields
&lt;/h3&gt;

&lt;p&gt;Include a value &lt;strong&gt;calculated from other fields&lt;/strong&gt;:&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;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;computed_field&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;last_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;

    &lt;span class="nd"&gt;@computed_field&lt;/span&gt;
    &lt;span class="nd"&gt;@property&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;full_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"{self.first_name} {self.last_name}"&lt;/span&gt;


&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Deepika"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;last_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Rao"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&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;full_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Deepika Rao&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;user.model_dump()&lt;/code&gt; includes &lt;code&gt;full_name&lt;/code&gt; in the serialized output.&lt;/p&gt;

&lt;h3&gt;
  
  
  🅲 JSON Schema
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model_json_schema&lt;/span&gt;&lt;span class="p"&gt;(&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;p&gt;JSON Schema describes field names, types, required fields, constraints and descriptions. FastAPI uses this to generate the OpenAPI docs that power &lt;strong&gt;Swagger UI and ReDoc&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  🅳 Custom error types
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic_core&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;PydanticCustomError&lt;/span&gt;

&lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;PydanticCustomError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;"username_taken"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"Username '{username}' is already taken"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"deepika"&lt;/span&gt;&lt;span class="p"&gt;}&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;p&gt;Useful when you want a specific, machine-readable validation error type.&lt;/p&gt;

&lt;h3&gt;
  
  
  🅴 Validation context
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model_validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"deepika"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"allowed_usernames"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"deepika"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sneha"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;}&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;p&gt;A validator can read this through &lt;code&gt;info.context&lt;/code&gt;, which helps with dynamic rules. But rules requiring &lt;strong&gt;database queries&lt;/strong&gt; should usually stay in your service layer.&lt;/p&gt;




&lt;h2&gt;
  
  
  25. 📋 Complete Pydantic v2 Cheat Sheet
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BaseModel&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Creates Pydantic models&lt;/td&gt;
&lt;td&gt;&lt;code&gt;class User(BaseModel)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Field()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Defines field constraints&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Field(min_length=3)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;field_validator&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Validates individual fields&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@field_validator("username")&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;model_validator&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Validates multiple fields together&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@model_validator(mode="after")&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ValidationInfo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Accesses validation information&lt;/td&gt;
&lt;td&gt;&lt;code&gt;info.data&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ConfigDict&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Configures model behavior&lt;/td&gt;
&lt;td&gt;&lt;code&gt;extra="forbid"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EmailStr&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Validates email addresses&lt;/td&gt;
&lt;td&gt;&lt;code&gt;email: EmailStr&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;from_attributes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reads attributes from objects&lt;/td&gt;
&lt;td&gt;&lt;code&gt;from_attributes=True&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;validation_alias&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Changes accepted input field name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Field(validation_alias="userName")&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;serialization_alias&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Changes serialized field name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Field(serialization_alias="createdAt")&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;model_dump()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Converts model to dictionary&lt;/td&gt;
&lt;td&gt;&lt;code&gt;user.model_dump()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;model_dump_json()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Converts model to JSON string&lt;/td&gt;
&lt;td&gt;&lt;code&gt;user.model_dump_json()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;model_validate()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Validates Python data&lt;/td&gt;
&lt;td&gt;&lt;code&gt;User.model_validate(data)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;model_validate_json()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Validates JSON data&lt;/td&gt;
&lt;td&gt;&lt;code&gt;User.model_validate_json(data)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;model_copy()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Copies a model&lt;/td&gt;
&lt;td&gt;&lt;code&gt;user.model_copy()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;computed_field&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Adds calculated serialized fields&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@computed_field&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  26. 🎤 Common Interview Questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. What is Pydantic?&lt;/strong&gt;&lt;br&gt;
A Python library for data validation, parsing and serialization based on type annotations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. What's the difference between &lt;code&gt;BaseModel&lt;/code&gt; and a normal Python class?&lt;/strong&gt;&lt;br&gt;
A normal class stores whatever you pass without checking types. A &lt;code&gt;BaseModel&lt;/code&gt; validates (and where allowed, converts) data against its annotations and constraints, and provides serialization methods.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. What's the difference between &lt;code&gt;field_validator&lt;/code&gt; and &lt;code&gt;model_validator&lt;/code&gt;?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;field_validator&lt;/code&gt; validates individual fields. &lt;code&gt;model_validator&lt;/code&gt; validates the whole model, so it can check relationships between fields, such as password and confirm password.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. What's the difference between &lt;code&gt;before&lt;/code&gt; and &lt;code&gt;after&lt;/code&gt; validators?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;before&lt;/code&gt; runs before Pydantic's type validation and receives raw input, which suits cleaning and normalizing. &lt;code&gt;after&lt;/code&gt; runs after type validation and receives the already validated value, which suits business rules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Why do we separate request and response schemas?&lt;/strong&gt;&lt;br&gt;
Request schemas validate what clients send. Response schemas control what the API returns, preventing sensitive fields such as password hashes from being exposed, and giving the frontend a clear contract.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. What does &lt;code&gt;response_model&lt;/code&gt; do in FastAPI?&lt;/strong&gt;&lt;br&gt;
It tells FastAPI which schema describes and validates the output. FastAPI validates and serializes the returned data against it, filters fields not in the schema, and documents the response in OpenAPI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. What is serialization?&lt;/strong&gt;&lt;br&gt;
Converting a Python object or model into a format that can be transmitted or stored, such as a dictionary (&lt;code&gt;model_dump()&lt;/code&gt;) or a JSON string (&lt;code&gt;model_dump_json()&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8. What's the difference between &lt;code&gt;alias&lt;/code&gt; and &lt;code&gt;serialization_alias&lt;/code&gt;?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;alias&lt;/code&gt; applies to both validation (input) and serialization (output). &lt;code&gt;serialization_alias&lt;/code&gt; only changes the field name in outgoing data, used with &lt;code&gt;by_alias=True&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;9. What is &lt;code&gt;from_attributes=True&lt;/code&gt;?&lt;/strong&gt;&lt;br&gt;
A &lt;code&gt;ConfigDict&lt;/code&gt; setting that lets Pydantic read values from object attributes, such as SQLAlchemy ORM objects, when using &lt;code&gt;model_validate()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;10. What's the difference between &lt;code&gt;ValidationError&lt;/code&gt; and HTTP 422?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;ValidationError&lt;/code&gt; is the Pydantic exception raised when validation fails. HTTP 422 is the response FastAPI sends to the client when &lt;strong&gt;request&lt;/strong&gt; validation fails. A response-validation failure in your own endpoint is usually a server error (500).&lt;/p&gt;




&lt;h2&gt;
  
  
  27. 🏋️ Practical Exercises
&lt;/h2&gt;

&lt;p&gt;Try these in a separate Python file:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a &lt;code&gt;Product&lt;/code&gt; model with name, price and quantity. Price must be greater than zero&lt;/li&gt;
&lt;li&gt;Create a username &lt;code&gt;field_validator&lt;/code&gt; that removes extra spaces&lt;/li&gt;
&lt;li&gt;Create an email validator that converts emails to lowercase&lt;/li&gt;
&lt;li&gt;Create a registration model with &lt;code&gt;password&lt;/code&gt; and &lt;code&gt;confirm_password&lt;/code&gt;. Reject mismatches&lt;/li&gt;
&lt;li&gt;Create &lt;code&gt;UserCreate&lt;/code&gt; and &lt;code&gt;UserResponse&lt;/code&gt; schemas. Never expose the password in the response&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;Post&lt;/code&gt; model with a nested &lt;code&gt;UserResponse&lt;/code&gt; author and a list of comments&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;validation_alias&lt;/code&gt; and &lt;code&gt;serialization_alias&lt;/code&gt; to convert &lt;code&gt;username&lt;/code&gt; into &lt;code&gt;userName&lt;/code&gt; on input and &lt;code&gt;displayName&lt;/code&gt; on output&lt;/li&gt;
&lt;li&gt;Create a FastAPI POST endpoint using a request schema and &lt;code&gt;response_model&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Enable &lt;code&gt;extra="forbid"&lt;/code&gt; and test what happens when an unknown field is submitted&lt;/li&gt;
&lt;li&gt;Write pytest tests for valid and invalid usernames&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  28. 🏁 What to Remember
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;🏗️ Pydantic models define the structure of API data&lt;/li&gt;
&lt;li&gt;📏 &lt;code&gt;Field&lt;/code&gt; constraints enforce basic rules such as minimum lengths and positive numbers&lt;/li&gt;
&lt;li&gt;🧪 Custom validators enforce additional input rules&lt;/li&gt;
&lt;li&gt;📥 Request schemas define what the frontend can send&lt;/li&gt;
&lt;li&gt;📤 Response schemas define what the backend returns&lt;/li&gt;
&lt;li&gt;🔄 Serialization converts Python model data into dictionaries or JSON&lt;/li&gt;
&lt;li&gt;🏷️ Aliases let Python and JavaScript follow different naming conventions&lt;/li&gt;
&lt;li&gt;⚡ FastAPI integrates all of this into the request-response lifecycle&lt;/li&gt;
&lt;li&gt;🗄️ SQLAlchemy handles database persistence; Pydantic handles API data validation and serialization&lt;/li&gt;
&lt;li&gt;🧠 Services handle business logic that shouldn't live inside your schemas&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;🎯 &lt;strong&gt;One important architectural lesson:&lt;/strong&gt; Pydantic validates whether data is structurally and semantically acceptable. It does &lt;strong&gt;not&lt;/strong&gt; replace database constraints, authentication, authorization, or business logic.&lt;/p&gt;
&lt;/blockquote&gt;




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

&lt;p&gt;Implement these concepts in a mini &lt;strong&gt;registration and post-creation API&lt;/strong&gt;: write the schemas, add validators, wire up &lt;code&gt;response_model&lt;/code&gt;, and cover it all with pytest. 💡&lt;/p&gt;

&lt;p&gt;If this post helped you, drop a ❤️ or 🦄 and share it with a friend learning FastAPI. Happy coding! 👩‍💻✨&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>fastapi</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>FastAPI Fundamentals &amp; Dependency Injection: Explained from Absolute Zero ⚡</title>
      <dc:creator>Deepika Pusala</dc:creator>
      <pubDate>Wed, 11 Nov 2026 06:05:45 +0000</pubDate>
      <link>https://dev.to/deepika_pusala/fastapi-fundamentals-dependency-injection-explained-from-absolute-zero-36ch</link>
      <guid>https://dev.to/deepika_pusala/fastapi-fundamentals-dependency-injection-explained-from-absolute-zero-36ch</guid>
      <description>&lt;h1&gt;
  
  
  ⚡ FastAPI Fundamentals &amp;amp; Dependency Injection: Let's Learn from ZERO!
&lt;/h1&gt;

&lt;p&gt;Ever wondered how FastAPI &lt;strong&gt;receives requests, validates data, calls your Python functions, connects to databases, handles authentication, and organizes a real-world backend&lt;/strong&gt;? 🤔&lt;/p&gt;

&lt;p&gt;In this post we'll learn it all from scratch, with simple examples, real-life analogies, Python code, and folder structures. No prior backend knowledge needed! 🚀&lt;/p&gt;




&lt;h2&gt;
  
  
  1. 🤔 What exactly is FastAPI?
&lt;/h2&gt;

&lt;p&gt;Imagine you're building &lt;strong&gt;Instagram&lt;/strong&gt;. Your app has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⚛️ A React frontend&lt;/li&gt;
&lt;li&gt;🐍 A Python FastAPI backend&lt;/li&gt;
&lt;li&gt;🐘 A PostgreSQL database&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A user clicks the &lt;strong&gt;Like&lt;/strong&gt; button. What happens?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;⚛️ Frontend (React)
   User clicks Like
        │  HTTP request
        ▼
⚡ Backend (FastAPI)
   Receives request, validates data, executes logic
        │  Database query
        ▼
🐘 PostgreSQL Database
   Stores the like information
        │
        ▼
⚡ FastAPI returns a response
   200 OK → { "message": "Post liked successfully" }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;FastAPI&lt;/strong&gt; is a modern Python web framework used to build &lt;strong&gt;APIs&lt;/strong&gt;. An &lt;strong&gt;API&lt;/strong&gt; (Application Programming Interface) lets two software applications communicate.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;FastAPI helps us:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;🛣️ Create API endpoints&lt;/li&gt;
&lt;li&gt;📥 Receive data from frontend applications&lt;/li&gt;
&lt;li&gt;✔️ Validate incoming data&lt;/li&gt;
&lt;li&gt;🧠 Execute business logic&lt;/li&gt;
&lt;li&gt;🗄️ Communicate with databases&lt;/li&gt;
&lt;li&gt;📤 Return responses in JSON&lt;/li&gt;
&lt;li&gt;📖 Automatically generate API documentation
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&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="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;home&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Hello World"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;from fastapi import FastAPI&lt;/code&gt; imports the FastAPI class&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;app = FastAPI()&lt;/code&gt; creates our application&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@app.get("/")&lt;/code&gt; creates a &lt;strong&gt;GET&lt;/strong&gt; endpoint at the root URL&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;def home()&lt;/code&gt; is the Python function that handles the request&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;return&lt;/code&gt; sends the response back to the client&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Visit &lt;code&gt;http://127.0.0.1:8000/&lt;/code&gt; and you get:&lt;br&gt;
&lt;/p&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="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Hello World"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's your first FastAPI endpoint! 🎉&lt;/p&gt;




&lt;h2&gt;
  
  
  2. 📬 Understanding API Parameters
&lt;/h2&gt;

&lt;p&gt;When a frontend calls a backend, it often needs to send information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which post should we retrieve?&lt;/li&gt;
&lt;li&gt;Which page of posts should we show?&lt;/li&gt;
&lt;li&gt;What data should we save?&lt;/li&gt;
&lt;li&gt;Which user is making the request?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;FastAPI has three parameter types you need to know:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;🛤️ Path parameters&lt;/li&gt;
&lt;li&gt;❓ Query parameters&lt;/li&gt;
&lt;li&gt;📦 Body parameters&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  2.1 🛤️ Path parameters
&lt;/h3&gt;

&lt;p&gt;Values passed &lt;strong&gt;directly inside the URL path&lt;/strong&gt;. You have 100 posts and want post number 25:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /posts/25
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here &lt;code&gt;25&lt;/code&gt; is the path parameter, because it's part of the URL path itself.&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;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&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="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/posts/{post_id}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"post_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Post retrieved"&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;ul&gt;
&lt;li&gt;
&lt;code&gt;{post_id}&lt;/code&gt; is a &lt;strong&gt;dynamic placeholder&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The actual value comes from the URL&lt;/li&gt;
&lt;li&gt;FastAPI extracts it and passes it to your function&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you request &lt;code&gt;GET /posts/25&lt;/code&gt;, FastAPI internally calls:&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="nf"&gt;get_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response:&lt;br&gt;
&lt;/p&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="nl"&gt;"post_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Post retrieved"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice we wrote &lt;code&gt;post_id: int&lt;/code&gt;. What if somebody sends &lt;code&gt;GET /posts/hello&lt;/code&gt;? FastAPI automatically validates the value. Since &lt;code&gt;"hello"&lt;/code&gt; can't become an integer, it returns a &lt;strong&gt;validation error (HTTP 422)&lt;/strong&gt;. You don't write that validation yourself! ✨&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Real-life examples:&lt;/em&gt; &lt;code&gt;/users/10&lt;/code&gt;, &lt;code&gt;/posts/25&lt;/code&gt;, &lt;code&gt;/comments/100&lt;/code&gt;, &lt;code&gt;/products/500&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 Path parameters generally &lt;strong&gt;identify a particular resource&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2.2 ❓ Query parameters
&lt;/h3&gt;

&lt;p&gt;Extra values passed &lt;strong&gt;after a &lt;code&gt;?&lt;/code&gt;&lt;/strong&gt; in the URL. Want only 10 of your 100 posts?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /posts?limit=10
GET /posts?limit=10&amp;amp;skip=20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/posts&lt;/code&gt; = endpoint&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;?&lt;/code&gt; = start of query parameters&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;limit=10&lt;/code&gt; = a query parameter&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;amp;&lt;/code&gt; separates multiple query parameters
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/posts"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;skip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&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="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"skip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;skip&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;p&gt;&lt;code&gt;limit: int = 10&lt;/code&gt; means &lt;code&gt;limit&lt;/code&gt; should be an integer, and if the user doesn't provide it, use &lt;code&gt;10&lt;/code&gt;. Same idea for &lt;code&gt;skip&lt;/code&gt;, defaulting to &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Request&lt;/th&gt;
&lt;th&gt;Response&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /posts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{"limit": 10, "skip": 0}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /posts?limit=5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{"limit": 5, "skip": 0}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /posts?limit=20&amp;amp;skip=40&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{"limit": 20, "skip": 40}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Real-life uses:&lt;/em&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Query parameter&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;?limit=10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Get 10 records&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;?page=2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Get page 2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;?search=deepika&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Search for something&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;?sort=latest&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sort by latest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;?category=technology&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Filter by category&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In a social feed, query parameters are perfect for &lt;strong&gt;pagination, filtering and searching&lt;/strong&gt;, like &lt;code&gt;GET /feed?limit=10&amp;amp;cursor=abc123&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.3 📦 Body parameters (request body)
&lt;/h3&gt;

&lt;p&gt;A user wants to &lt;strong&gt;create a new post&lt;/strong&gt; and must send a caption, image URL and location. That data usually goes in the &lt;strong&gt;request body&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /posts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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="nl"&gt;"caption"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Beautiful evening!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"image_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"https://example.com/image.jpg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"location"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Bengaluru"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;BaseModel&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;image_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;


&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/posts"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PostCreate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Post created"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"caption"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;caption&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;ul&gt;
&lt;li&gt;
&lt;code&gt;PostCreate&lt;/code&gt; is a &lt;strong&gt;Pydantic model&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;post: PostCreate&lt;/code&gt; tells FastAPI to expect request-body data matching that model&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;post.caption&lt;/code&gt; and &lt;code&gt;post.image_url&lt;/code&gt; access the values&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2.4 ⚖️ Path vs Query vs Body
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;Question it answers&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🛤️ &lt;strong&gt;Path&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Identifies which resource&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET /posts/25&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Which post? Post 25&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;❓ &lt;strong&gt;Query&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Controls how we retrieve&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET /posts?limit=10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;How many posts? 10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;📦 &lt;strong&gt;Body&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Carries data to create/update&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;POST /posts&lt;/code&gt; + &lt;code&gt;{"caption": "My new post"}&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;What do we save? The caption&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 &lt;strong&gt;Quick rule:&lt;/strong&gt; Path = &lt;em&gt;which resource?&lt;/em&gt; · Query = &lt;em&gt;how should we retrieve it?&lt;/em&gt; · Body = &lt;em&gt;what data are we sending?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  3. ✔️ Pydantic Models: Request and Response Schemas
&lt;/h2&gt;

&lt;p&gt;Imagine your frontend sends:&lt;br&gt;
&lt;/p&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="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"deepika"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"twenty"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"not-an-email"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Age is a string instead of an integer, and the email isn't valid. Should your backend blindly accept that? &lt;strong&gt;Obviously not!&lt;/strong&gt; We need a way to validate incoming data. Enter &lt;strong&gt;Pydantic&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.1 🛂 What is Pydantic?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;Pydantic&lt;/strong&gt; is a Python library for &lt;strong&gt;data validation and parsing&lt;/strong&gt;. It checks whether incoming data follows the structure and rules we've defined.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Think of it as an &lt;strong&gt;airport security checkpoint&lt;/strong&gt; ✈️: before passengers enter, their details are checked. Before request data reaches your business logic, Pydantic validates it.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.2 🏗️ Creating a Pydantic model
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;BaseModel&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;BaseModel&lt;/code&gt; is Pydantic's base class. We inherit from it to create a model.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Expected type&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;username&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;String&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;age&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Integer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;email&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;String&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  3.3 ✅ Valid data arrives
&lt;/h3&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="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"deepika"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"deepika@example.com"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pydantic validates it, creates a Python model object, and you access &lt;code&gt;user.username&lt;/code&gt;, &lt;code&gt;user.age&lt;/code&gt;, &lt;code&gt;user.email&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.4 ❌ Invalid data arrives
&lt;/h3&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="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"deepika"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"hello"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"deepika@example.com"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;"hello"&lt;/code&gt; can't become an integer, so FastAPI automatically returns:&lt;br&gt;
&lt;/p&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="nl"&gt;"detail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"int_parsing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;"loc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;"msg"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Input should be a valid integer"&lt;/span&gt;
    &lt;span class="p"&gt;}&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;blockquote&gt;
&lt;p&gt;💡 FastAPI handles this request-validation error automatically. You don't write manual &lt;code&gt;if&lt;/code&gt; checks for every field.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3.5 📏 Validation rules
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;EmailStr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ge&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;le&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;EmailStr&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;min_length=3&lt;/code&gt;: username needs at least 3 characters&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;max_length=20&lt;/code&gt;: username can't exceed 20 characters&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ge=18&lt;/code&gt;: age must be &lt;strong&gt;g&lt;/strong&gt;reater than or &lt;strong&gt;e&lt;/strong&gt;qual to 18&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;le=100&lt;/code&gt;: age must be &lt;strong&gt;l&lt;/strong&gt;ess than or &lt;strong&gt;e&lt;/strong&gt;qual to 100&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;EmailStr&lt;/code&gt;: validates email format&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;📝 &lt;code&gt;EmailStr&lt;/code&gt; requires the optional &lt;code&gt;email-validator&lt;/code&gt; package.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3.6 🕳️ Optional fields and defaults
&lt;/h3&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;UserCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;bio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;str | None&lt;/code&gt; means the value can be a string &lt;strong&gt;or&lt;/strong&gt; absent/null&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;= None&lt;/code&gt; provides a default value&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3.7 🔄 Request schemas vs Response schemas
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;🎯 A VERY important distinction in real backends!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A user creates an account and sends:&lt;br&gt;
&lt;/p&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="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"deepika"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"deepika@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"secret123"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's a &lt;strong&gt;request schema&lt;/strong&gt;. But should we return the password? &lt;strong&gt;Absolutely not!&lt;/strong&gt; 🙅 We return:&lt;br&gt;
&lt;/p&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="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"deepika"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"deepika@example.com"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's a &lt;strong&gt;response schema&lt;/strong&gt;.&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;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;BaseModel&lt;/span&gt;


&lt;span class="c1"&gt;# Incoming data&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;


&lt;span class="c1"&gt;# Outgoing data&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Connect them in FastAPI:&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="nd"&gt;@app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/users"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response_model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;UserResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UserCreate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"username"&lt;/span&gt;&lt;span class="p"&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;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&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;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"password"&lt;/span&gt;&lt;span class="p"&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;password&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;p&gt;Even though the function returns a dict &lt;strong&gt;containing the password&lt;/strong&gt;, the &lt;code&gt;response_model&lt;/code&gt; filters the output to the fields in &lt;code&gt;UserResponse&lt;/code&gt;. The client never sees the password. 🔐&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why separate request and response schemas?&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;✔️ Validate incoming data&lt;/li&gt;
&lt;li&gt;🎛️ Control outgoing data&lt;/li&gt;
&lt;li&gt;🔒 Prevent sensitive information from being exposed&lt;/li&gt;
&lt;li&gt;📜 Maintain a clear API contract&lt;/li&gt;
&lt;li&gt;🧹 Make code easier to maintain&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Common naming conventions:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UserCreate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Incoming user creation data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UserUpdate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Incoming user update data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UserResponse&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Outgoing user data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PostCreate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Incoming post creation data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PostResponse&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Outgoing post data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LoginRequest&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Login credentials&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  4. 💉 Dependency Injection (DI): The MOST Important Concept!
&lt;/h2&gt;

&lt;p&gt;Don't worry about the big name. Let's use a simple example.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.1 🚨 First, the problem
&lt;/h3&gt;

&lt;p&gt;Whenever a user views their feed, your backend needs a database:&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;get_feed&lt;/span&gt;&lt;span class="p"&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;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;connect_to_database&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now imagine &lt;strong&gt;20 endpoints&lt;/strong&gt; needing database access. Would you write &lt;code&gt;db = connect_to_database()&lt;/code&gt; inside all 20 functions? That causes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;🔁 Repeated code&lt;/li&gt;
&lt;li&gt;😩 Hard to maintain&lt;/li&gt;
&lt;li&gt;🧪 Hard to test&lt;/li&gt;
&lt;li&gt;🔌 Hard to manage database connections&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What if &lt;strong&gt;one function provided the database connection&lt;/strong&gt; whenever another function needed it? That's the basic idea of Dependency Injection.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.2 🧩 What is a dependency?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 A &lt;strong&gt;dependency&lt;/strong&gt; is something a function or class &lt;strong&gt;needs&lt;/strong&gt; to do its work.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&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;get_feed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here &lt;code&gt;db&lt;/code&gt; is a dependency, because &lt;code&gt;get_feed()&lt;/code&gt; depends on it. Other examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🗄️ Database session&lt;/li&gt;
&lt;li&gt;👤 Authenticated user&lt;/li&gt;
&lt;li&gt;⚙️ Configuration settings&lt;/li&gt;
&lt;li&gt;📝 Logger&lt;/li&gt;
&lt;li&gt;🌍 External API client&lt;/li&gt;
&lt;li&gt;⚡ Cache connection&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4.3 💉 What is Dependency Injection?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;Dependency Injection&lt;/strong&gt; means: instead of creating everything a function needs by itself, we &lt;strong&gt;provide&lt;/strong&gt; those things &lt;strong&gt;from outside&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;❌ Without DI:&lt;/strong&gt;&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;get_feed&lt;/span&gt;&lt;span class="p"&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;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;connect_to_database&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_posts&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;p&gt;The function creates its own dependency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅ With DI:&lt;/strong&gt;&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;get_feed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_posts&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;p&gt;The dependency is provided from outside. This is more flexible. For example, during &lt;strong&gt;testing&lt;/strong&gt; you can pass in a &lt;strong&gt;fake database&lt;/strong&gt; instead of the real one. 🧪&lt;/p&gt;

&lt;h3&gt;
  
  
  4.4 🎯 What is &lt;code&gt;Depends()&lt;/code&gt; in FastAPI?
&lt;/h3&gt;

&lt;p&gt;FastAPI has a built-in function, &lt;code&gt;Depends()&lt;/code&gt;, to declare dependencies:&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;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&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="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_database&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"Database connection"&lt;/span&gt;


&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/posts"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_database&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"database"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Posts retrieved"&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;p&gt;&lt;code&gt;get_database()&lt;/code&gt; is our &lt;strong&gt;dependency provider&lt;/strong&gt;. Now the interesting part:&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;get_posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_database&lt;/span&gt;&lt;span class="p"&gt;)&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;p&gt;&lt;code&gt;Depends(get_database)&lt;/code&gt; tells FastAPI: &lt;em&gt;"Before executing &lt;code&gt;get_posts()&lt;/code&gt;, run &lt;code&gt;get_database()&lt;/code&gt; and inject its result into the &lt;code&gt;db&lt;/code&gt; parameter."&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client sends GET /posts
        ▼
FastAPI sees Depends(get_database)
        ▼
Executes get_database()
        ▼
Injects the returned value into db
        ▼
Executes get_posts(db)
        ▼
Response returned
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You &lt;strong&gt;never manually call&lt;/strong&gt; &lt;code&gt;get_database()&lt;/code&gt; inside &lt;code&gt;get_posts()&lt;/code&gt;. FastAPI resolves it for you. That's the beauty of &lt;code&gt;Depends()&lt;/code&gt;! ✨&lt;/p&gt;

&lt;h3&gt;
  
  
  4.5 🗄️ A practical example: database dependency
&lt;/h3&gt;

&lt;p&gt;With PostgreSQL and SQLAlchemy, we create a &lt;strong&gt;database session provider&lt;/strong&gt;:&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;from&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;database&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;SessionLocal&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_db&lt;/span&gt;&lt;span class="p"&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;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SessionLocal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;
    &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&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;ul&gt;
&lt;li&gt;
&lt;code&gt;db = SessionLocal()&lt;/code&gt; creates a new database session (a temporary working connection to the database)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;yield db&lt;/code&gt; hands the session to the function that requested it&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;finally: db.close()&lt;/code&gt; closes the session after the request finishes, &lt;strong&gt;even if an exception occurs&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;🤔 &lt;strong&gt;Why &lt;code&gt;yield&lt;/code&gt; instead of &lt;code&gt;return&lt;/code&gt;?&lt;/strong&gt; A generator dependency lets FastAPI run &lt;strong&gt;cleanup code after the request completes&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now use it in an endpoint:&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;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;orm&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;Session&lt;/span&gt;


&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/posts"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_db&lt;/span&gt;&lt;span class="p"&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;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;db: Session&lt;/code&gt; is the expected SQLAlchemy session type&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Depends(get_db)&lt;/code&gt; tells FastAPI how to provide it&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;db.query(Post).all()&lt;/code&gt; retrieves all Post records&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We never created the session manually. FastAPI manages its lifecycle through the dependency.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📝 This is a simplified &lt;strong&gt;synchronous&lt;/strong&gt; SQLAlchemy example. Async SQLAlchemy uses an async session and an async dependency.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  4.6 🔐 DI for authentication
&lt;/h3&gt;

&lt;p&gt;A user wants to create a post. Before allowing it we check: &lt;em&gt;Is the user logged in? Is their token valid? Which user is this?&lt;/em&gt; Create a dependency:&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;get_current_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Validate access token&lt;/span&gt;
    &lt;span class="c1"&gt;# Find user in database&lt;/span&gt;
    &lt;span class="c1"&gt;# Return authenticated user&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then use it:&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="nd"&gt;@app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/posts"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_current_user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Post created"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&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;p&gt;What happens:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The client sends a request&lt;/li&gt;
&lt;li&gt;FastAPI sees &lt;code&gt;Depends(get_current_user)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;It runs the authentication dependency&lt;/li&gt;
&lt;li&gt;The dependency validates the user's identity&lt;/li&gt;
&lt;li&gt;It returns the authenticated user&lt;/li&gt;
&lt;li&gt;FastAPI injects that user into &lt;code&gt;current_user&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The endpoint executes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If authentication fails, the dependency can raise an &lt;strong&gt;HTTP 401 or 403&lt;/strong&gt;, and the endpoint won't run normally. With 15 endpoints needing authentication, you &lt;strong&gt;reuse the same dependency&lt;/strong&gt; instead of repeating token logic 15 times. ♻️&lt;/p&gt;




&lt;h2&gt;
  
  
  5. 🔄 Inversion of Control (IoC)
&lt;/h2&gt;

&lt;p&gt;You understand DI, so IoC is easier now.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.1 🤔 What is IoC?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;Inversion of Control&lt;/strong&gt; means transferring the responsibility of managing something &lt;strong&gt;from your code to an external framework or system&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;❌ Without IoC&lt;/strong&gt;, you decide when to create the session, how to pass it, and when to close it:&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;get_posts&lt;/span&gt;&lt;span class="p"&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;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_database_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&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;p&gt;&lt;strong&gt;✅ With IoC using FastAPI:&lt;/strong&gt;&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;get_posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_posts&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;p&gt;FastAPI manages dependency resolution and the cleanup lifecycle for the request. You simply &lt;strong&gt;declare what you need&lt;/strong&gt;. Control has shifted from your endpoint to the framework. That's Inversion of Control. 🎛️&lt;/p&gt;

&lt;h3&gt;
  
  
  5.2 🔗 Relationship between IoC and DI
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;IoC&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The general principle of handing over control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A technique used to implement IoC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;Depends()&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;FastAPI's mechanism for declaring dependencies&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Inversion of Control (IoC)   → the general design principle
        ▼
Dependency Injection (DI)    → a way to achieve IoC
        ▼
FastAPI Depends()            → a built-in dependency mechanism
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🎤 Interview-ready explanation
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Inversion of Control is a design principle where the responsibility of managing dependencies is transferred from our application code to the framework. In FastAPI, we achieve this using Dependency Injection through &lt;code&gt;Depends()&lt;/code&gt;. For example, instead of manually creating database sessions inside every route, we declare &lt;code&gt;Depends(get_db)&lt;/code&gt;, and FastAPI resolves and manages the dependency."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  6. 🗂️ APIRouter: Organizing API Endpoints
&lt;/h2&gt;

&lt;p&gt;As your app grows, you might write everything in one file:&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;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&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="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/users"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/users"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/posts"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/posts"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/comments"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_comments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Imagine &lt;strong&gt;100 endpoints&lt;/strong&gt; in one file! 😵 We need to split them by responsibility. That's &lt;strong&gt;APIRouter&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  6.1 🤔 What is APIRouter?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;APIRouter&lt;/strong&gt; is a FastAPI class that organizes related endpoints into separate modules.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Think of dividing a company into &lt;strong&gt;departments&lt;/strong&gt;: the user department handles users, the post department handles posts, the comment department handles comments, the auth department handles authentication.&lt;/p&gt;

&lt;h3&gt;
  
  
  6.2 📁 Recommended folder structure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;backend/
└── app/
    ├── api/
    │   ├── users.py       # User endpoints
    │   ├── posts.py       # Post endpoints
    │   └── comments.py    # Comment endpoints
    └── main.py            # Main application
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6.3 🛠️ Creating a router
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;users.py&lt;/code&gt;:&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;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;APIRouter&lt;/span&gt;

&lt;span class="n"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;APIRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"All users"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="nd"&gt;@router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/{user_id}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="nd"&gt;@router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"User created"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;router = APIRouter()&lt;/code&gt; creates a separate router. Instead of &lt;code&gt;@app.get("/")&lt;/code&gt; we write &lt;code&gt;@router.get("/")&lt;/code&gt;, because these endpoints belong to the &lt;strong&gt;router&lt;/strong&gt;, not directly to the main app.&lt;/p&gt;

&lt;h3&gt;
  
  
  6.4 🔌 Connecting the router to the main app
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;main.py&lt;/code&gt;:&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;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;

&lt;span class="k"&gt;from&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;api&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&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="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;users&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="s2"&gt;"/users"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Users"&lt;/span&gt;&lt;span class="p"&gt;]&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;ul&gt;
&lt;li&gt;
&lt;code&gt;include_router()&lt;/code&gt; registers the router with the main app&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;prefix="/users"&lt;/code&gt; means every endpoint in that router starts with &lt;code&gt;/users&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tags=["Users"]&lt;/code&gt; groups these endpoints under a &lt;strong&gt;Users&lt;/strong&gt; section in Swagger docs&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Router endpoint&lt;/th&gt;
&lt;th&gt;Final endpoint&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@router.get("/")&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET /users/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@router.get("/{user_id}")&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET /users/{user_id}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@router.post("/")&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;POST /users/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  6.5 ✅ Why use APIRouter?
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;🧩 &lt;strong&gt;Modularity:&lt;/strong&gt; separate related endpoints&lt;/li&gt;
&lt;li&gt;🛠️ &lt;strong&gt;Maintainability:&lt;/strong&gt; easier to modify code&lt;/li&gt;
&lt;li&gt;♻️ &lt;strong&gt;Reusability:&lt;/strong&gt; routers can be included in different apps&lt;/li&gt;
&lt;li&gt;📖 &lt;strong&gt;Readability:&lt;/strong&gt; easier for developers to understand&lt;/li&gt;
&lt;li&gt;📈 &lt;strong&gt;Scalability:&lt;/strong&gt; suitable for large backends&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Important:&lt;/strong&gt; APIRouter does &lt;strong&gt;not&lt;/strong&gt; create a separate server or application. It organizes routes &lt;strong&gt;inside one FastAPI application&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  7. 📖 Automatic Interactive Documentation: Swagger UI and ReDoc
&lt;/h2&gt;

&lt;p&gt;One of FastAPI's coolest features! You create 50 endpoints. How will your frontend developer know which endpoints exist, which methods they support, what parameters and request body they need, and what they return? You could write docs manually, &lt;strong&gt;but FastAPI generates them automatically!&lt;/strong&gt; 🪄&lt;/p&gt;

&lt;h3&gt;
  
  
  7.1 🧪 Swagger UI
&lt;/h3&gt;

&lt;p&gt;An &lt;strong&gt;interactive interface&lt;/strong&gt; to explore and test your API. After starting your app, visit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://127.0.0.1:8000/docs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;View all endpoints&lt;/li&gt;
&lt;li&gt;Expand an endpoint&lt;/li&gt;
&lt;li&gt;See required parameters&lt;/li&gt;
&lt;li&gt;Enter request data&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Try it out&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Execute&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;View the response and HTTP status code&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can test your backend &lt;strong&gt;without writing any frontend code&lt;/strong&gt;! 🎉&lt;/p&gt;

&lt;h3&gt;
  
  
  7.2 📚 ReDoc
&lt;/h3&gt;

&lt;p&gt;Another automatically generated documentation interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://127.0.0.1:8000/redoc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It presents your API in a more &lt;strong&gt;reference-oriented, organized layout&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  7.3 ⚖️ Swagger UI vs ReDoc
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Swagger UI&lt;/th&gt;
&lt;th&gt;ReDoc&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;URL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/docs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/redoc&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interactive testing&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Primarily documentation-focused&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API exploration&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Layout&lt;/td&gt;
&lt;td&gt;Interactive interface&lt;/td&gt;
&lt;td&gt;Reference-style documentation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Generated automatically&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Both are generated from FastAPI's &lt;strong&gt;OpenAPI schema&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  7.4 📜 What is OpenAPI?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;OpenAPI&lt;/strong&gt; is a standard specification that describes your API: available endpoints, HTTP methods, request parameters, request body schemas, response schemas and authentication requirements.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;FastAPI generates it automatically at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://127.0.0.1:8000/openapi.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Swagger UI and ReDoc read this schema to display the docs.&lt;/p&gt;

&lt;h3&gt;
  
  
  7.5 🪄 How does FastAPI generate docs automatically?
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/posts/{post_id}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"post_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;FastAPI understands the HTTP method (GET), the endpoint (&lt;code&gt;/posts/{post_id}&lt;/code&gt;), the parameter name (&lt;code&gt;post_id&lt;/code&gt;) and its type (integer). Likewise:&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;PostCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;image_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;FastAPI understands the request-body structure, and:&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="nd"&gt;@app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/posts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response_model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;PostResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;tells it the expected response schema. It collects all this into OpenAPI docs. That's why &lt;strong&gt;type hints and Pydantic models matter so much&lt;/strong&gt; in FastAPI. 💡&lt;/p&gt;




&lt;h2&gt;
  
  
  8. 🎯 Single Responsibility Principle (SRP)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;SRP&lt;/strong&gt; is one of the five &lt;strong&gt;SOLID&lt;/strong&gt; principles of object-oriented design. Let's focus on just this one.&lt;/p&gt;

&lt;h3&gt;
  
  
  8.1 🤔 What is SRP?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 A function, class, or module should have &lt;strong&gt;one clear responsibility&lt;/strong&gt;, or &lt;strong&gt;one reason to change&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Think of a restaurant 🍽️: the chef prepares food, the cashier handles payments, the waiter takes orders, the manager manages operations. If one person did everything, it'd be chaos. Similarly, we shouldn't put all backend responsibilities into one function.&lt;/p&gt;

&lt;h3&gt;
  
  
  8.2 ❌ The problem: without SRP
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/posts"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PostCreate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Validate user&lt;/span&gt;
    &lt;span class="c1"&gt;# Connect to database&lt;/span&gt;
    &lt;span class="c1"&gt;# Check image URL&lt;/span&gt;
    &lt;span class="c1"&gt;# Create post in database&lt;/span&gt;
    &lt;span class="c1"&gt;# Send notification&lt;/span&gt;
    &lt;span class="c1"&gt;# Return response&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Post created"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function handles too many responsibilities:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Receiving the HTTP request&lt;/li&gt;
&lt;li&gt;Validating user permissions&lt;/li&gt;
&lt;li&gt;Handling database operations&lt;/li&gt;
&lt;li&gt;Executing post creation logic&lt;/li&gt;
&lt;li&gt;Sending notifications&lt;/li&gt;
&lt;li&gt;Returning the HTTP response&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Change notification logic? Modify this function. Change database logic? Modify it again. It becomes hard to maintain. 😩&lt;/p&gt;

&lt;h3&gt;
  
  
  8.3 ✅ Applying SRP: separate responsibilities
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🛣️ Router Layer      posts.py             Receives HTTP requests, reads parameters, calls services, returns responses
        ▼
🧠 Service Layer      post_service.py      Contains business logic and rules
        ▼
📚 Repository Layer   post_repository.py   Handles database operations (insert, update, retrieve)
        ▼
🗄️ Database           Stores application data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;A. Router layer&lt;/strong&gt; (&lt;code&gt;posts.py&lt;/code&gt;)&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="nd"&gt;@router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PostCreate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;post_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Its job: receive the request, get the request data, obtain the database dependency, call the service, return the result. It doesn't need to know the internal business rules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;B. Service layer&lt;/strong&gt; (&lt;code&gt;post_service.py&lt;/code&gt;)&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;create_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Caption is required"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;post_repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Its job: implement business rules, decide what should happen, coordinate the required operations (like checking whether a user is allowed to create a post).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C. Repository layer&lt;/strong&gt; (&lt;code&gt;post_repository.py&lt;/code&gt;)&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;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;new_post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;caption&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;refresh&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;new_post&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Its job: talk to the database (insert, retrieve, update, delete).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why is this better?&lt;/strong&gt; If your database changes, you modify the repository layer without rewriting all your HTTP endpoints. If business rules change, you update the service layer. The app becomes easier to maintain and test. 🧪&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📝 These layers are an &lt;strong&gt;architectural choice&lt;/strong&gt;, not a mandatory FastAPI requirement. Small apps can have fewer layers. The key is to avoid mixing unrelated responsibilities.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  9. 🧵 Putting Everything Together: A Real FastAPI Example
&lt;/h2&gt;

&lt;p&gt;Let's build a mini Instagram backend with an endpoint to create posts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;backend/
└── app/
    ├── api/
    │   └── posts.py              # Routes
    ├── schemas/
    │   └── post.py               # Pydantic models
    ├── services/
    │   └── post_service.py       # Business logic
    ├── repositories/
    │   └── post_repository.py    # Database operations
    └── main.py                   # Application entry point
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 1: create Pydantic schemas&lt;/strong&gt; (&lt;code&gt;schemas/post.py&lt;/code&gt;)&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;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;BaseModel&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: create the repository&lt;/strong&gt; (&lt;code&gt;repositories/post_repository.py&lt;/code&gt;)&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;create_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;new_post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"caption"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;new_post&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;📝 A simplified repository using a dictionary instead of a real SQLAlchemy database.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 3: create the service&lt;/strong&gt; (&lt;code&gt;services/post_service.py&lt;/code&gt;)&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;from&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;repositories&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;post_repository&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;post_repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4: create the router&lt;/strong&gt; (&lt;code&gt;api/posts.py&lt;/code&gt;)&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;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;APIRouter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;

&lt;span class="k"&gt;from&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;schemas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;PostCreate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;PostResponse&lt;/span&gt;
&lt;span class="k"&gt;from&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;services&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;post_service&lt;/span&gt;

&lt;span class="n"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;APIRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_db&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"Database session"&lt;/span&gt;


&lt;span class="nd"&gt;@router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response_model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;PostResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PostCreate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;post_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 5: connect the router&lt;/strong&gt; (&lt;code&gt;main.py&lt;/code&gt;)&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;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;

&lt;span class="k"&gt;from&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;api&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&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="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;posts&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="s2"&gt;"/posts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Posts"&lt;/span&gt;&lt;span class="p"&gt;]&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;p&gt;Start the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uvicorn app.main:app --reload
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Visit &lt;code&gt;http://127.0.0.1:8000/docs&lt;/code&gt;, find &lt;code&gt;POST /posts/&lt;/code&gt;, and send:&lt;br&gt;
&lt;/p&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="nl"&gt;"caption"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"My first post!"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected response:&lt;br&gt;
&lt;/p&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="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"caption"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"My first post!"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  9.1 🔍 What happened internally?
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;🌐 &lt;strong&gt;Frontend sends the request:&lt;/strong&gt; &lt;code&gt;POST /posts/&lt;/code&gt; with JSON data&lt;/li&gt;
&lt;li&gt;🛣️ &lt;strong&gt;FastAPI identifies the route:&lt;/strong&gt; it finds the matching endpoint in the posts router&lt;/li&gt;
&lt;li&gt;✔️ &lt;strong&gt;Pydantic validates request data:&lt;/strong&gt; checks that the caption is a string&lt;/li&gt;
&lt;li&gt;💉 &lt;strong&gt;Dependency Injection happens:&lt;/strong&gt; FastAPI sees &lt;code&gt;Depends(get_db)&lt;/code&gt; and resolves the database dependency&lt;/li&gt;
&lt;li&gt;🧠 &lt;strong&gt;Router calls the service:&lt;/strong&gt; &lt;code&gt;post_service.create_post(db, post)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;📚 &lt;strong&gt;Service calls the repository:&lt;/strong&gt; the repository performs the database operation&lt;/li&gt;
&lt;li&gt;📤 &lt;strong&gt;Response model validates output:&lt;/strong&gt; FastAPI uses &lt;code&gt;PostResponse&lt;/code&gt; to validate and serialize the response&lt;/li&gt;
&lt;li&gt;🔙 &lt;strong&gt;Response goes back to the frontend&lt;/strong&gt; as JSON&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is how everything you've learned works together in a real backend. 🎯&lt;/p&gt;




&lt;h2&gt;
  
  
  10. 📋 Quick Revision: Everything at a Glance
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;FastAPI&lt;/td&gt;
&lt;td&gt;Python framework for APIs&lt;/td&gt;
&lt;td&gt;&lt;code&gt;FastAPI()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Path parameter&lt;/td&gt;
&lt;td&gt;Identifies a resource&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/posts/10&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Query parameter&lt;/td&gt;
&lt;td&gt;Filters or controls retrieval&lt;/td&gt;
&lt;td&gt;&lt;code&gt;?limit=10&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Body parameter&lt;/td&gt;
&lt;td&gt;Sends structured data&lt;/td&gt;
&lt;td&gt;JSON request body&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pydantic&lt;/td&gt;
&lt;td&gt;Validates and parses data&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BaseModel&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Request schema&lt;/td&gt;
&lt;td&gt;Defines incoming data&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PostCreate&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Response schema&lt;/td&gt;
&lt;td&gt;Defines outgoing data&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PostResponse&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Depends()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Injects dependencies&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Depends(get_db)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IoC&lt;/td&gt;
&lt;td&gt;Transfers control to a framework&lt;/td&gt;
&lt;td&gt;FastAPI manages dependency resolution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;APIRouter&lt;/td&gt;
&lt;td&gt;Organizes endpoints&lt;/td&gt;
&lt;td&gt;&lt;code&gt;APIRouter()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Swagger UI&lt;/td&gt;
&lt;td&gt;Interactive API testing&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/docs&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ReDoc&lt;/td&gt;
&lt;td&gt;API documentation&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/redoc&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SRP&lt;/td&gt;
&lt;td&gt;One clear responsibility per component&lt;/td&gt;
&lt;td&gt;Separate router and service&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  11. 🎤 Interview Questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. What is FastAPI?&lt;/strong&gt;&lt;br&gt;
A modern Python web framework used to build APIs. It supports automatic request validation, dependency injection, asynchronous programming and automatic API documentation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. What's the difference between path and query parameters?&lt;/strong&gt;&lt;br&gt;
Path parameters identify a particular resource, whereas query parameters provide additional filtering, sorting or pagination information.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. What is Pydantic?&lt;/strong&gt;&lt;br&gt;
A Python data validation library. It validates incoming data based on the types and rules defined in our models.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Why do we need separate request and response schemas?&lt;/strong&gt;&lt;br&gt;
Request schemas validate incoming data, while response schemas control the structure of outgoing data and prevent unwanted fields, such as passwords, from being exposed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. What is Dependency Injection?&lt;/strong&gt;&lt;br&gt;
A technique where required objects or services are provided to a function from outside instead of the function creating them internally.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. What is &lt;code&gt;Depends()&lt;/code&gt; in FastAPI?&lt;/strong&gt;&lt;br&gt;
FastAPI's built-in mechanism for declaring dependencies. FastAPI resolves them and injects their results into endpoint functions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. What is Inversion of Control?&lt;/strong&gt;&lt;br&gt;
A design principle where control over certain operations is transferred from application code to a framework or external component. FastAPI implements this through dependency injection.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8. What is APIRouter?&lt;/strong&gt;&lt;br&gt;
It organizes related API endpoints into separate modules, making large applications modular and maintainable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;9. What's the difference between Swagger UI and ReDoc?&lt;/strong&gt;&lt;br&gt;
Swagger UI provides an interactive interface for exploring and testing APIs. ReDoc provides a documentation-focused interface.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;10. What is SRP?&lt;/strong&gt;&lt;br&gt;
The Single Responsibility Principle says a component should have one clear responsibility. In FastAPI, we apply it by separating route handling, business logic and database operations.&lt;/p&gt;


&lt;h2&gt;
  
  
  12. 🧪 Test Yourself!
&lt;/h2&gt;

&lt;p&gt;Try these before peeking at the answers! ❤️&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;You want to retrieve post number 50 using &lt;code&gt;/posts/50&lt;/code&gt;. What type of parameter is &lt;code&gt;50&lt;/code&gt;?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Query parameter&lt;/li&gt;
&lt;li&gt;B) Path parameter&lt;/li&gt;
&lt;li&gt;C) Body parameter&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Which library validates incoming request data using &lt;code&gt;BaseModel&lt;/code&gt;?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) SQLAlchemy&lt;/li&gt;
&lt;li&gt;B) Uvicorn&lt;/li&gt;
&lt;li&gt;C) Pydantic&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;What does &lt;code&gt;Depends(get_db)&lt;/code&gt; do?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Deletes the database&lt;/li&gt;
&lt;li&gt;B) Asks FastAPI to provide a database dependency&lt;/li&gt;
&lt;li&gt;C) Creates a new API endpoint&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Which component organizes related API endpoints?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) &lt;code&gt;APIRouter&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;B) &lt;code&gt;BaseModel&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;C) ReDoc&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Where can you interactively test your FastAPI endpoints by default?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) &lt;code&gt;/redoc&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;B) &lt;code&gt;/openapi.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;C) &lt;code&gt;/docs&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Which layer should contain business logic?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) Router&lt;/li&gt;
&lt;li&gt;B) Service&lt;/li&gt;
&lt;li&gt;C) Frontend&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;/p&gt;
  👀 Click to reveal answers
  &lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;B&lt;/strong&gt;: &lt;code&gt;50&lt;/code&gt; is part of the URL path, so it's a path parameter&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C&lt;/strong&gt;: Pydantic validates data using &lt;code&gt;BaseModel&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;B&lt;/strong&gt;: It asks FastAPI to resolve and inject the &lt;code&gt;get_db&lt;/code&gt; dependency&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A&lt;/strong&gt;: &lt;code&gt;APIRouter&lt;/code&gt; groups related endpoints&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C&lt;/strong&gt;: &lt;code&gt;/docs&lt;/code&gt; opens Swagger UI for interactive testing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;B&lt;/strong&gt;: The service layer holds business logic&lt;/li&gt;
&lt;/ol&gt;



&lt;p&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  13. 🧭 What to Remember in Your Own Backend
&lt;/h2&gt;

&lt;p&gt;When you open your own FastAPI project, try to identify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🛣️ &lt;code&gt;api/&lt;/code&gt;: where are the routes?&lt;/li&gt;
&lt;li&gt;✔️ &lt;code&gt;schemas/&lt;/code&gt;: what request and response structures are defined?&lt;/li&gt;
&lt;li&gt;🧠 &lt;code&gt;services/&lt;/code&gt;: where is the business logic?&lt;/li&gt;
&lt;li&gt;📚 &lt;code&gt;repositories/&lt;/code&gt;: where are database operations performed?&lt;/li&gt;
&lt;li&gt;💉 &lt;code&gt;Depends()&lt;/code&gt;: how are dependencies provided?&lt;/li&gt;
&lt;li&gt;🗂️ &lt;code&gt;APIRouter&lt;/code&gt;: how are endpoints grouped?&lt;/li&gt;
&lt;li&gt;🚪 &lt;code&gt;main.py&lt;/code&gt;: where are routers registered and the app created?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you can spot these in real code, you'll understand how a professional FastAPI backend is structured instead of just memorizing definitions.&lt;/p&gt;




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

&lt;p&gt;Pick &lt;strong&gt;one endpoint&lt;/strong&gt; and trace its complete flow: frontend request → router → dependency → service → repository → database → response. That's far more valuable than memorizing 50 definitions. 💡&lt;/p&gt;

&lt;p&gt;If this post helped you, drop a ❤️ or 🦄 and share it with a friend learning FastAPI. Happy coding! 👩‍💻✨&lt;/p&gt;

</description>
      <category>backend</category>
      <category>fastapi</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
