Lesson 70 · Web Development

Python FastAPI

Learn how to build modern, fast, type-safe APIs with FastAPI, including request validation, Pydantic models, automatic documentation, dependency injection, and asynchronous endpoints.

50–60 minutes Intermediate APIs & Web Development
0%
01

Learning Objectives

FastAPI builds on several concepts you have already learned: functions, type hints, dictionaries, JSON, HTTP, REST APIs, and asynchronous programming.

By the end of this lesson, you will be able to:

  • Understand what FastAPI is.
  • Explain why FastAPI is popular for API development.
  • Install and run a FastAPI application.
  • Create API endpoints.
  • Work with GET, POST, PUT, PATCH, and DELETE.
  • Use path and query parameters.
  • Accept JSON request bodies.
  • Use Pydantic models for validation.
  • Define response models.
  • Return appropriate HTTP status codes.
  • Handle API errors.
  • Understand dependency injection.
  • Use FastAPI's automatic documentation.
  • Understand basic asynchronous endpoints.
02

What Is FastAPI?

FastAPI is a modern Python web framework designed primarily for building APIs.

It combines Python type hints with automatic request validation and API documentation.

The key idea

You describe your API using normal Python functions, type hints, and models. FastAPI uses that information to validate data and generate documentation automatically.

FastAPI is especially useful for backend services that communicate with websites, mobile applications, data applications, and other services.

03

Why FastAPI?

FastAPI has become popular because it provides a productive development experience while supporting modern Python features.

Important advantages

  • Excellent support for Python type hints.
  • Automatic validation of request data.
  • Automatic interactive API documentation.
  • Support for asynchronous endpoints.
  • Clear API design.
  • Excellent developer experience.
  • Suitable for microservices and backend APIs.

FastAPI also works closely with the Pydantic library for data validation and serialization.

04

Installing FastAPI

Install FastAPI together with an ASGI server such as Uvicorn:

python -m pip install fastapi uvicorn

You can verify the installation with:

python -m pip show fastapi
python -m pip show uvicorn

Use a virtual environment

As with Flask and other Python projects, install FastAPI inside a project-specific virtual environment whenever possible.

05

Your First FastAPI Application

Create a file named main.py.

from fastapi import FastAPI

app = FastAPI()


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

The application object is created with:

app = FastAPI()

The route:

@app.get("/")

tells FastAPI that the function should handle GET requests to the root URL.

Notice the difference

Flask commonly uses @app.route(), while FastAPI provides method-specific decorators such as @app.get(), @app.post(), and @app.delete().

06

Running the Server

Start the FastAPI application with Uvicorn:

uvicorn main:app --reload

The command means:

  • main — the Python file main.py.
  • app — the FastAPI application object.
  • --reload — automatically reload during development when source files change.

You can normally access the application at:

http://127.0.0.1:8000

Alternative command

python -m uvicorn main:app --reload
07

Routes & Path Operations

FastAPI calls its endpoint declarations path operations.

@app.get("/products")
def get_products():
    return {
        "message": "Product list"
    }


@app.post("/products")
def create_product():
    return {
        "message": "Product created"
    }

Each decorator connects a URL and HTTP method to a function.

  • @app.get()
  • @app.post()
  • @app.put()
  • @app.patch()
  • @app.delete()
08

HTTP Methods

REST APIs commonly use different HTTP methods to represent different operations.

  • GET — retrieve data.
  • POST — create data.
  • PUT — replace a resource.
  • PATCH — partially update a resource.
  • DELETE — remove a resource.

Example

@app.get("/products")
def get_products():
    return {"action": "get"}


@app.post("/products")
def create_product():
    return {"action": "create"}


@app.put("/products/1")
def replace_product():
    return {"action": "replace"}


@app.patch("/products/1")
def update_product():
    return {"action": "update"}


@app.delete("/products/1")
def delete_product():
    return {"action": "delete"}
09

Path Parameters

