FastAPI Project Example: A Comprehensive Guide
FastAPI Project Example: A Comprehensive Guide
Hey guys! Today, we’re diving deep into creating a FastAPI project example. FastAPI has become a go-to framework for building APIs with Python, thanks to its speed, ease of use, and automatic data validation. Whether you’re a beginner or an experienced developer, this guide will walk you through setting up a project, defining endpoints, handling data, and more.
Table of Contents
Setting Up Your FastAPI Project
First things first, let’s get our environment ready. The initial setup is crucial for a smooth development process. This involves creating a virtual environment, installing FastAPI, and choosing a suitable project structure. Getting this right from the start will save you headaches down the line.
To kick things off, you’ll need Python installed on your system. I recommend using Python 3.7 or higher to take advantage of the latest features and improvements. Once you have Python, create a virtual environment. A virtual environment isolates your project dependencies, preventing conflicts with other projects. You can create one using the following commands:
python3 -m venv venv
source venv/bin/activate # On Linux/macOS
.\venv\Scripts\activate # On Windows
Next, install FastAPI and Uvicorn, an ASGI server that will run our application. Use pip, Python’s package installer:
pip install fastapi uvicorn
Now that we have our environment set up and FastAPI installed, let’s talk about project structure. A well-organized project structure makes your code easier to maintain and scale. Here’s a basic structure you can start with:
myproject/
├── app/
│ ├── __init__.py
│ ├── main.py
│ ├── models.py
│ ├── routers/
│ │ ├── __init__.py
│ │ └── items.py
│ └── database.py
├── venv/ # Your virtual environment
├── .gitignore
└── README.md
-
app/: This directory contains all the application-specific code. -
app/main.py: This is the main entry point of your application. -
app/models.py: This file defines your data models using Pydantic. -
app/routers/: This directory contains separate modules for different API endpoints. -
app/database.py: This file handles database connections and operations. -
venv/: Your virtual environment (usually ignored by version control). -
.gitignore: Specifies intentionally untracked files that Git should ignore. -
README.md: A file providing an overview of your project.
Remember , this is just a starting point. You can adjust the structure to fit the needs of your specific project. The key is to keep things organized and modular. Now that we have our project structure in place, let’s move on to defining our first API endpoint.
Defining Your First API Endpoint
Alright, let’s get our hands dirty and define our first API endpoint using FastAPI. Endpoints are the heart of any API; they define the specific URLs that clients can access to interact with our application. We’ll start with a simple “Hello, World!” endpoint and then move on to something a bit more complex.
Open up
app/main.py
and add the following code:
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def read_root():
return {"Hello": "World"}
Let’s break down what’s happening here:
-
We import the
FastAPIclass from thefastapilibrary. -
We create an instance of the
FastAPIclass, which will be our application. -
We use the
@app.get("/")decorator to define a GET endpoint at the root URL (“/”). -
The
read_rootfunction is an asynchronous function that returns a dictionary with a “Hello” key and a “World” value. FastAPI automatically converts this dictionary to JSON.
To run your application, use the following command:
uvicorn app.main:app --reload
-
app.main: This specifies the module and file where your FastAPI instance is located. -
app: This is the name of the FastAPI instance. -
--reload: This option tells Uvicorn to automatically reload the server whenever you make changes to your code.
Open your browser and go to
http://127.0.0.1:8000/
. You should see the following JSON response:
{"Hello": "World"}
Congratulations! You’ve just created your first FastAPI endpoint. Now, let’s create a more complex endpoint that accepts parameters.
Create a new file
app/routers/items.py
and add the following code:
from fastapi import APIRouter
router = APIRouter()
@router.get("/items/{item_id}")
async def read_item(item_id: int, q: str = None):
return {"item_id": item_id, "q": q}
Here, we’re using an
APIRouter
to define a separate module for our item-related endpoints. The
@router.get("/items/{item_id}")
decorator defines a GET endpoint at
/items/{item_id}
, where
item_id
is a path parameter. The
read_item
function takes the
item_id
as an integer and an optional query parameter
q
as a string. To include this router in our main application, we need to import it in
app/main.py
and include it in our FastAPI instance.
Modify
app/main.py
to include the new router:
from fastapi import FastAPI
from .routers import items
app = FastAPI()
app.include_router(items.router)
@app.get("/")
async def read_root():
return {"Hello": "World"}
Now, if you restart your server and go to
http://127.0.0.1:8000/items/123?q=test
, you should see the following JSON response:
{"item_id": 123, "q": "test"}
This demonstrates how to define endpoints with path and query parameters. FastAPI automatically handles the parsing and validation of these parameters, making your code cleaner and more efficient. Defining API endpoints becomes more manageable and scalable as you add more routes. Keep practicing and experimenting with different types of parameters and request methods to master this crucial aspect of FastAPI development.
Handling Data with Pydantic
Data handling is a crucial aspect of any API, and FastAPI makes it incredibly easy with Pydantic. Pydantic is a data validation and settings management library that uses Python type annotations. With Pydantic, you can define data models that automatically validate incoming data, ensuring that your application only processes valid information. This not only reduces errors but also makes your code more readable and maintainable.
Let’s define a simple data model for creating an item. Open up
app/models.py
and add the following code:
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = None
Here, we’re defining a class
Item
that inherits from
BaseModel
. The attributes of the class are annotated with Python type hints.
name
is a required string,
description
is an optional string (defaulting to
None
),
price
is a required float, and
tax
is an optional float (defaulting to
None
). Now, let’s use this model in our API endpoint. Go to
app/routers/items.py
and modify the code to include a POST endpoint that creates a new item:
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from typing import Optional
router = APIRouter()
class Item(BaseModel):
name: str
description: Optional[str] = None
price: float
tax: Optional[float] = None
items = {}
@router.post("/items/")
async def create_item(item: Item):
if item.name in items:
raise HTTPException(status_code=400, detail="Item already exists")
items[item.name] = item
return item
@router.get("/items/{item_id}")
async def read_item(item_id: str, q: str = None):
return {"item_id": item_id, "q": q}
In this code, we import
HTTPException
from
fastapi
to handle errors. We also define a dictionary
items
to store our items. The
@router.post("/items/")
decorator defines a POST endpoint at
/items/
. The
create_item
function takes an
Item
object as input, which FastAPI automatically validates using Pydantic. If the data is invalid, FastAPI will return an error response. If the item exists it will raise an
HTTPException
. Let’s modify the
read_item
function to retrieve the
item
from the dictionary.
@router.get("/items/{item_id}")
async def read_item(item_id: str, q: str = None):
if item_id not in items:
raise HTTPException(status_code=404, detail="Item not found")
return items[item_id]
To test this endpoint, you can use a tool like curl or Postman to send a POST request with the following JSON payload:
{
"name": "Example Item",
"description": "This is an example item",
"price": 99.99,
"tax": 9.99
}
FastAPI will automatically validate the data and create an
Item
object, which you can then use in your application logic. If you send invalid data, such as a string for the price, FastAPI will return an error response with a clear explanation of what went wrong. Using Pydantic for data handling not only simplifies your code but also makes it more robust and secure. By defining clear data models, you ensure that your application only processes valid data, reducing the risk of errors and security vulnerabilities.
Using Databases in FastAPI
Integrating a database into your FastAPI application is essential for storing and retrieving data. FastAPI works well with various databases, including relational databases like PostgreSQL and MySQL, as well as NoSQL databases like MongoDB. In this example, we’ll use a simple SQLite database for demonstration purposes. However, the principles can be applied to other databases as well.
First, install SQLAlchemy, a popular Python SQL toolkit and Object-Relational Mapper (ORM):
pip install sqlalchemy
Next, create a file
app/database.py
and add the following code:
from sqlalchemy import create_engine, Column, Integer, String, Float
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
class Items(Base):
__tablename__ = "items"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, unique=True, index=True)
description = Column(String)
price = Column(Float)
tax = Column(Float)
In this code, we’re using SQLAlchemy to define our database model. We create an engine that connects to our SQLite database, a sessionmaker to manage database sessions, and a base class for our models. The
Items
class defines the structure of our
items
table, with columns for
id
,
name
,
description
,
price
, and
tax
. Now, let’s modify our API endpoints to use the database. First, let’s create the database table. Add the following code to
app/main.py
:
from fastapi import FastAPI
from .routers import items
from .database import engine, Base
Base.metadata.create_all(bind=engine)
app = FastAPI()
app.include_router(items.router)
@app.get("/")
async def read_root():
return {"Hello": "World"}
Next, modify
app/routers/items.py
to interact with the database:
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from . import models, database
from typing import Optional
router = APIRouter()
class Item(models.Items):
pass
@router.post("/items/")
async def create_item(item: models.Items, db: Session = Depends(database.SessionLocal)):
db_item = models.Items(name=item.name, description=item.description, price=item.price, tax=item.tax)
db.add(db_item)
db.commit()
db.refresh(db_item)
return db_item
@router.get("/items/{item_id}")
async def read_item(item_id: int, db: Session = Depends(database.SessionLocal)):
item = db.query(models.Items).filter(models.Items.id == item_id).first()
if item is None:
raise HTTPException(status_code=404, detail="Item not found")
return item
Here, we’re using SQLAlchemy to interact with the database. The
create_item
function now takes a database session as a dependency, which FastAPI automatically provides. We create a new
Items
object, add it to the session, commit the changes, and return the created item. The
read_item
function retrieves an item from the database based on its ID. Using a database in your FastAPI application allows you to store and retrieve data in a structured and persistent manner. SQLAlchemy provides a powerful and flexible way to interact with databases, making it easier to manage your application’s data.
Conclusion
Alright, guys! We’ve covered a lot in this guide, from setting up your FastAPI project to defining endpoints, handling data with Pydantic, and integrating a database. You’ve now got a solid foundation for building your own APIs with FastAPI. Remember to keep practicing and experimenting with different features and techniques to master this powerful framework. Happy coding!