API Microservices & APIs
Beginner-friendly FastAPI + uvicorn 110 quiz questions

Microservices, APIs
& CI/CD

Learn how software talks to software β€” from REST fundamentals to hands-on FastAPI endpoints and automated deployments.

11

Sections

110

MCQs

CRUD

Examples

01

Foundation

Prerequisites

Before diving in, make sure you have a few basics in place. Think of these as the tools in your toolbox β€” you don't need to be an expert, but familiarity helps.

πŸ“š

Required knowledge

  • Basic computer literacy (files, folders, browsers)
  • Elementary programming concepts (variables, functions)
  • Comfort using a terminal / command line
πŸ› οΈ

Tools to install

  • Python 3.10+ β€” python.org
  • pip β€” comes with Python
  • A code editor β€” VS Code, Cursor, etc.
  • Git β€” for version control & CI/CD

Quick start check: Open a terminal and run python --version. If you see a version number, you're ready to proceed.

02

Core concept

What Is an API?

API stands for Application Programming Interface. In plain English: it's a menu that one program offers to another. Your app doesn't need to know how the kitchen cooks β€” it just orders from the menu and gets a result back.

When you use a weather app, it doesn't measure temperature itself. It calls a weather service's API: "Give me the forecast for London." The API returns structured data (usually JSON), and the app displays it.

Client ↔ Server API flow
Client App (Browser / Mobile) API Server (Backend) HTTP Request JSON Response
03

Web standards

REST & HTTP Methods

REST (Representational State Transfer) is a popular style for building web APIs. Resources (users, orders, products) are identified by URLs, and you use standard HTTP methods to act on them β€” like verbs in a sentence.

Method Purpose Analogy
GETRead dataLook at a menu
POSTCreate new dataPlace a new order
PUT / PATCHUpdate existing dataChange your order
DELETERemove dataCancel the order
REST resource lifecycle
POST Create GET Read PUT Update DELETE Remove /items

Status codes tell you what happened: 200 OK means success, 404 Not Found means the resource doesn't exist, and 500 Server Error means something broke on the server.

04

Architecture

Introduction to Microservices

Imagine a restaurant that used to be one giant kitchen where one chef did everything. A monolith app works the same way β€” one big codebase handles users, payments, inventory, and shipping.

Microservices split that kitchen into specialized stations: a pastry station, a grill, a salad bar. Each service is a small, independent app with its own database, deployed and scaled separately. They talk to each other through APIs.

Monolith vs. microservices
Monolith Users Orders Payments Inventory Single database Microservices User Svc Order Svc Pay Svc Stock Svc API Gateway

βš–οΈ Benefits & trade-offs

  • + Teams can deploy services independently
  • + Scale only the busy parts (e.g., payment service during sales)
  • βˆ’ More moving parts = more DevOps complexity
  • βˆ’ Network calls between services can fail β€” you need resilience patterns
05

DevOps

CI/CD & Automation

CI/CD stands for Continuous Integration and Continuous Delivery/Deployment. Instead of manually copying code to a server every Friday, automation pipelines test and deploy your API whenever you push to Git.

  • CI β€” automatically run tests & lint on every commit
  • CD β€” automatically deploy passing builds to staging or production
Typical CI/CD pipeline
Git Push Build Test & Lint Staging Production Monitor

Popular tools: GitHub Actions, GitLab CI, Jenkins, CircleCI. For APIs, pipelines often run unit tests with pytest, build Docker images, and deploy to cloud platforms.

06

Hands-on

FastAPI & uvicorn Setup

FastAPI is a modern Python framework for building APIs. It's fast, automatically generates interactive docs, and uses Python type hints for validation. uvicorn is the ASGI server that actually runs your FastAPI app.

Request path through FastAPI
HTTP Client uvicorn FastAPI Your Code JSON Response

Installation

terminal
pip install fastapi uvicorn[standard]

Minimal app (main.py)

main.py
from fastapi import FastAPI

app = FastAPI(title="My API")

@app.get("/")
def read_root():
    return {"message": "Hello, API!"}

Run with uvicorn

terminal
uvicorn main:app --reload --host 0.0.0.0 --port 8000

Visit http://127.0.0.1:8000/docs for auto-generated Swagger UI.

GET

