Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 58

Designing API Specifications with FastAPI

Recommended resources

Hello! Welcome to the final and most exciting module of your FastAPI course: The Capstone Project.

Over the past ten lessons, you've mastered everything from the fundamentals of asynchronous Python to building, testing, and deploying a production-grade API. Now, it's time to put all that knowledge into practice by building a complete application from the ground up.

This first lesson is all about the design phase. Before we write a single line of implementation code, we will act as architects. A well-thought-out design is the foundation of a scalable and maintainable application. Today, we will design the complete API specification for our project, which directly addresses our first learning outcome for this module.

Defining the Capstone Project: A Micro-Blog API

To make this project concrete and engaging, we will build a Micro-Blog API. This application will allow users to register, post short messages, and comment on others' posts. This project is a great way to synthesize all the concepts we've covered, as it requires:

  • User authentication and authorization.
  • CRUD operations for multiple resources (posts, comments).
  • Database relationships (one-to-many).
  • A clean, modern project structure.

Your experience with Django projects like "Found Forge" will be a great asset here, as you'll see many conceptual parallels in how we break down the problem.


The Three Pillars of API Specification

A complete API specification can be broken down into three core components. Think of this as the architectural blueprint for our application.

  1. Database Models (SQLAlchemy): The blueprint for our data's structure in the database. This defines the tables, columns, and relationships.
  2. Data Schemas (Pydantic): The contract for data sent to and from the API. This defines the shape of our JSON payloads, handles validation, and ensures data consistency.
  3. API Endpoints (Routing): The map of our API. This defines the available URLs, the HTTP methods they respond to, and the actions they perform.
FastAPI Dependency Injection Architecture Overview
This diagram shows a high-level layered architecture. Our design process today will define the components that fit into each of these layers: the API (endpoints), the data layer (SQLAlchemy models), and the data transfer objects (Pydantic schemas) that flow between them.

We will design these three pillars step-by-step.


Pillar 1: Designing the Database Models (SQLAlchemy)

We'll start with the database, as it's the heart of our application's data. We need to identify the core entities and how they relate to each other. For our Micro-Blog, we can identify three main entities: Users, Posts, and Comments.

Here’s how we can define them using SQLAlchemy models:

  • User Model:

    • id: Primary Key, Integer
    • username: String, Unique
    • email: String, Unique
    • hashed_password: String
    • created_at: DateTime
    • Relationship: A user can have many posts and many comments (one-to-many).
  • Post Model:

    • id: Primary Key, Integer
    • content: Text
    • created_at: DateTime
    • owner_id: Foreign Key to users.id
    • Relationships: A post belongs to one user and can have many comments (one-to-many).
  • Comment Model:

    • id: Primary Key, Integer
    • content: Text
    • created_at: DateTime
    • owner_id: Foreign Key to users.id
    • post_id: Foreign Key to posts.id
    • Relationships: A comment belongs to one user and one post.

This is very similar to defining models in Django's models.py. The concept of a ForeignKey is identical, and SQLAlchemy's relationship() function serves a similar purpose to Django's related_name, allowing you to easily navigate connections like user.posts.

To see how these models are coded, the following video provides an excellent walkthrough.

Python FastAPI Tutorial (Part 5): Adding a Database - SQLAlchemy Models and Relationships

This video from Corey Schafer provides a practical demonstration of how to define SQLAlchemy models and establish relationships between them. It's a great refresher on the syntax we'll be using.

Watch the segment from 08:32 to 16:23. Focus on how the User and Post models are defined with mapped_column, and more importantly, how ForeignKey and relationship are used to link them together.


Pillar 2: Designing the Data Schemas (Pydantic)

Now that we know how our data is stored, we need to define how it will be represented when communicating with clients via the API. This is where Pydantic schemas come in.

A key principle in modern FastAPI development is the separation of database models and API schemas.

  • SQLAlchemy Models are for the database's internal structure. They might contain sensitive fields like hashed_password.
  • Pydantic Schemas are for the external API contract. They define what the client can send and what it will receive. This allows us to expose only the data we want, in the format we want.

Python FastAPI Tutorial (Part 5): Adding a Database - SQLAlchemy Models and Relationships

To understand why this separation is so important, let's refer back to the introductory part of the same video. It clearly articulates the distinct roles of the three layers in a FastAPI application.

Watch the first 4 minutes and 27 seconds. Pay close attention to the explanation of the three-layer architecture (Database Models, Pydantic Schemas, API Routes) and the justification for keeping models and schemas separate.

