Extending the API
Relevant source files
The following files were used as context for generating this wiki page:
This document outlines the process for extending the FastAPI backend by adding new API endpoints. The project follows a modular structure that makes adding new features straightforward and consistent. The core idea is to group related endpoints into their own "router" files, define clear data contracts using Pydantic schemas, and then register these routers with the main FastAPI application.
The backend is built with FastAPI, using SQLAlchemy for database interaction and Pydantic for data validation. Understanding these components is key to extending the system. The process generally involves defining data models, creating a new router file with endpoint logic, and integrating it into the main application.
API Architecture Overview
The API is structured around a main application file (main.py) that aggregates several modular routers from the backend/api/ directory. Each router handles a specific domain, such as questions or answers. Data structures for requests and responses are centrally defined in backend/schemas.py.
Sources: backend/main.py:48-50, backend/api/questions.py, backend/api/answers.py
This diagram illustrates the high-level architecture of the backend application.
Key Components
| File | Purpose |
|---|---|
backend/main.py | The main FastAPI application entry point. It initializes the database, configures middleware, and includes all API routers. |
backend/api/ | A directory containing modular APIRouter files, each dedicated to a specific resource (e.g., questions, answers). |
backend/schemas.py | Contains all Pydantic models used for request validation, response serialization, and data contract definition. |
backend/database.py | Defines SQLAlchemy ORM models and provides the database session dependency (get_db). |
backend/security.py | Handles user authentication and authorization via JWT tokens, providing dependencies like get_current_user. |
Sources: backend/main.py, backend/schemas.py, backend/database.py, backend/security.py
Step 1: Define Data Schemas
Before creating an endpoint, you must define the data structures it will accept and return. This is done in backend/schemas.py using Pydantic's BaseModel.
Schema Types
- Create Schemas (e.g.,
QuestionCreate): Define the fields required to create a new resource. These are used for request bodies inPOSTendpoints. - Update Schemas (e.g.,
QuestionUpdate): Define optional fields that can be modified. These are used forPATCHendpoints. - Response Schemas (e.g.,
QuestionResponse): Define the full data structure of a resource that is sent back to the client. These are used in theresponse_modelof an endpoint.
# backend/schemas.py:30-33
# Example of a "Create" schema
class QuestionCreate(BaseModel):
message: str
Sources: backend/schemas.py:30-33
# backend/schemas.py:56-59
# Example of a "Response" schema
class AnswerResponse(BaseModel):
id: str
# ... other fields
user: UserResponse
Sources: backend/schemas.py:56-62
The Config inner class with from_attributes = True allows Pydantic to create a schema from a SQLAlchemy ORM object, which is essential for serializing database models into JSON responses.
Schema Relationships
This diagram shows the relationships between some of the core Pydantic schemas.
Sources: backend/schemas.py:53, backend/schemas.py:62
Step 2: Create a New API Router
Group related endpoints into a new Python file inside the backend/api/ directory. For example, to add endpoints for a "tags" feature, you would create backend/api/tags.py.
Each router file should:
- Import
APIRouterfrom FastAPI. - Instantiate a router with a
prefixandtags. The prefix is prepended to all endpoint paths in the file, and tags are used for grouping in the OpenAPI documentation.
# backend/api/questions.py:26-27
from fastapi import APIRouter
# ... other imports
router = APIRouter(prefix="/questions", tags=["questions"])
Sources: backend/api/questions.py:26-27
Step 3: Implement Endpoint Logic
Within your new router file, define functions for each endpoint using FastAPI's operation decorators (@router.get, @router.post, etc.).
Endpoint Function Structure
An endpoint function typically includes:
- A
response_modelin the decorator to define and validate the output. - Parameters for path variables, query parameters, or request bodies.
- Dependencies for database sessions (
db: Session = Depends(get_db)) and user authentication (current_user: User = Depends(get_current_user)).
This sequence diagram shows the flow for creating a new question.
Interacting with the Database
Use the db session object provided by the get_db dependency to perform CRUD operations with your SQLAlchemy models.
- Create: Instantiate a model,
db.add(),db.commit(),db.refresh(). - Read:
db.query(Model).filter(...).all()or.first(). - Update: Query an object, modify its attributes,
db.commit(). - Delete: Query an object,
db.delete(),db.commit().
# backend/api/answers.py:29-36
# Example of creating a new Answer
db_answer = Answer(
question_id=question_id, user_id=current_user.id, message=answer_data.message
)
db.add(db_answer)
db.commit()
db.refresh(db_answer)
Sources: backend/api/answers.py:29-36
Step 4: Register the New Router
The final step is to make the main FastAPI application aware of your new router. In backend/main.py, import your new router and include it using app.include_router().
# backend/main.py
# 1. Import your new router
from api import auth, questions, answers, your_new_router
# ... (FastAPI app setup)
# 2. Include your new router
app.include_router(auth.router)
app.include_router(questions.router)
app.include_router(answers.router)
app.include_router(your_new_router.router) # Add this line
Sources: backend/main.py:48-50
Step 5: Update Frontend Configuration (Optional)
If the new endpoint needs to be called from the frontend, add it to the API_ENDPOINTS object in web/src/config/api.js. This centralizes all API paths for the client-side application.
// web/src/config/api.js:9
export const API_ENDPOINTS = {
// ... existing endpoints
GET_QUESTIONS: '/questions',
CREATE_QUESTION: '/questions',
// Add your new endpoint here
GET_MY_NEW_FEATURE: '/my-new-feature',
};
Sources: web/src/config/api.js:9-21
Securing New Endpoints
To protect an endpoint, add current_user: User = Depends(get_current_user) to its signature. If the client does not provide a valid JWT token, a 401 Unauthorized error will be automatically returned.
For admin-only endpoints, use Depends(get_admin_user).
# backend/api/answers.py:50-54
# Example of an admin-only endpoint
async def delete_answer(
answer_id: str,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
if not current_user.is_admin:
raise HTTPException(...)
Sources: backend/api/answers.py:50-58, backend/security.py:110-115
Summary
Extending the API is a structured process: define your data contracts in schemas.py, create a new router file in api/ with the business logic, and register it in main.py. This modular approach keeps the codebase organized and scalable. By leveraging FastAPI's dependency injection for database sessions and security, new endpoints can be added with minimal boilerplate and a consistent design pattern.