Section 07

GET Endpoints β€” Reading Data

GET requests retrieve data without changing anything. Use path parameters for specific resources and query parameters for filtering.

main.py
GET
from fastapi import FastAPI
from typing import List

app = FastAPI()

# In-memory "database" for learning
items_db = [
    {"id": 1, "name": "Laptop", "price": 999.99},
    {"id": 2, "name": "Mouse", "price": 29.99},
]

@app.get("/items", response_model=List[dict])
def list_items(skip: int = 0, limit: int = 10):
    """GET all items with pagination."""
    return items_db[skip : skip + limit]

@app.get("/items/{item_id}")
def get_item(item_id: int):
    """GET a single item by ID."""
    for item in items_db:
        if item["id"] == item_id:
            return item
    from fastapi import HTTPException
    raise HTTPException(status_code=404, detail="Item not found")

πŸš€ Try it

curl -i http://localhost:8000/items curl -i http://localhost:8000/items/1

What the client receives

The client (browser, mobile app, or curl) gets an HTTP status code plus a JSON body. Use curl -i to see headers and body together.

200 OK GET /items Success β€” list all items
[
  { "id": 1, "name": "Laptop", "price": 999.99 },
  { "id": 2, "name": "Mouse", "price": 29.99 }
]
200 OK GET /items/1 Success β€” single item
{
  "id": 1,
  "name": "Laptop",
  "price": 999.99
}
404 Not Found GET /items/999 Item does not exist
{
  "detail": "Item not found"
}

Raised by HTTPException(status_code=404, ...) in your route.

422 Unprocessable Entity GET /items/abc Bad request β€” invalid path param
{
  "detail": [
    {
      "type": "int_parsing",
      "loc": ["path", "item_id"],
      "msg": "Input should be a valid integer...",
      "input": "abc"
    }
  ]
}

FastAPI auto-validates item_id: int β€” non-numeric IDs are rejected before your function runs.

Client tip: In JavaScript, const res = await fetch('/items/1') then const data = await res.json() gives you the object above. Check res.ok or res.status before assuming success.

POST

Section 08

POST Endpoints β€” Creating Data

POST sends a JSON body to create a new resource. FastAPI uses Pydantic models to validate incoming data automatically.

main.py
POST
from fastapi import FastAPI, status
from pydantic import BaseModel

app = FastAPI()
items_db = []
next_id = 1

class ItemCreate(BaseModel):
    name: str
    price: float

@app.post("/items", status_code=status.HTTP_201_CREATED)
def create_item(item: ItemCreate):
    global next_id
    new_item = {"id": next_id, **item.model_dump()}
    items_db.append(new_item)
    next_id += 1
    return new_item

πŸš€ Try it

curl -i -X POST http://localhost:8000/items \ -H "Content-Type: application/json" \ -d '{"name": "Keyboard", "price": 79.99}'

What the client receives

On success, the client gets 201 Created and the new record (including the server-assigned id). On bad input, FastAPI returns 422 with a structured list of validation errors.

201 Created POST /items Success β€” item created
{
  "id": 1,
  "name": "Keyboard",
  "price": 79.99
}

The client can read id from the response and immediately call GET /items/1.

422 Unprocessable Entity POST /items Missing required field

Request body: {"name": "Keyboard"} β€” no price

{
  "detail": [
    {
      "type": "missing",
      "loc": ["body", "price"],
      "msg": "Field required",
      "input": { "name": "Keyboard" }
    }
  ]
}
422 Unprocessable Entity POST /items Wrong data type

Request body: {"name": "Keyboard", "price": "free"}

{
  "detail": [
    {
      "type": "float_parsing",
      "loc": ["body", "price"],
      "msg": "Input should be a valid number...",
      "input": "free"
    }
  ]
}

Pydantic enforces price: float β€” strings that are not valid numbers are rejected.

PUT
DEL

Section 09

PUT/PATCH & DELETE β€” Edit & Remove

PUT replaces an entire resource; PATCH updates only the fields you send. DELETE removes a resource.

main.py
PATCH DELETE
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import Optional

app = FastAPI()
items_db = [{"id": 1, "name": "Laptop", "price": 999.99}]

class ItemUpdate(BaseModel):
    name: Optional[str] = None
    price: Optional[float] = None

