Skip to content

Latest commit

 

History

History
275 lines (177 loc) · 8.87 KB

File metadata and controls

275 lines (177 loc) · 8.87 KB

Make an API

Requirements

See the README file for the requirements.

A. Setup fastapi and uvicorn

  1. Set up your FastAPI by creating your main.py file.

    Tip: You can start with something simple, such as:

    # main.py
    from fastapi import FastAPI
    
    app = FastAPI()
    
    @app.get("/")
    
    def read_root():
        return {"message": "This is YOUR API!"}
  2. From the terminal, in the same folder where main.py is located, run the following command:

    uvicorn main:app --reload

    If the script was successful, you can try opening your browser to:

    Troubleshooting: To ensure that the the uvicorn command runs successfully, make sure you are using a virtual environment (venv).

B. Add a Pydantic model for ForageItem

  1. In your main.py file, add to the top, below the FastAPI import:

    from pydantic import BaseModel, Field, HttpUrl
    from typing import Optional
    from datetime import date
  2. Define the ForageItem model, by creating a new class:

    class ForageItem(BaseModel):
        id_num: int = Field(..., example=1, description="Unique ID for the item")
        name: str = Field(..., example="Chanterelle", description="Name of the mushroom or berry")
        item_type: str = Field(..., example="mushroom", description="Either 'mushroom' or 'berry'")
        location: str = Field(..., example="Grimsta Naturreservat", description="Where it was found")
        date_found: date = Field(..., example="2025-07-21", description="Date of the foraging event")
        is_edible: bool = Field(..., example=True, description="Whether the item is safe to eat")
        notes: Optional[str] = Field(None, example="Found under pine trees", description="Extra notes")
        photo_url: Optional[HttpUrl] = Field(None, example="https://example.com/photo.jpg" description="Optional image link")

    Explanation of the Python constructs used:

    • BaseModel a class that is the "core" of Pydandic. All your data models inherit from it.

    • Field() a function that lets your customise descriptions, examples, and validation.

    • HttpUrl a type that ensures only valid URLs are provided for image links.

    • Optional[...] a type that lets you declare if a field is optional.

    • date a class allows you to store calendar dates, as a date data type, not just as a string (str). FastAPI will automatically convert string input into a date type if the format is valid.

  3. Run the API with the following command:

    uvicorn main:app --reload
  4. Visit the Swagger or Redoc version of the API to see how it has been rendered.

C. Create basic /items endpoint to POST new items

  1. Create an empty list to act as a "fake" database, after app=FastAPI():

    fake_db = []

    This list will hold each ForageItem that you POST. It acts as a temporary storage. You can later replace this with a real database, such as SQLite.

  2. Define POST /items route:

    a. Add the following to your imports:

    from fastapi import HTTPException

    b. Update the @app.get to @app.post.

    c. In @app.post, add the following:

    @app.post("/items", response_model=ForageItem, status_code=201)
    ...

    Explantion of elements used:

    • This creates a POST endpoint at /items

    • response_model=ForageItems tells FastAPI to use the same schema when sending the response

    • status_code=201 is the HTTP code for "Created"

  3. Use the ForageItem model to validate incoming data, by adding the following in the next line after @app.post():

        def create_item(item: ForageItem):
        if any(existing.id == item.id for existing in fake_db):
            raise HTTPException(status_code=400, detail="Item with this ID already exists.")
    
  4. Add the new item to the list and return it:

            fake_db.append(item)
    
            return item
  5. Go to http://localhost:8000/docs, click POST /items, and try the following sample payload:

    {
        "id": 1,
        "name": "Chanterelle",
        "type": "mushroom",
        "location": "Tyresta National Park",
        "date": "2025-07-21",
        "is_edible": true,
        "notes": "Found under pine trees",
        "photo_url": "https://example.com/chanterelle.jpg"
    }

    Add items endpoint to POST

    You should get a 201 Created response and see your item echoed back.

Full code added from this section:

# main.py

from fastapi import FastAPI
from fastapi import HTTPException
from pydantic import BaseModel, Field, HttpUrl
from typing import Optional
from datetime import date


# Create an instance of the FastAPI application
app = FastAPI()

# Temporary in-memory storage for forage items
fake_db = []


# Define a new data model called ForageItem, which inherits from BaseModel, so FastAPI knows how to parse, validate, and document it
class ForageItem(BaseModel):
    id_num: int = Field(..., example=1, description="Unique ID for the item")
    name: str = Field(..., example="Chanterelle", description="Name of the mushroom or berry")
    item_type: str = Field(..., example="mushroom", description="Either 'mushroom' or 'berry'")
    location: str = Field(..., example="Grimsta Naturreservat", description="Where it was found")
    date_found: date = Field(..., example="2025-07-21", description="Date of the foraging event")
    is_edible: bool = Field(..., example=True, description="Whether the item is safe to eat")
    notes: Optional[str] = Field(None, example="Found under pine trees", description="Extra notes")
    photo_url: Optional[HttpUrl] = Field(None, example="https://example.com/photo.jpg", description="Optional image link")


# Define a route: when someone visits GET / (the root), this function runs
@app.post("/items", response_model=ForageItem, status_code=201)
def create_item(item: ForageItem):
    # Check for duplicates by ID
    if any(existing.id == item.id for existing in fake_db):
        raise HTTPException(status_code=400, detail="Item with this ID already exists.")

    # Add the item to the fake database
    fake_db.append(item)

    return item

D. Add GET /items to list all foraged items

Now that the API allows you to create (but not yet store) items, add to the script to be able to retrieve items.

  1. Add the following to main.py:

    • Import typing from List
    • Add @app.get and the following function below the existing POST route
    from typing import List
    ... # Rest of the script
    
    @app.get("/items", response_model=List[ForageItem])
    def get_all_items():
        return fake_db

    Explanation of what was added:

    For @app.get,

    • The function defines a GET endpoint at /items.

    • The response_model declares that it will return a list of ForageItems.

  2. (Optional) Test out the GET function:

    • Run the API from your CLI.

    • Go to http://127.0.0.1:8000/docs (features Try it out).

    • In the POST section, click Try it out and paste in the example value.

    • Click Execute. (Tip: try adding another example response 😄.)

    • Go to the GET section, and click Try it out.

    You should see the example(s) values you executed in the POST section.

E. Retrieve foraged items by ID

To retrieve foraged items by its unique ID, add GET /items/{id} by adding the following to main.py:

from fastapi import Path

# Add this below the other routes
@app.get("/items/{item_id}", resposne_model=ForageItem)
def get_item_by_id(item_id: int = Path(..., description="ID of the item you want to retrieve")):
    # Search the fake database for the item
    for item in fake_db:
        if item.id == item_id:
            return item

    # If not found, raise a 404 error
    raise HTTPException(status_code=404, detail="Item not found. Try another ID number.")

Note: Path(...) is optional but useful for docs (you can add more metadata or validations here). It is a function from FastAPI that lets you describe and validate path parameters. It allows you to:

  • Set constraints or rules

  • Declare default values if the parameter is not required

  • Add more descriptive information for documentation (for example, with Redocly or Swagger)

F. Delete items by ID

(With DELETE /items/{id})

G. Update existing entries

(With PUT /items/{id})