Understanding FastAPI
FastAPI is a modern, fast web framework for building APIs with Python based on standard Python type hints. It encourages the use of async and await for asynchronous programming and is designed to be easy to use while giving a performance comparable to Node.js and Go. Its main features are a simple yet powerful design, automatic interactive API documentation, and built-in data validation.
Environment Setup
Prerequisites
Before diving into building your API, make sure you have Python 3.7 or later installed on your machine. You can verify your Python version by running:
python --version
Installing FastAPI
To get started, you need to install FastAPI and an ASGI server to serve your application. We recommend using uvicorn for this purpose. Install both with pip:
pip install fastapi uvicorn
Directory Structure
Create a directory for your project:
mkdir fastapi_service
cd fastapi_service
Inside this directory, create a new Python file named main.py, where your API will be defined.
Creating a Simple API
Defining Your First Endpoint
Open main.py and set up a simple “Hello World” endpoint:
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"message": "Hello, World"}
Running the Application
To run your FastAPI application with uvicorn, execute the following command in the terminal:
uvicorn main:app --reload
The --reload flag allows for automatic code reloads when changes are made to the codebase. Visit http://127.0.0.1:8000 in your browser, and you should see the JSON response: {"message": "Hello, World"}.
Interactive API Documentation
FastAPI automatically provides interactive API documentation using Swagger UI and ReDoc. You can access these at:
- Swagger UI:
http://127.0.0.1:8000/docs - ReDoc:
http://127.0.0.1:8000/redoc
Working with Path Parameters
Path parameters allow you to accept variables in your API paths. To demonstrate this, modify your main.py as follows:
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str = None):
return {"item_id": item_id, "query": q}
Here, you can access an item by its ID, and there’s an optional query parameter q. If you navigate to http://127.0.0.1:8000/items/42?q=test, you’ll see:
{"item_id": 42, "query": "test"}
Working with Request Body
You can define more complex data requirements using Pydantic models. Create a new model for items:
from pydantic import BaseModel
class Item(BaseModel):
name: str
price: float
is_offer: bool = None
@app.post("/items/")
def create_item(item: Item):
return {"item": item}
This sets up a POST endpoint, where users can send a JSON object with name, price, and is_offer. Test this endpoint using an HTTP client like Postman or CURL.
Example CURL Command
Here’s how to test your POST request using CURL:
curl -X POST "http://127.0.0.1:8000/items/" -H "Content-Type: application/json" -d '{"name": "Example", "price": 10.5, "is_offer": true}'
The response should echo the item back in JSON format.
Adding Query Parameters
Aside from path and body parameters, FastAPI supports query parameters. Modify your create_item function to accept additional query parameters:
@app.post("/items/")
def create_item(item: Item, description: str = None):
return {"item": item, "description": description}
Now you can include a description when creating an item, e.g., http://127.0.0.1:8000/items/?description=Sample%20Item.
Asynchronous Support
FastAPI is designed for asynchronous programming. You can define asynchronous endpoints easily:
@app.get("/async_items/")
async def read_async_items():
return {"message": "This is an asynchronous response!"}
With async and await, you can effectively handle high-concurrency scenarios, making FastAPI suitable for building high-speed applications.
Handling Errors
To manage errors effectively, you can use FastAPI’s exception handling features. For instance, you can raise an HTTPException if an item is not found:
from fastapi import HTTPException
@app.get("/items/{item_id}")
def read_item(item_id: int):
if item_id not in items_db: # Assume items_db is predefined
raise HTTPException(status_code=404, detail="Item not found")
return items_db[item_id]
Custom Exception Handling
You can even define custom exception handlers to manage your error responses neatly and consistently.
Dependency Injection
FastAPI has built-in support for dependency injection, which is a great way to manage dependencies in your application. For instance:
from fastapi import Depends
def get_query_params(q: str = None):
return q
@app.get("/items/")
def read_items(q: str = Depends(get_query_params)):
return {"query": q}
This way, you can create reusable components to handle different aspects of your application with minimal duplication.
Middleware
FastAPI supports middleware, which acts as a bridge between requests received by the application and the responses sent back. You can use middleware for cross-cutting concerns like logging, CORS, etc.
Example: CORS Middleware
To add CORS middleware, install the required package:
pip install fastapi[all]
Then configure CORS in your application:
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # Allows all origins
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
User Authentication
FastAPI also offers versatile options for APIs requiring authentication. You can use OAuth2, JWT tokens, or any other method suited to your project needs.
Example: Simple Bearer Token Authentication
Set up a simple token authentication process:
from fastapi import Security, Depends
from fastapi.security import OAuth2PasswordBearer
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/users/me")
async def read_users_me(token: str = Depends(oauth2_scheme)):
return {"token": token}
In a real-world application, you would decode the token and validate the user’s identity.
Database Integration
Integrating a database is crucial for most applications. FastAPI can easily connect with various databases using ORMs like SQLAlchemy or Tortoise-ORM.
Example with SQLAlchemy
- Install SQLAlchemy:
pip install sqlalchemy
- Set up a database model:
from sqlalchemy import Column, Integer, String
from database import Base # Assume Base is your SQLAlchemy base
class ItemModel(Base):
__tablename__ = "items"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, index=True)
price = Column(Float)
- Connect FastAPI with the database session.
Testing Your API
FastAPI also emphasizes testing your API endpoints. You can create tests using the pytest framework. Start by installing it:
pip install pytest
Create a tests directory and write tests using httpx for real HTTP requests to your FastAPI app.
Example Test
from fastapi.testclient import TestClient
client = TestClient(app)
def test_read_root():
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"message": "Hello, World"}
Deploying FastAPI
Deployment options for FastAPI are flexible. You can deploy using a variety of services, including AWS, Heroku, and Digital Ocean. A common deployment method is using Docker to containerize your application for easier scaling and management.
Example Dockerfile
Here’s a simple Dockerfile for your FastAPI application:
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.7
COPY ./app /app
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
Conclusion
FastAPI is a powerful tool that simplifies creating RESTful APIs with Python. The combination of clear syntax, automatic documentation, data validation, and support for asynchronous programming makes it an excellent choice for developers aiming to build efficient and scalable APIs. With its robust features, intuitive design, and responsive community support, FastAPI has quickly become a favorite among Python developers. As you grow more comfortable with FastAPI, you will find a wealth of tools, libraries, and extensions to enhance your API capabilities even further. Happy coding!