def find_item(item_id: int):
    for i, item in enumerate(items_db):
        if item["id"] == item_id:
            return i, item
    raise HTTPException(status_code=404, detail="Item not found")

@app.put("/items/{item_id}")
def replace_item(item_id: int, data: ItemUpdate):
    """Full replace β€” all fields required in practice."""
    idx, _ = find_item(item_id)
    updated = {"id": item_id, **data.model_dump(exclude_unset=True)}
    items_db[idx] = updated
    return updated

@app.patch("/items/{item_id}")
def patch_item(item_id: int, data: ItemUpdate):
    """Partial update β€” only sent fields change."""
    idx, item = find_item(item_id)
    patch_data = data.model_dump(exclude_unset=True)
    items_db[idx] = {**item, **patch_data}
    return items_db[idx]

@app.delete("/items/{item_id}", status_code=204)
def delete_item(item_id: int):
    idx, _ = find_item(item_id)
    items_db.pop(idx)
    return None

πŸš€ Try it

curl -i -X PATCH http://localhost:8000/items/1 -H "Content-Type: application/json" -d '{"price": 899.99}' curl -i -X DELETE http://localhost:8000/items/1

What the client receives

Updates return the full updated object (200 OK). Deletes often return 204 No Content with an empty body. Missing resources and invalid input produce clear error JSON β€” same patterns as GET and POST.

200 OK PATCH /items/1 Success β€” partial update

Request body: {"price": 899.99}

{
  "id": 1,
  "name": "Laptop",
  "price": 899.99
}

name unchanged; only price was patched.

204 No Content DELETE /items/1 Success β€” item removed
(empty response body)

In JavaScript: res.status === 204 means success with nothing to parse via .json().

404 Not Found PATCH /items/42 Item does not exist
{
  "detail": "Item not found"
}

Same JSON shape for DELETE /items/42 when the ID is missing.

422 Unprocessable Entity PATCH /items/1 Invalid body type

Request body: {"price": "cheap"}

{
  "detail": [
    {
      "type": "float_parsing",
      "loc": ["body", "price"],
      "msg": "Input should be a valid number...",
      "input": "cheap"
    }
  ]
}
10

Ship it

Production, Testing & CI/CD

A complete microservice isn't just endpoints β€” it needs tests, containers, and automated deployment.

Example pytest test

test_main.py
from fastapi.testclient import TestClient
from main import app

client = TestClient(app)

def test_read_items():
    response = client.get("/items")
    assert response.status_code == 200
    assert isinstance(response.json(), list)

GitHub Actions snippet

.github/workflows/ci.yml
# .github/workflows/ci.yml
name: API CI
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - run: pip install fastapi uvicorn pytest httpx
      - run: pytest

✨ Key takeaways

  • β†’ APIs are contracts β€” document them (FastAPI does this automatically)
  • β†’ Microservices communicate over HTTP/gRPC β€” design for failure
  • β†’ Automate testing and deployment early β€” CI/CD saves hours later
  • β†’ Start simple (monolith or few services), split when you have a clear reason
11

Toolkit

Understanding curl Commands

curl (pronounced β€œcurl” or β€œsee-url”) is a command-line tool that talks to URLs β€” perfect for testing APIs without building a whole app first. Think of it as speed-dialling a web address: you tell curl where to call, what kind of request to send, and optionally what data to attach.

Every example in the GET, POST, PATCH, and DELETE sections used curl. Here we unpack how those commands are built, flag by flag, in plain language.

Parts of a curl command β€” two valid layouts
Both send the same POST request β€” only the argument order on the command line differs Style A β€” flags first, URL last curl -i -X POST -H -d http://localhost:8000/items Style B β€” URL in the middle (also valid) curl -i -X POST http://localhost:8000/items -H -d Always start with curl Β· include exactly one URL Β· add whichever flags you need Order of flags and URL placement is flexible β€” curl figures out what you mean

↔️ Does the order matter?

Usually no. curl treats flags (-X, -H, -d, -i, …) and the URL as separate pieces of the same instruction. You can put the URL after all flags (Style A) or before trailing flags like -H and -d (Style B). The request sent over the network is identical.

Same POST β€” written two ways

Style A (URL last)