Path parameters allow a value from the URL to be passed directly into a Python function.

@app.get("/products/{product_id}")
def get_product(product_id: int):

    return {
        "product_id": product_id
    }

A request such as:

/products/25

passes 25 to the function.

Type hints do useful work

Because product_id is declared as an integer, FastAPI can validate and convert compatible input automatically.

10

Query Parameters

Query parameters appear after ? in a URL.

/products?category=phones&limit=10

FastAPI can recognize query parameters directly from function parameters.

@app.get("/products")
def get_products(
    category: str | None = None,
    limit: int = 10
):

    return {
        "category": category,
        "limit": limit
    }

The URL:

/products?category=phones&limit=5

produces values similar to:

{
    "category": "phones",
    "limit": 5
}
11

Request Body

POST and PUT requests commonly send structured JSON data in the request body.

For example:

{
    "name": "Laptop",
    "price": 1200,
    "quantity": 5
}

FastAPI can automatically parse and validate this data using a model.

This is one of the areas where FastAPI's integration with Pydantic becomes extremely useful.

12

Pydantic Models

A Pydantic model describes the structure of data that your API expects.

from pydantic import BaseModel


class Product(BaseModel):

    name: str
    price: float
    quantity: int

You can then use the model as a function parameter.

@app.post("/products")
def create_product(product: Product):

    return product

FastAPI understands that the endpoint expects a JSON object matching the Product model.

Connection to previous lessons

This is where your earlier knowledge of type hints becomes directly useful in professional API development.

13

Automatic Validation

FastAPI and Pydantic can validate incoming data according to the types and rules defined by your models.

from pydantic import BaseModel, Field


class Product(BaseModel):

    name: str

    price: float = Field(
        gt=0
    )

    quantity: int = Field(
        ge=0
    )

Here:

  • price must be greater than zero.
  • quantity must be zero or greater.

Why validation matters

Validation prevents invalid data from entering your application's business logic and makes API behavior more predictable.

14

Response Models

You can also define the structure of the data your API should return.

class ProductResponse(BaseModel):

    id: int
    name: str
    price: float


@app.get(
    "/products/{product_id}",
    response_model=ProductResponse
)
def get_product(product_id: int):

    return {
        "id": product_id,
        "name": "Laptop",
        "price": 1200
    }

Response models help keep your API output consistent and documented.

Useful concept

A response model can also help prevent unintended fields from being exposed in an API response.

15

HTTP Status Codes

APIs should communicate the result of an operation using appropriate HTTP status codes.

from fastapi import FastAPI, status


@app.post(
    "/products",
    status_code=status.HTTP_201_CREATED
)
def create_product():

    return {
        "message": "Product created"
    }
  • 200 — OK
  • 201 — Created
  • 204 — No Content
  • 400 — Bad Request
  • 401 — Unauthorized
  • 403 — Forbidden
  • 404 — Not Found
  • 422 — Validation-related request error
  • 500 — Internal Server Error
16

JSON Responses

Returning a Python dictionary from a FastAPI endpoint normally produces a JSON response.

@app.get("/api/products")
def products():

    return {
        "products": [
            {
                "id": 1,
                "name": "Laptop",
                "price": 1200
            },
            {
                "id": 2,
                "name": "Phone",
                "price": 800
            }
        ]
    }

The client receives structured JSON that can easily be consumed by JavaScript, mobile applications, or other backend services.

17

Headers

HTTP headers carry additional information about requests and responses.

from fastapi import FastAPI, Header

app = FastAPI()


@app.get("/profile")
def profile(
    user_agent: str | None = Header(default=None)
):

    return {
        "user_agent": user_agent
    }

FastAPI can map HTTP headers into Python function parameters.

Common header uses

  • Authentication information
  • Content type
  • Client information
  • Request tracing
  • Caching instructions
18

Error Handling

FastAPI provides HTTPException for returning HTTP errors from your application.

from fastapi import FastAPI, HTTPException

