In the previous article, we explored why FastAPI has become one of the most popular backend frameworks for modern AI applications.
If you haven't read the previous post, check it out: https://dev.to/zeroshotanu/fastapi-for-ai-engineers-part-1-why-every-ai-backend-is-moving-toward-fastapi-45fg
Now it's time to build something practical.
Most backend applications revolve around four basic operations:
- Create
- Read
- Update
- Delete
Together, these operations are known as CRUD.
Whether you're building:
- a social media application,
- an e-commerce platform,
- a chatbot,
- or an AI agent,
CRUD operations are the foundation of backend development.
In this article, we'll build a simple Student Management API while learning:
- Path Parameters
- Query Parameters
- GET Requests
- POST Requests
- PUT Requests
- DELETE Requests
Creating Sample Data
Let's start with a small dataset.
from fastapi import FastAPI
app = FastAPI()
students = [
{
"id": 1,
"name": "Ananya",
"department": "CSE",
"cgpa": 8.9
},
{
"id": 2,
"name": "Rahul",
"department": "ECE",
"cgpa": 8.4
},
{
"id": 3,
"name": "Priya",
"department": "IT",
"cgpa": 9.1
}
]
Run the application:
uvicorn main:app --reload
Open Swagger UI:
http://127.0.0.1:8000/docs
Path Parameters
A path parameter is part of the URL itself.
/student/2
Here, 2 is the path parameter.
Think of path parameters as:
"I know exactly which resource I want."
Examples:
/users/10
/products/25
/orders/1001
/student/2
Let's fetch a specific student using their ID.
@app.get("/student/{id}")
def get_student_info(id: int):
for user in students:
if user["id"] == id:
return user
return {"message": "Student not found"}
Request:
/student/2
Response:
{
"id": 2,
"name": "Rahul",
"department": "ECE",
"cgpa": 8.4
}
Query Parameters
A query parameter appears after the ? in a URL.
/student?department="CSE"
They are commonly used for:
- filtering
- searching
- sorting
- pagination
Let's implement the same endpoint using a query parameter.
@app.get("/students")
def get_students(department: str):
filtered_students = []
for student in students:
if student["department"] == department:
filtered_students.append(student)
return filtered_students
Request:
/student?department="CSE"
Response:
{
"id": 1,
"name": "Ananya",
"department": "CSE",
"cgpa": 8.9
}
All students in CSE department would be filtered.
Query parameters are often optional and are used to modify, filter, or search results.
Path vs Query Parameters
| Path Parameter | Query Parameter |
|---|---|
| Part of URL path | Appears after ? |
| Identifies a resource | Filters or searches |
/student/1 |
/student?id=1 |
GET Request
GET requests are used to retrieve data.
@app.get("/students")
def get_all_students():
return students
Response:
[
{
"id": 1,
"name": "Ananya",
"department": "CSE",
"cgpa": 8.9
},
{
"id": 2,
"name": "Rahul",
"department": "ECE",
"cgpa": 8.4
},
{
"id": 3,
"name": "Priya",
"department": "IT",
"cgpa": 9.1
}
]
Request Bodies with Pydantic
When users send data to our API, FastAPI needs a way to validate that the incoming data has the correct structure.
This is where Pydantic comes in.
Pydantic allows us to define the expected shape of incoming data using Python classes.
For example, every student should have:
- an ID
- a name
- a department
- a CGPA
We can define this structure using a Pydantic model.
from pydantic import BaseModel
class Student(BaseModel):
id: int
name: str
department: str
cgpa: float
Now FastAPI automatically validates incoming requests.
For example, this request is valid:
{
"id": 4,
"name": "Karthik",
"department": "AI",
"cgpa": 8.8
}
But if someone sends:
{
"id": "four",
"name": "Karthik"
}
FastAPI will automatically return a validation error because:
id should be an integer
required fields are missing
This saves us from writing validation code manually.
We'll explore Pydantic, validation, optional fields, custom validators, and advanced request handling in a dedicated article later in this series.
POST Request
POST requests are used to create new resources.
from pydantic import BaseModel
class Student(BaseModel):
id: int
name: str
department: str
cgpa: float
@app.post("/student")
def add_student(student: Student):
students.append(student.dict())
return {
"message": "Student added successfully",
"student": student
}
Request Body:
{
"id": 4,
"name": "Karthik",
"department": "AI",
"cgpa": 8.8
}
PUT Request
PUT requests are used to update existing resources.
@app.put("/student/{id}")
def update_student(id: int, updated_student: Student):
for index, user in enumerate(students):
if user["id"] == id:
students[index] = updated_student.dict()
return {
"message": "Student updated successfully",
"student": updated_student
}
return {"message": "Student not found"}
Request:
PUT /student/2
DELETE Request
DELETE requests are used to remove resources.
@app.delete("/student/{id}")
def delete_student(id: int):
for index, user in enumerate(students):
if user["id"] == id:
deleted_student = students.pop(index)
return {
"message": "Student deleted successfully",
"student": deleted_student
}
return {"message": "Student not found"}
Request:
DELETE /student/3
CRUD Summary
| Operation | HTTP Method |
|---|---|
| Create | POST |
| Read | GET |
| Update | PUT |
| Delete | DELETE |
CRUD operations form the foundation of almost every backend application you'll build.
What's Next?
Right now, our data exists only in memory.
If the server restarts, everything disappears.
In the next article, we'll connect FastAPI with SQLite and MySQL so our application can store data permanently, just like real-world production systems.
Top comments (0)