curl -i -X POST -H "Content-Type: application/json" -d '{"name":"Keyboard","price":79.99}' http://localhost:8000/items

Style B (URL before -H / -d)

curl -i -X POST http://localhost:8000/items -H "Content-Type: application/json" -d '{"name":"Keyboard","price":79.99}'

Why both appear in this tutorial: multi-line examples often put the URL right after -X POST so the next lines clearly attach headers and body to that address. Single-line examples sometimes group all flags first. Pick whichever reads clearer to you.

Essential flags & options

Flags start with a hyphen (-) or double hyphen (--). They modify how curl behaves.

Flag Plain-English meaning
-X METHODPick the HTTP verb (GET, POST, PATCH, DELETE…)
-H "Name: value"Add a header β€” metadata about the request
-d '...'Attach a body (usually JSON) to the request
-iInclude response headers in the output
-vVerbose β€” show full request/response conversation
-sSilent β€” hide progress meter
-u user:passBasic authentication credentials
--failTreat HTTP errors (4xx/5xx) as command failure

Worked examples (with explanations)

Example 1 β€” Simple GET (read data)

curl http://localhost:8000/items
  • curl β€” runs the tool.
  • http://localhost:8000/items β€” the address. localhost means β€œthis computer”; port 8000 is where uvicorn listens; /items is the API path.
  • No -X needed β€” curl assumes GET when you only pass a URL (like opening a page in a browser).
  • You’ll see JSON printed in the terminal β€” that’s the response body from FastAPI.

Example 2 β€” GET with response headers (-i)

curl -i http://localhost:8000/items/1
  • -i (include) β€” shows the β€œenvelope” (status line + headers) and the message inside (JSON body).
  • Look for the first line: HTTP/1.1 200 OK β€” that’s how the client knows the request succeeded before reading JSON.
  • Use this whenever you need to distinguish 200 vs 404 vs 422 at a glance.

Example 3 β€” POST (create data)

curl -i -X POST http://localhost:8000/items \
  -H "Content-Type: application/json" \
  -d '{"name": "Keyboard", "price": 79.99}'
  • -X POST β€” explicitly says β€œcreate something” (don’t just read).
  • http://localhost:8000/items β€” the URL appears before -H and -d here (Style B). That is valid β€” you could also move the URL to the very end (Style A) and get the same result.
  • -H "Content-Type: application/json" β€” tells the server β€œthe body is JSON text.” Without this, FastAPI may not parse the body correctly.
  • -d '{...}' β€” the data payload (d = data). The single quotes wrap JSON so the shell doesn’t break on inner double quotes.
  • The backslash \ at line ends continues the command on the next line β€” purely for readability; it does not change flag order rules.
  • Expect 201 Created and JSON with the new id.

Example 4 β€” PATCH (partial update)

curl -i -X PATCH http://localhost:8000/items/1 \ -H "Content-Type: application/json" \ -d '{"price": 899.99}'
  • -X PATCH β€” β€œchange only what I send.” Here we update price only; name stays the same.
  • /items/1 β€” the 1 in the URL identifies which record to patch (path parameter).
  • Again, -H and -d come after the URL in this layout β€” equivalent to putting them before the URL.

Example 5 β€” DELETE (remove data)

curl -i -X DELETE http://localhost:8000/items/1
  • -X DELETE β€” asks the server to remove the resource at that URL.
  • No -d body needed β€” you’re not sending new data, just saying β€œdelete this ID.”
  • Often returns 204 No Content β€” success with an empty body. That’s normal; check the status line, not the body.

Example 6 β€” Query parameters (filters in the URL)

curl "http://localhost:8000/items?skip=0&limit=5"
  • Everything after ? is the query string β€” optional filters as key=value pairs separated by &.
  • Quotes wrap the URL so the shell doesn’t treat & as β€œrun in background.”
  • Maps to FastAPI parameters: skip: int = 0, limit: int = 10 in your route function.

πŸ’‘ Quick cheat sheet

Read β†’ curl URL
Create β†’ curl -X POST -H ... -d ... URL
Update β†’ curl -X PATCH -H ... -d ... URL/id
Delete β†’ curl -X DELETE URL/id

Tip: add -i to any command when learning. For POST/PATCH, the URL can sit before or after -H / -d.