app = FastAPI()


@app.get("/products/{product_id}")
def get_product(product_id: int):

    if product_id != 1:

        raise HTTPException(
            status_code=404,
            detail="Product not found"
        )

    return {
        "id": 1,
        "name": "Laptop"
    }

The client receives an appropriate HTTP error response instead of an unexplained application failure.

Good API behavior

Error responses should be meaningful without exposing sensitive internal implementation details.

19

Dependency Injection

FastAPI includes a dependency injection system that allows reusable logic to be shared across endpoints.

from fastapi import Depends, FastAPI

app = FastAPI()


def get_database():
    return "Database Connection"


@app.get("/products")
def products(
    database=Depends(get_database)
):

    return {
        "database": database
    }

Dependencies can be used for things such as authentication, database connections, reusable parameters, and shared application logic.

Why dependencies are useful

Instead of copying the same logic into every endpoint, you can define it once and inject it wherever required.

20

Automatic API Documentation

One of FastAPI's most useful features is automatic interactive API documentation.

After starting your application, FastAPI normally provides interactive documentation at:

/docs

For example:

http://127.0.0.1:8000/docs

FastAPI also provides an alternative documentation interface at:

/redoc

Why this matters

Developers can inspect available endpoints, parameters, request models, response models, and test API requests directly through the generated documentation.

21

Async Endpoints

FastAPI supports Python's asynchronous programming model.

from fastapi import FastAPI

app = FastAPI()


@app.get("/products")
async def products():

    return {
        "message": "Async endpoint"
    }

An asynchronous function uses async def.

When an operation supports asynchronous waiting, you can use await.

import asyncio


@app.get("/wait")
async def wait_example():

    await asyncio.sleep(1)

    return {
        "message": "Finished waiting"
    }

Important

Adding async does not automatically make every operation faster. It is most useful when your application spends time waiting for asynchronous I/O, such as network or database operations.

22

Professional FastAPI Project Structure

A larger API should be organized into modules instead of putting every endpoint inside one file.

my_api/
│
├── app/
│   ├── main.py
│   │
│   ├── routers/
│   │   ├── products.py
│   │   └── users.py
│   │
│   ├── models/
│   │   └── product.py
│   │
│   ├── services/
│   │   └── product_service.py
│   │
│   └── database.py
│
├── tests/
│
├── requirements.txt
│
└── .env

This structure separates routing, models, business logic, database access, and testing.

Grow your architecture gradually

A small learning project does not need this entire structure. Introduce additional layers when the application becomes large enough to benefit from them.

23

Practical Example — Ecommerce Product API

Let's combine routes, path parameters, Pydantic models, validation, and status codes into a small ecommerce API.

main.py

from fastapi import (
    FastAPI,
    HTTPException,
    status
)

from pydantic import BaseModel, Field


app = FastAPI()


class Product(BaseModel):

    name: str

    price: float = Field(
        gt=0
    )

    quantity: int = Field(
        ge=0
    )


products = []


@app.get("/products")
def get_products():

    return products


@app.post(
    "/products",
    status_code=status.HTTP_201_CREATED
)
def create_product(product: Product):

    products.append(product)

    return product


@app.get("/products/{product_id}")
def get_product(product_id: int):

    if product_id >= len(products):

        raise HTTPException(
            status_code=404,
            detail="Product not found"
        )

    return products[product_id]

Example JSON Request

{
    "name": "Laptop",
    "price": 1200,
    "quantity": 10
}

FastAPI validates the incoming structure before passing the data into your endpoint.

Real-world connection

A production ecommerce API would normally add a real database, authentication, authorization, pagination, logging, testing, configuration management, and additional business rules.

24

Common FastAPI Mistakes

Mistake 1 — Forgetting to start Uvicorn

Defining an API does not automatically start a web server. You need to run the ASGI server during development.

Mistake 2 — Incorrect module path

