Delete Expense by ID
Add an endpoint that removes an expense from the tracker
Writing code and entering commands is only available on desktop. Open this page on a larger screen to complete this chapter.
Why you need a delete endpoint
Users make mistakes. Someone logs an expense twice or enters the wrong amount. Without a delete endpoint, bad data stays in the tracker permanently.
The HTTP DELETE method signals that the client wants to remove a resource. You use the same {expense_id} path parameter to identify which expense to remove. Python's dict.pop() method removes a key from the dictionary and returns its value in one step. This lets you return the deleted expense so the client can confirm what was removed.
The error handling follows the same pattern as the get endpoint. If the identifier does not match any stored expense, you raise an HTTPException with status code 404.
Instructions
- Add the
@app.delete("/expenses/{expense_id}")decorator. - Define a function named
delete_expensewith anexpense_idparameter of typeint. - If
expense_idis not inexpenses, raiseHTTPException(status_code=404, detail="Expense not found"). - Return
expenses.pop(expense_id).
from datetime import datetime
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field, field_validator
from typing import Literal, Optional
app = FastAPI()
class Expense(BaseModel):
description: str = Field(min_length=1)
amount: float = Field(gt=0)
category: Literal["food", "transport", "entertainment", "utilities", "other"]
date: Optional[str] = None
@field_validator("date")
@classmethod
def validate_date_format(cls, v):
if v is not None:
try:
datetime.strptime(v, "%Y-%m-%d")
except ValueError:
raise ValueError("Date must be in YYYY-MM-DD format")
return v
expenses = {}
counter = 0
@app.post("/expenses", status_code=201)
def create_expense(expense: Expense):
global counter
counter += 1
expenses[counter] = {"id": counter, **expense.model_dump()}
return expenses[counter]
@app.get("/expenses")
def list_expenses(category: str = None):
if category:
return [e for e in expenses.values() if e["category"] == category]
return list(expenses.values())
@app.get("/expenses/{expense_id}")
def get_expense(expense_id: int):
if expense_id not in expenses:
raise HTTPException(status_code=404, detail="Expense not found")
return expenses[expense_id]
# Step 1: Add @app.delete("/expenses/{expense_id}")
# Step 2: Define delete_expense with expense_id: int
# Step 3: If expense_id not in expenses, raise HTTPException(status_code=404, detail="Expense not found")
# Step 4: Return expenses.pop(expense_id)
Interactive Code Editor
Sign in to write and run code, track your progress, and unlock all chapters.
Sign In to Start Coding