For our project, we'll follow the best practice of creating different schemas for different operations (e.g., creating a resource vs. reading it). This is analogous to how you might have a WriteSerializer and a ReadSerializer in Django REST Framework.

Here is our Pydantic schema design:

User Schemas

  • UserCreate: For user registration.
    • username: str
    • email: str
    • password: str
  • UserPublic: For publicly displaying user information.
    • id: int
    • username: str

Post Schemas

  • PostCreate: For creating a new post. The owner will be inferred from the authentication token.
    • content: str
  • PostResponse: For sending post data back to the client. This will include the owner's public information.
    • id: int
    • content: str
    • created_at: datetime
    • owner: UserPublic (a nested schema)

Comment Schemas

  • CommentCreate: For creating a new comment.
    • content: str
  • CommentResponse: For sending comment data back to the client.
    • id: int
    • content: str
    • created_at: datetime
    • post_id: int
    • owner: UserPublic (a nested schema)
Schema-Driven FastAPI with Pydantic
This image illustrates the central role of Pydantic. The schema you define (left) dictates the validation logic and the structure of the data that your API endpoints (right) will accept and return.

The following reading provides a comprehensive overview of defining Pydantic models, including the use of nested models, which is exactly what we're doing with the owner field.

Ultimate guide to FastAPI library in Python - Deepnote

This guide from Deepnote provides a detailed look at using Pydantic for data validation and serialization. It will solidify your understanding of how we'll structure our schemas.

Read section '2. Data models and validation (Pydantic models)'. Focus on the syntax for creating a BaseModel, the examples of nested models, and the concept of a response_model.


Pillar 3: Designing the API Endpoints (Routing)

Finally, we define the API's public interface—the endpoints. We'll group them by resource for clarity. This is analogous to organizing URLs in Django's urls.py files, where you might include() URLs from different apps. In FastAPI, we'll later use APIRouter for this.

Authentication Endpoints (/auth)

  • POST /token: User login. Takes form data (username, password) and returns a JWT access token.
  • POST /register: User registration. Takes UserCreate schema, returns UserPublic.

User Endpoints (/users)

  • GET /me: Get current authenticated user's details. Returns a more detailed user schema (to be defined later).
  • GET /{user_id}: Get public profile of any user. Returns UserPublic.

Post Endpoints (/posts)

  • POST /: Create a new post. (Requires authentication). Takes PostCreate, returns PostResponse.
  • GET /: Get a list of all posts (with pagination). Returns List[PostResponse].
  • GET /{post_id}: Get a single post. Returns PostResponse.
  • DELETE /{post_id}: Delete a post. (Requires authentication and ownership). Returns a success message.

Comment Endpoints (nested under posts)

  • POST /posts/{post_id}/comments: Create a comment on a specific post. (Requires authentication). Takes CommentCreate, returns CommentResponse.
  • GET /posts/{post_id}/comments: Get all comments for a specific post. Returns List[CommentResponse].
  • DELETE /comments/{comment_id}: Delete a comment. (Requires authentication and ownership). Returns a success message.

This structure defines a clear and logical RESTful API. The resource viktorsapozhok/fastapi-services-oauth2 provides a great template for how these routers, schemas, and models will be organized in our project files.

Structuring FastAPI application with multiple services using 3-tier ...

This article presents a clean, 3-tier structure for a FastAPI application. It's a great reference for how we will organize our designed components into actual files.

Review the 'Structure overview' section to see the proposed folder layout (models, schemas, routers). Then, skim the 'Models', 'Schemas', and 'Routers' sections for the movies service to see a concrete example of how a single feature is broken down across these directories.


Conclusion

Congratulations! You have just designed a complete API from scratch. By defining the database models, Pydantic schemas, and API endpoints, you've created a comprehensive blueprint that will guide our development for the rest of this module. This planning phase is critical for building robust and scalable applications.

Key Takeaways:

  • API design involves three pillars: Database Models (SQLAlchemy), Data Schemas (Pydantic), and API Endpoints (Routing).
  • A key principle is the separation of concerns between internal database models and external API schemas.
  • Designing schemas for different operations (Create, Read) provides flexibility and security, a pattern analogous to using multiple serializers in DRF.
  • A well-defined set of RESTful endpoints provides a logical and intuitive interface for API consumers.

Preview of the Next Lesson:

With our design complete, we are ready to start coding! In the next lesson, we will set up the project structure based on the modern patterns you've just reviewed. We will then begin implementing our design, starting with the User resource, including its SQLAlchemy model, Pydantic schemas, and initial authentication endpoints.