If your file is main.py and your application object is named app, the command is uvicorn main:app --reload.

Mistake 3 — Ignoring validation

Do not assume that client input is correct. Use appropriate models and validation rules.

Mistake 4 — Returning inappropriate status codes

An API should clearly communicate whether an operation succeeded, failed, created a resource, or could not find a resource.

Mistake 5 — Putting everything in one file

Small examples are fine in one file, but larger applications should separate routes, models, services, and database logic.

Mistake 6 — Misunderstanding async

Do not use asynchronous syntax simply because it looks modern. Understand whether the operation benefits from asynchronous I/O.

25

Practice Exercises

Exercise 1 — First API

Create a FastAPI application with a root endpoint that returns a JSON welcome message.

Exercise 2 — Product Route

Create a GET endpoint at /products.

Exercise 3 — Path Parameter

Create /products/{product_id} using an integer path parameter.

Exercise 4 — Query Parameters

Add category and limit query parameters to your product endpoint.

Exercise 5 — Pydantic Model

Create a Product model containing name, price, and quantity.

Exercise 6 — Validation

Make sure price must be greater than zero and quantity cannot be negative.

Exercise 7 — API Error

Return a 404 error when a requested product does not exist.

Exercise 8 — Documentation

Run your application and explore the automatically generated /docs interface.

26

Mini Challenge — Ecommerce Product API

Build a small but professional REST API for an ecommerce product catalog.

Your Tasks

  1. Create a FastAPI application.
  2. Create a Product Pydantic model.
  3. Include id, name, category, price, and quantity.
  4. Validate that price is greater than zero.
  5. Validate that quantity is not negative.
  6. Create GET /products.
  7. Create GET /products/{id}.
  8. Create POST /products.
  9. Create PUT or PATCH for updating products.
  10. Create DELETE for removing products.
  11. Return a 404 error when a product does not exist.
  12. Use appropriate HTTP status codes.
  13. Add query parameters for category filtering.
  14. Add a response model.
  15. Test every endpoint through /docs.

Suggested Product Model

from pydantic import BaseModel, Field


class Product(BaseModel):

    id: int

    name: str

    category: str

    price: float = Field(
        gt=0
    )

    quantity: int = Field(
        ge=0
    )

Challenge goal

Do not think of this as just a coding exercise. Think of yourself as an API developer designing a backend that another application could actually consume.

27

Knowledge Check

Test your understanding of FastAPI.

1. What is FastAPI mainly used for?

2. Which decorator creates a GET endpoint?

3. What is Pydantic commonly used for in FastAPI?

4. What is a path parameter?

5. Which status code normally indicates a newly created resource?

6. Which command starts a development FastAPI server?

7. Where can you normally find FastAPI's interactive documentation?

8. What does Depends() help implement?

9. What keyword is used to define an asynchronous function?

10. Why are type hints particularly useful in FastAPI?

28

Lesson Summary

FastAPI provides a modern approach to building Python APIs. It combines Python type hints, validation, automatic documentation, and asynchronous capabilities.

Key concepts to remember

  • FastAPI is a modern Python framework for APIs.
  • FastAPI() creates the application.
  • @app.get(), @app.post(), and other decorators define endpoints.
  • Path parameters are embedded in URLs.
  • Query parameters provide optional URL input.
  • Pydantic models define and validate structured data.
  • Response models define expected API output.
  • HTTP status codes communicate operation results.
  • HTTPException handles API errors.
  • Depends() supports dependency injection.
  • FastAPI automatically generates API documentation.
  • async def can be used for asynchronous endpoints.
  • Uvicorn is commonly used to run FastAPI applications.

What's next?

You have now learned Flask and FastAPI. Next, we move into Async Python and examine asynchronous programming more deeply so you understand what actually happens when Python handles concurrent I/O operations.

Ready for the next lesson?

Mark this lesson complete when you can create a basic FastAPI application, define API endpoints, validate request data, and test your API through the interactive documentation.