Okay, here’s a comprehensive article on getting started with Python FastAPI, aiming for approximately 5000 words:
Python FastAPI: A Comprehensive Getting Started Guide
FastAPI is a modern, fast (high-performance), web framework for building APIs with Python 3.7+ based on standard Python type hints. It’s designed to be easy to use, fast to code with, and incredibly performant. It boasts automatic data validation, interactive documentation, and a focus on developer experience. This guide will walk you through everything you need to know to get started with FastAPI, from basic installation to deploying a production-ready API.
Table of Contents
- Introduction: Why FastAPI?
- The Need for Speed and Simplicity
- Key Features
- Comparison with Flask and Django
- Installation and Setup
- Prerequisites
- Installing FastAPI and Uvicorn
- Creating a Virtual Environment (Recommended)
- Setting up Your Project Directory
- Your First FastAPI Application
- Creating a Minimal
main.py
- Running the Application with Uvicorn
- Understanding the Code
- Interactive API Documentation (Swagger UI and ReDoc)
- Creating a Minimal
- Path Operations
- Defining Path Parameters
- Type Hints and Data Validation
- Query Parameters
- Request Body (Using Pydantic)
- Combining Path, Query, and Request Body Parameters
- Data Validation and Modeling with Pydantic
- Introduction to Pydantic
- Defining Data Models
- Field Types and Constraints
- Nested Models
- Automatic Data Conversion
- Custom Validators
- Handling Errors
- Default Error Handling
- Raising HTTP Exceptions
- Custom Exception Handlers
- Response Handling
- Returning Different Response Types (JSON, HTML, Plain Text, etc.)
- Setting Response Status Codes
- Custom Headers
- Response Models (Data Filtering and Transformation)
- Working with Databases
- Choosing a Database (SQLite, PostgreSQL, MySQL, etc.)
- Using SQLAlchemy (ORM)
- Connecting to a Database
- Defining Database Models
- CRUD Operations (Create, Read, Update, Delete)
- Database Migrations (Alembic)
- Dependency Injection
- Understanding Dependency Injection
- Creating Dependencies
- Using Dependencies in Path Operations
- Sub-Dependencies
- Dependencies with
yield
(Context Managers)
- Background Tasks
- Running Tasks in the Background
- Using
BackgroundTasks
- When to Use Background Tasks
- Security
- Authentication (OAuth2 with Password Flow)
- Authorization (Roles and Permissions)
- Protecting Against Common Web Vulnerabilities (CORS, CSRF)
- HTTPS
- Testing
- Writing Unit Tests with
pytest
- Testing Path Operations
- Using TestClient
- Mocking Dependencies
- Writing Unit Tests with
- Deployment
- Choosing a Deployment Strategy (Docker, Cloud Providers, etc.)
- Preparing for Production (Gunicorn)
- Example: Deploying to Heroku
- Example: Deploying with Docker
- Advanced Topics
- Using WebSockets
- Middleware
- Customizing OpenAPI (Swagger UI)
- Asynchronous Database Operations
- Streaming Responses
- Event Handling (Startup and Shutdown)
- Conclusion and Further Resources
1. Introduction: Why FastAPI?
In the world of web development with Python, frameworks like Flask and Django have long been the dominant players. Flask is known for its simplicity and flexibility, making it ideal for small to medium-sized projects. Django, on the other hand, is a full-featured framework with a “batteries-included” approach, suitable for large and complex applications. FastAPI enters this landscape as a modern alternative, combining the best aspects of both while introducing innovative features.
-
The Need for Speed and Simplicity:
Modern web applications, especially APIs, demand high performance and rapid development cycles. Traditional frameworks often require significant boilerplate code and manual configuration. FastAPI addresses these concerns by leveraging Python’s type hints and asynchronous capabilities to provide both speed and simplicity.
-
Key Features:
- Fast: Built on top of Starlette (for the web parts) and Pydantic (for the data parts), FastAPI is exceptionally fast, comparable to NodeJS and Go in terms of performance. This is largely due to its asynchronous nature, allowing it to handle many concurrent requests efficiently.
- Fast to Code: FastAPI minimizes code duplication and boilerplate. Type hints drive automatic data validation, serialization, and documentation. This significantly speeds up development time.
- Easy to Learn: The framework is designed to be intuitive and easy to learn, with a clear and concise API. The excellent documentation further aids in rapid onboarding.
- Automatic Data Validation: Leveraging Pydantic, FastAPI automatically validates incoming data based on type hints. This prevents common errors and ensures data integrity.
- Interactive API Documentation: FastAPI automatically generates interactive API documentation using OpenAPI (Swagger UI) and ReDoc. This makes it easy for developers to understand and interact with your API.
- Dependency Injection: FastAPI has a powerful yet easy-to-use dependency injection system, promoting code reusability and testability.
- Standards-Based: FastAPI is built on open standards like OpenAPI (formerly Swagger) and JSON Schema, ensuring compatibility and interoperability.
-
Comparison with Flask and Django:
Feature FastAPI Flask Django Performance Very High Moderate Moderate Ease of Use Very Easy Easy Moderate Data Validation Automatic (Pydantic) Manual/Extensions ORM-based Documentation Automatic (OpenAPI) Manual/Extensions Manual/Extensions Dependency Inj. Built-in Extensions Limited Async Support Native Extensions ASGI Support (Recent) “Batteries” Minimal Minimal Full-Featured Best For APIs, Microservices Small/Medium Projects Large/Complex Projects FastAPI is a strong choice for building APIs and microservices where performance and development speed are critical. Flask remains a good option for smaller projects or when maximum flexibility is required. Django excels in building large, monolithic applications with many built-in features.
2. Installation and Setup
-
Prerequisites:
- Python 3.7+: FastAPI requires Python 3.7 or higher. You can check your Python version by running
python --version
orpython3 --version
in your terminal. - pip: The Python package installer. It usually comes bundled with Python. Verify with
pip --version
orpip3 --version
.
- Python 3.7+: FastAPI requires Python 3.7 or higher. You can check your Python version by running
-
Installing FastAPI and Uvicorn:
Open your terminal and run the following command:
bash
pip install fastapi uvicornThis installs both FastAPI and Uvicorn. Uvicorn is an ASGI (Asynchronous Server Gateway Interface) server, which is needed to run FastAPI applications. Think of it as the engine that handles incoming requests and serves your FastAPI application.
-
Creating a Virtual Environment (Recommended):
It’s highly recommended to use a virtual environment to isolate your project’s dependencies. This prevents conflicts between different projects that might require different versions of the same package.
“`bash
Create a virtual environment (choose one)
python3 -m venv .venv # Using the built-in venv module
OR
virtualenv .venv # Using the virtualenv package (if installed)
Activate the virtual environment (different commands depending on your OS)
On macOS and Linux:
source .venv/bin/activate
On Windows (cmd.exe):
.venv\Scripts\activate.bat
On Windows (PowerShell):
.venv\Scripts\Activate.ps1
“`Once the virtual environment is activated, your terminal prompt will usually change to indicate this (e.g.,
(.venv) $
). Now, any packages you install withpip
will be installed within this isolated environment. To deactivate the environment, simply rundeactivate
. -
Setting up Your Project Directory:
Create a new directory for your project:
bash
mkdir my_fastapi_project
cd my_fastapi_projectInside this directory, you’ll create your Python files.
3. Your First FastAPI Application
-
Creating a Minimal
main.py
:Create a file named
main.py
inside your project directory with the following content:“`python
from fastapi import FastAPIapp = FastAPI()
@app.get(“/”)
async def root():
return {“message”: “Hello World”}
“` -
Running the Application with Uvicorn:
In your terminal, within your project directory and with your virtual environment activated, run the following command:
bash
uvicorn main:app --reloaduvicorn
: Starts the Uvicorn server.main
: Refers to themain.py
file.app
: Refers to theapp = FastAPI()
instance withinmain.py
.--reload
: This is a development flag that tells Uvicorn to automatically restart the server whenever you make changes to your code. This is extremely helpful during development.
You should see output similar to this:
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [12345] using statreload
INFO: Started server process [67890]
INFO: Waiting for application startup.
INFO: Application startup complete. -
Understanding the Code:
from fastapi import FastAPI
: Imports theFastAPI
class.app = FastAPI()
: Creates an instance of theFastAPI
class. This is your main application object.@app.get("/")
: This is a path operation decorator. It tells FastAPI that the function below should handle requests to the root path (/
) using the HTTP GET method.async def root():
: Defines an asynchronous function namedroot
. Theasync
keyword is crucial for FastAPI’s performance. It indicates that this function can run concurrently with other tasks.return {"message": "Hello World"}
: Returns a Python dictionary. FastAPI automatically converts this dictionary into a JSON response.
-
Interactive API Documentation (Swagger UI and ReDoc):
One of the most compelling features of FastAPI is its automatic API documentation. Once your application is running, open your web browser and go to:
- Swagger UI:
http://127.0.0.1:8000/docs
- ReDoc:
http://127.0.0.1:8000/redoc
Swagger UI provides an interactive interface where you can explore your API, see the available endpoints, their parameters, and even try them out directly in the browser. ReDoc offers a more traditional, read-only documentation view. These are generated automatically based on your code and type hints.
- Swagger UI:
4. Path Operations
Path operations are the core of your API. They define the different endpoints that your API exposes and the HTTP methods they support.
-
Defining Path Parameters:
Path parameters are parts of the URL that can vary. You define them using curly braces
{}
in the path.“`python
from fastapi import FastAPIapp = FastAPI()
@app.get(“/items/{item_id}”)
async def read_item(item_id: int):
return {“item_id”: item_id}
“`In this example,
item_id
is a path parameter. When you access/items/5
, FastAPI will extract the value5
and pass it to theread_item
function as theitem_id
argument. -
Type Hints and Data Validation:
Notice the
item_id: int
in the function signature. This is a Python type hint. FastAPI uses these type hints to:- Validate the data: If you try to access
/items/abc
, FastAPI will return an error because “abc” cannot be converted to an integer. - Convert the data: FastAPI will automatically convert the string “5” from the URL into an integer
5
. - Provide documentation: The type hint is used to generate the API documentation.
You can use various Python types, including
str
,float
,bool
, and even more complex types likedatetime
and custom Pydantic models (which we’ll cover later). - Validate the data: If you try to access
-
Query Parameters:
Query parameters are optional parameters passed in the URL after a question mark (
?
). They are typically used for filtering, sorting, or pagination.“`python
from fastapi import FastAPIapp = FastAPI()
@app.get(“/items/”)
async def read_items(skip: int = 0, limit: int = 10):
return {“skip”: skip, “limit”: limit}
“`Here,
skip
andlimit
are query parameters. You can access them like this:/items/
(uses default values:skip=0
,limit=10
)/items/?skip=20&limit=5
FastAPI handles default values, type conversion, and validation for query parameters just like it does for path parameters. You can also make query parameters required by omitting the default value:
python
@app.get("/items/")
async def read_items(q: str): # 'q' is now a required query parameter
return {"q": q} -
Request Body (Using Pydantic):
For operations that send data to the server (like creating or updating resources), you’ll typically use a request body. FastAPI uses Pydantic models to define the structure and validation of request bodies.
“`python
from fastapi import FastAPI
from pydantic import BaseModelapp = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None@app.post(“/items/”)
async def create_item(item: Item):
return item
“`class Item(BaseModel):
: Defines a Pydantic model namedItem
. This model specifies the expected structure of the request body.name: str
: Thename
field is a required string.description: str | None = None
: Thedescription
field is an optional string (can beNone
). The= None
provides a default value. The| None
syntax (orOptional[str]
) is used for optional fields.price: float
: Theprice
field is a required float.tax: float | None = None
: Thetax
field is an optional float.@app.post("/items/")
: Uses thePOST
HTTP method, typically used for creating resources.async def create_item(item: Item):
: Theitem
parameter is of typeItem
. FastAPI will automatically parse the request body (which should be JSON), validate it against theItem
model, and pass a populatedItem
object to the function.
To send a request to this endpoint, you would send a JSON payload like this:
json
{
"name": "Foo",
"description": "A very nice Item",
"price": 50.2,
"tax": 10.5
}
If any of the required fields are missing, or if the data types are incorrect, FastAPI will return a detailed error message. -
Combining Path, Query, and Request Body Parameters:
You can combine path parameters, query parameters, and a request body in a single path operation:
python
@app.put("/items/{item_id}") #PUT is often used for updating
async def update_item(item_id: int, item: Item, q: str | None = None):
result = {"item_id": item_id, **item.dict()} #converts Item instance to a dictionary
if q:
result.update({"q": q})
return result
This endpoint expects:- A path parameter
item_id
(an integer). - A request body that conforms to the
Item
model. - An optional query parameter
q
(a string).
- A path parameter
5. Data Validation and Modeling with Pydantic
Pydantic is a data validation and parsing library that is deeply integrated with FastAPI. It’s the foundation for defining request bodies, response models, and other data structures.
-
Introduction to Pydantic:
Pydantic allows you to define data models using Python type hints. It handles data validation, type coercion, and serialization/deserialization. It excels at converting raw data (like JSON) into well-defined Python objects and vice versa.
-
Defining Data Models:
As we saw earlier, you create Pydantic models by subclassing
BaseModel
:“`python
from pydantic import BaseModelclass User(BaseModel):
id: int
name: str
signup_ts: datetime | None = None
friends: list[int] = []
“` -
Field Types and Constraints:
Pydantic supports a wide range of field types, including:
- Basic Types:
int
,float
,str
,bool
,bytes
- Date/Time Types:
datetime
,date
,time
,timedelta
- Collection Types:
list
,tuple
,dict
,set
,frozenset
- Other Types:
UUID
,FilePath
,DirectoryPath
,EmailStr
,UrlStr
, and more.
You can also add constraints to your fields:
“`python
from pydantic import BaseModel, Fieldclass Item(BaseModel):
name: str = Field(…, min_length=3, max_length=50) # Required, length between 3 and 50
price: float = Field(…, gt=0) # Required, greater than 0
description: str | None = Field(None, title=”The description of the item”, max_length=300)
tax: float = 0.1 # Default value of 0.1
``
Field(…)
*: The ellipsis
…signifies that the field is *required*.
min_length
*,
max_length: String length constraints.
gt
*(greater than),
lt(less than),
ge(greater than or equal to),
le(less than or equal to): Numeric constraints.
title
*,
description`: Metadata that will be included in the generated OpenAPI documentation.
You can explore the Pydantic documentation for a complete list of available field types and constraints. - Basic Types:
-
Nested Models:
You can create complex, nested data structures by using Pydantic models within other Pydantic models:
“`python
from pydantic import BaseModelclass Image(BaseModel):
url: str
name: strclass Item(BaseModel):
name: str
price: float
image: Image | None = None # An optional Image
“`This allows you to represent hierarchical data in a structured and validated way.
-
Automatic Data Conversion:
Pydantic automatically converts data to the specified types when possible. For example, if you have a field defined as
price: float
and you receive"12.99"
in the JSON, Pydantic will convert it to the float12.99
. -
Custom Validators:
For more complex validation logic, you can define custom validators using the
@validator
decorator:“`python
from pydantic import BaseModel, validatorclass User(BaseModel):
name: str
age: int@validator("age") def age_must_be_positive(cls, value): if value <= 0: raise ValueError("Age must be positive") return value
“`
The
@validator("age")
decorator indicates that theage_must_be_positive
function should be used to validate theage
field. The validator function receives the field value as input and should either return the validated value or raise aValueError
if the validation fails.
6. Handling Errors
FastAPI provides robust error handling capabilities, automatically generating informative error responses for common scenarios.
-
Default Error Handling:
When a request fails validation (e.g., a required field is missing or a type mismatch occurs), FastAPI automatically returns a JSON response with a
422 Unprocessable Entity
status code and a detailed error message. For example:json
{
"detail": [
{
"loc": [
"body",
"name"
],
"msg": "field required",
"type": "value_error.missing"
}
]
}The
detail
field provides a list of errors, each with aloc
(location),msg
(message), andtype
(error type). This makes it easy for clients to understand what went wrong. -
Raising HTTP Exceptions:
You can manually raise HTTP exceptions within your path operations to return specific error responses. FastAPI provides the
HTTPException
class for this purpose:“`python
from fastapi import FastAPI, HTTPExceptionapp = FastAPI()
items = {“foo”: “The Foo Wrestlers”}
@app.get(“/items/{item_id}”)
async def read_item(item_id: str):
if item_id not in items:
raise HTTPException(status_code=404, detail=”Item not found”)
return {“item”: items[item_id]}
“`raise HTTPException(status_code=404, detail="Item not found")
: Raises anHTTPException
with a404 Not Found
status code and a custom detail message. FastAPI will catch this exception and return an appropriate JSON response.
You can use any valid HTTP status code (e.g.,
400 Bad Request
,401 Unauthorized
,403 Forbidden
,500 Internal Server Error
). -
Custom Exception Handlers:
For more fine-grained control over error handling, you can define custom exception handlers. These handlers are functions that are called when a specific exception is raised.
“`python
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from fastapi.exceptions import RequestValidationErrorapp = FastAPI()
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
return JSONResponse(
status_code=400, # Changed status code to 400
content={“message”: “Validation failed”, “errors”: exc.errors()},
)@app.post(“/items/”)
async def create_item(name: str): # No Pydantic model for demonstration
return {“name”: name}
“`@app.exception_handler(RequestValidationError)
: Registers a handler forRequestValidationError
, which is the exception raised by FastAPI for validation errors.async def validation_exception_handler(request: Request, exc: RequestValidationError):
: The handler function receives theRequest
object and the exception instance (exc
).return JSONResponse(...)
: Returns a custom JSON response. In this case, we’re changing the status code to400 Bad Request
and providing a more user-friendly message.
You can create custom exception handlers for any exception type, including your own custom exceptions.
7. Response Handling
FastAPI provides flexibility in how you return responses from your path operations.
-
Returning Different Response Types:
- JSON (Default): If you return a Python dictionary, list, or a Pydantic model, FastAPI automatically converts it to a JSON response.
-
HTML: You can return a string containing HTML, and FastAPI will set the
Content-Type
header totext/html
.
“`python
from fastapi import FastAPI
from fastapi.responses import HTMLResponseapp = FastAPI()
@app.get(“/html”, response_class=HTMLResponse)
async def get_html():
return “””
Some HTML in here
Look ma! HTML!
“””
``
Content-Type
* **Plain Text:** You can return a plain string, and FastAPI will set theto
text/plain`.“`python
from fastapi import FastAPI
from fastapi.responses import PlainTextResponseapp = FastAPI()
@app.get(“/plain-text”, response_class=PlainTextResponse)
async def get_plain_text():
return “This is plain text.”
“` -
Other Response Types: FastAPI provides specific response classes for various content types, including
FileResponse
,StreamingResponse
,RedirectResponse
, etc.
-
Setting Response Status Codes:
You can explicitly set the response status code using the
status_code
parameter in the path operation decorator:python
@app.post("/items/", status_code=201) # 201 Created
async def create_item(item: Item):
return item -
Custom Headers:
You can set custom response headers using the
response
object:“`python
from fastapi import FastAPI, Responseapp = FastAPI()
@app.get(“/items/”)
async def read_items(response: Response):
response.headers[“X-Custom-Header”] = “My Custom Value”
return {“message”: “Items”}
“` -
Response Models (Data Filtering and Transformation):
You can use Pydantic models as response models to filter and transform the data returned by your API. This is useful for:
- Hiding sensitive information: You might not want to expose all fields of a database model to the client.
- Formatting data: You can change the format of data (e.g., converting a
datetime
object to a specific string format). - Adding computed fields: You can add fields that are calculated based on other fields.
“`python
from fastapi import FastAPI
from pydantic import BaseModelapp = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = Noneclass ItemOut(BaseModel): # Response model
name: str
price: float@app.get(“/items/{item_id}”, response_model=ItemOut)
async def read_item(item_id: int):
# Simulate fetching an item from a database
item = Item(name=”Foo”, description=”A very nice Item”, price=50.2, tax=10.5)
return item
“`response_model=ItemOut
: Specifies that the response should be validated and serialized according to theItemOut
model. Only thename
andprice
fields will be included in the response, even though theitem
object has other fields.
You can also use
response_model
with a list of models, for instance:python
@app.get("/items/", response_model=list[ItemOut])
async def read_items():
# Simulate fetching a list of items from a database
items = [
Item(name="Foo", price=50.2),
Item(name="Bar", price=25.1),
]
return items
8. Working with Databases
Most real-world APIs interact with a database to store and retrieve data. FastAPI doesn’t have a built-in database layer, but it integrates seamlessly with popular database tools like SQLAlchemy (an ORM) and other database drivers.
-
Choosing a Database:
FastAPI is compatible with various databases, including:
- SQLite: A lightweight, file-based database. Good for development and small projects.
- PostgreSQL: A powerful, open-source relational database. A popular choice for production applications.
- MySQL: Another popular open-source relational database.
- MongoDB: A NoSQL document database.
The choice of database depends on your project’s requirements.
-
Using SQLAlchemy (ORM):
SQLAlchemy is a popular Object-Relational Mapper (ORM) for Python. It allows you to interact with databases using Python objects instead of writing raw SQL queries. This makes your code more readable, maintainable, and less prone to SQL injection vulnerabilities.
We install SQLAlchemy with:bash
pip install sqlalchemy -
Connecting to a Database:
Here’s an example of connecting to a SQLite database using SQLAlchemy:
“`python
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmakerSQLALCHEMY_DATABASE_URL = “sqlite:///./sql_app.db” # SQLite database file
engine = create_engine(
SQLALCHEMY_DATABASE_URL, connect_args={“check_same_thread”: False}
) # connect_args only needed for SQLite
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)Base = declarative_base()
``
SQLALCHEMY_DATABASE_URL
*: Defines the database connection string. For SQLite, it's the path to the database file. For other databases, you'll need to provide the appropriate connection details (username, password, host, port, database name).
create_engine(…)
*: Creates a SQLAlchemy engine, which is responsible for connecting to the database.
SessionLocal = sessionmaker(…)
*: Creates a session factory. You'll use this to create individual database sessions.
Base = declarative_base()`: Creates a base class for your declarative models.
* -
Defining Database Models:
You define your database tables as Python classes that inherit from
Base
:“`python
from sqlalchemy import Boolean, Column, Integer, String
from sqlalchemy.orm import relationship
from .database import Base # we import Base from our database.py fileclass Item(Base):
tablename = “items”id = Column(Integer, primary_key=True, index=True) title = Column(String, index=True) description = Column(String, index=True) is_active = Column(Boolean, default=True)
“`
__tablename__
: Specifies the name of the database table.Column(...)
: Defines the columns of the table, including their data types and constraints (e.g.,primary_key
,index
).
-
CRUD Operations (Create, Read, Update, Delete):
Here’s how you perform CRUD operations using SQLAlchemy and FastAPI:
“`python
from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy.orm import Session
from . import crud, models, schemas # our supporting files
from .database import SessionLocal, enginemodels.Base.metadata.create_all(bind=engine) # Creates the tables in the database
app = FastAPI()
Dependency
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()@app.post(“/items/”, response_model=schemas.Item) #schemas.Item is a Pydantic model
def create_item(item: schemas.ItemCreate, db: Session = Depends(get_db)): # schemas.ItemCreate is another Pydantic model
db_item = crud.create_item(db=db, item=item) # we create a new item
return db_item@app.get(“/items/”, response_model=list[schemas.Item])
def read_items(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
items = crud.get_items(db, skip=skip, limit=limit)
return items@app.get(“/items/{item_id}”, response_model=schemas.Item)
def read_item(item_id: int, db: Session = Depends(get_db)):
db_item = crud.get_item(db, item_id=item_id)
if db_item is None:
raise HTTPException(status_code=404, detail=”Item not found”)
return db_item@app.put(“/items/{item_id}”, response_model=schemas.Item)
def update_item(item_id: int, item_update: schemas.ItemUpdate, db: Session = Depends(get_db)):
db_item = crud.get_item(db, item_id)
if db_item is None:
raise HTTPException(status_code=404, detail=”Item not found”)updated_item = crud.update_item(db, db_item, item_update) return updated_item
@app.delete(“/items/{item_id}”)
def delete_item(item_id: int, db: Session = Depends(get_db)):
db_item = crud.get_item(db, item_id)
if db_item is None:
raise HTTPException(status_code=404, detail=”Item not found”)
crud.delete_item(db, db_item)
return {“message”: “Item deleted”}We place our CRUD functions in `crud.py`:
python
# crud.py
from sqlalchemy.orm import Session
from . import models, schemasdef get_item(db: Session, item_id: int):
return db.query(models.Item).filter(models.Item.id == item_id).first()def get_items(db: Session, skip: int = 0, limit: int = 100):
return db.query(models.Item).offset(skip).limit(limit).all()def create_