Build a Bookmark Manager API

Build a REST API with FastAPI that handles full CRUD operations.

Introduction

30 Second Summary

Every developer bookmarks useful links, but have you ever wondered how the apps that store those bookmarks actually work behind the scenes? The answer is a REST API, the same technology that powers almost every web and mobile app you use daily.

In this project, you will build a Bookmark Manager API from scratch using FastAPI. You'll create endpoints that save, retrieve, update, and delete bookmarks, then test them through auto-generated interactive documentation right in your browser.

What You'll Build

You'll type data into an interactive form, hit Execute, and watch your API respond in real time. No frontend code, no external tools. Just you, your API, and instant visual feedback.

By the end of this project, you'll have:

  • A working REST API with full CRUD operations (Create, Read, Update, Delete) that you can demo by sending requests and seeing structured responses come back instantly.
  • Automatic data validation powered by Pydantic that rejects bad input before it ever reaches your code, plus proper error handling that returns clear messages instead of crashing.
  • Auto-generated interactive API documentation at /docs where you can test every endpoint in your browser without writing a single line of frontend code.
  • Secret Mission: Add tag-based filtering so you can search bookmarks by category using query parameters.

Are there any prerequisites?

You'll need VS Code and Python 3.10 or higher installed on your machine. Step 1 walks you through installing everything else from scratch, including the project manager and FastAPI itself.

Before We Start

Before we dive in, let's lock in what you're about to build and why it matters to you.

Set Up Your FastAPI Project

Every API needs a home. Before you can write endpoints that save and retrieve bookmarks, you need a properly configured Python project with FastAPI installed.

In this step, you will install uv (a fast Python project manager), create your project, and launch a development server with your first endpoint.

In this step, get ready to:
  • Install uv and verify it works.
  • Create your project and add FastAPI as a dependency.
  • Write your first endpoint and see it in the browser.
Install uv

uv is a Python project manager recommended by FastAPI's official documentation. It handles virtual environments and dependency installation automatically, so you never accidentally install packages into your global Python.

  • Install uv by running the installer for your operating system:

macOS

curl -LsSf https://astral.sh/uv/install.sh | sh

Windows

powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
  • Close your terminal and open a new one so your system picks up the new uv command.
  • Verify the installation by running:
uv --version

✔️ I see a version number

You are good to go. uv is installed and ready.

ⓧ Command not found

Your terminal cannot find the uv binary. Try these fixes:

  • Make sure you opened a brand new terminal window after the install finished.
  • On macOS/Linux, run source $HOME/.local/bin/env to load the path into your current session, then try uv --version again.
  • On Windows, ensure the installer completed without errors and restart PowerShell.

Why uv instead of pip?

uv creates an isolated virtual environment automatically when you add packages. With plain pip, beginners often install packages globally and run into version conflicts across projects.

uv is also 10-100x faster than pip, so your installs finish almost instantly.

Create your project and add FastAPI

Now you will create a dedicated project directory and install FastAPI with all its standard dependencies (including the Uvicorn web server).

  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your project folder by running:
uv init bookmark-api --bare

What does this command do?

uv init bookmark-api --bare creates a new folder called bookmark-api with a minimal pyproject.toml file. The --bare flag skips generating sample code so you start with a clean slate.

  • Move into the new project folder by running:
cd bookmark-api
  • Add FastAPI with its standard extras by running:
uv add "fastapi[standard]"

You should see uv create a virtual environment, download FastAPI and its dependencies, and lock the versions. The output ends with a success message.

What did uv just do?

uv add did three things: created a .venv virtual environment in your project folder, added FastAPI to your pyproject.toml, and created a uv.lock file that pins exact package versions.

The [standard] extra includes the Uvicorn web server that will serve your API.

Seeing an error during install?

Make sure you are inside the bookmark-api directory before running uv add. Run pwd (macOS) or cd (Windows) to check your current location.

help me fix a uv add error when installing FastAPI

Create your first endpoint and start the server

Your project is configured. Now you will write a minimal FastAPI app and launch it to confirm everything works.

  • In VS Code, open the bookmark-api folder on your Desktop.
  • Create a new file called main.py in the bookmark-api folder.
  • Add the following code to main.py:
from fastapi import FastAPI

# Create the FastAPI application instance
app = FastAPI()


# Define a GET endpoint at the root path
@app.get("/")
async def root():
    return {"message": "Bookmark Manager API"}

What does this code do?

from fastapi import FastAPI brings in the FastAPI class from the package you just installed.

app = FastAPI() creates the application object that will receive and route all incoming requests.

@app.get("/") is a decorator that registers the function below it to handle GET requests at the root URL /. When someone visits that path, FastAPI runs root() and returns the dictionary as JSON.

  • Save main.py.
  • Start the development server by running this command in your terminal (make sure you are still in the bookmark-api folder):
uv run fastapi dev main.py

What does this command do?

uv run executes the command inside your project's virtual environment without you needing to activate it manually.

fastapi dev starts the development server with auto-reload enabled. Every time you save a change to main.py, the server restarts automatically.

Before you check the browser, what do you expect to see at the /docs URL based on the single endpoint you just wrote?

  • Open your browser and go to http://127.0.0.1:8000/docs.

You should see FastAPI's interactive Swagger UI documentation page with one endpoint listed: GET /. This page is auto-generated from your code.

Don't see the Swagger UI page?

  • Check that your terminal still shows the server running. Look for a line mentioning 127.0.0.1:8000. If you see an error, read the message for clues about what went wrong.
  • Make sure you saved main.py before starting the server.
  • Make sure the file is named exactly main.py (not Main.py or main.py.txt).

Help me fix my FastAPI dev server not starting

✔️ Awesome, I've got everything!

Great. Make sure you have saved main.py and your server is still running in the terminal.

ⓧ I'd like to double check the full code

Here is the complete main.py you should have at this point:

from fastapi import FastAPI

app = FastAPI()


@app.get("/")
async def root():
    return {"message": "Bookmark Manager API"}

Your FastAPI project is set up and serving its first endpoint. Next up, you will define your bookmark data model and build endpoints that create and list bookmarks.

Create Your First Bookmarks

Your development environment is ready, with a FastAPI server running and interactive docs at your fingertips. Now it's time for the first real payoff: making your API actually do something useful.

An API that can't accept data is just a static page. In this step, you'll define what a bookmark looks like, build an endpoint that accepts new bookmarks, and watch FastAPI automatically validate the incoming data for you.

In this step, get ready to:
  • Define data models with Pydantic to describe what a bookmark contains.
  • Create a POST endpoint that accepts and stores new bookmarks.
  • Create a GET endpoint that returns all stored bookmarks.
Define your bookmark data models

Before your API can accept bookmarks, it needs to know what a bookmark looks like. Pydantic models define the shape of your data and automatically reject anything that doesn't match.

  • In main.py, find the import line at the top:
from fastapi import FastAPI
  • Add a new import line directly below it:
from pydantic import BaseModel
  • Now find the app = FastAPI() line. Add the following code below it (above the existing @app.get("/") endpoint):
class BookmarkCreate(BaseModel):
    # What the client sends when creating a bookmark
    url: str
    title: str
    tag: str | None = None


class Bookmark(BaseModel):
    # What the API returns (includes the auto-assigned id)
    id: int
    url: str
    title: str
    tag: str | None = None


# In-memory storage for bookmarks
bookmarks: dict[int, Bookmark] = {}
next_id: int = 1

What does this code do?

You defined two models. BookmarkCreate is the shape of data a client sends when creating a bookmark: a URL, a title, and an optional tag.

Bookmark is the shape of data your API returns. It has the same fields plus an id that your server assigns. FastAPI uses these models to validate every request automatically.

The bookmarks dictionary stores your data in memory, and next_id keeps track of what ID to assign next.

  • Save main.py.
  • Check the terminal where your dev server is running. You should see a message indicating the server detected changes and reloaded successfully.

Seeing an error in the terminal?

  • Check that from pydantic import BaseModel is on its own line directly below from fastapi import FastAPI. A typo or indent error will cause a reload failure.
  • Make sure your model classes are placed below app = FastAPI() and above the existing @app.get("/") endpoint.

Help me fix a Pydantic import or model definition error in my FastAPI app.

Create the POST /bookmarks endpoint

With your data models defined, you can build an endpoint that accepts new bookmarks. The POST method is the standard HTTP method for creating new resources.

  • In main.py, add the following code below the existing root() endpoint:
@app.post("/bookmarks", status_code=201)
async def create_bookmark(bookmark: BookmarkCreate) -> Bookmark:
    global next_id
    # Create a new Bookmark with the next available ID
    new_bookmark = Bookmark(id=next_id, **bookmark.model_dump())
    bookmarks[next_id] = new_bookmark
    next_id += 1
    return new_bookmark

What does this code do?

  • status_code=201 tells FastAPI to return a "201 Created" response instead of the default 200. This is the proper HTTP status code for creating a resource.
  • bookmark.model_dump() converts the incoming Pydantic model to a dictionary. The ** unpacks it so you can add the id field when creating the full Bookmark object.
  • FastAPI automatically reads the request body, validates it against BookmarkCreate, and passes the result as the bookmark parameter. If a client sends invalid data, FastAPI returns a 422 error before your code even runs.
  • Save main.py.
  • Switch back to your browser and refresh the Swagger UI at http://127.0.0.1:8000/docs.
  • You should see a new green POST /bookmarks endpoint listed alongside your original GET endpoint.
  • Click the POST /bookmarks row to expand it, then click Try it out.
  • Replace the example JSON in the request body with:
{
  "url": "https://fastapi.tiangolo.com",
  "title": "FastAPI Docs",
  "tag": "learning"
}
  • Click Execute.
  • In the response section below, you should see a 201 status code and your bookmark returned with an auto-assigned id of 1.

Not seeing the POST endpoint?

  • Make sure you saved main.py after adding the new function.
  • Hard-refresh the docs page with Cmd+Shift+R (macOS) or Ctrl+Shift+R (Windows).
  • Check the terminal for error messages. A syntax error in your code will prevent the server from reloading.

Help me fix my POST endpoint in FastAPI.

Create the GET /bookmarks endpoint and test the full flow

You can create bookmarks, but you have no way to see all of them at once. A GET endpoint that returns the full list completes the read side of your API.

  • In main.py, add the following code below the create_bookmark function:
@app.get("/bookmarks")
async def list_bookmarks() -> list[Bookmark]:
    # Return all bookmarks as a list
    return list(bookmarks.values())

What does this code do?

The -> list[Bookmark] return type hint tells FastAPI what shape the response will be. FastAPI uses this to generate accurate documentation and validate the response automatically.

bookmarks.values() returns all the stored bookmarks from your dictionary. Wrapping it in list() converts it from a dictionary view to a plain list that FastAPI can serialize to JSON.

  • Save main.py.
  • Refresh the Swagger UI in your browser.

Before you test, think about this: if you call GET /bookmarks right now, do you expect to see the bookmark you created earlier, or an empty list?

Why did the server restart clear your data?

When you saved the file, the dev server reloaded, which restarted your Python process. Since your bookmarks are stored in memory (a plain dictionary), they get wiped on every restart. This is expected for now.

  • Expand POST /bookmarks, click Try it out, and create a bookmark with this body:
{
  "url": "https://docs.python.org",
  "title": "Python Docs",
  "tag": "learning"
}
  • Click Execute. Confirm you see a 201 response with id of 1.
  • Create a second bookmark:
{
  "url": "https://github.com",
  "title": "GitHub",
  "tag": "tools"
}
  • Click Execute. This one should return with id of 2.
  • Now expand GET /bookmarks, click Try it out, then click Execute.
  • You should see both bookmarks returned as a JSON list, each with their assigned IDs, URLs, titles, and tags.

Getting an empty list or error?

  • If you see an empty list [], the server likely reloaded between your POST and GET requests. Create the bookmarks again via POST before calling GET.
  • If you see an error, check that your list_bookmarks function is placed below the create_bookmark function in main.py.

Help me fix my GET /bookmarks endpoint.

✔️ Awesome, I've got everything!

Great. Double check you've saved main.py and your server is still running in the terminal.

ⓧ I'd like to double check the full code

Your main.py should now contain these sections in order. Scroll up and confirm each is present:

  • Two import lines: from fastapi import FastAPI and from pydantic import BaseModel.
  • app = FastAPI() on its own line.
  • The BookmarkCreate class with fields url, title, and tag.
  • The Bookmark class with fields id, url, title, and tag.
  • The bookmarks dictionary and next_id counter.
  • The root() endpoint at /.
  • The create_bookmark() endpoint at POST /bookmarks.
  • The list_bookmarks() endpoint at GET /bookmarks.

You've built the "Create" and "Read" halves of a CRUD API. Next up, you'll add the ability to retrieve a single bookmark by its ID.

Retrieve a Single Bookmark

You can now create bookmarks and list them all in one call. That covers the "collection" side of your API.

Most of the time, clients need to fetch one specific resource by its ID. Think of tapping a single bookmark in a list to see its details. FastAPI makes this easy with path parameters, where part of the URL becomes a variable your function can use.

In this step, get ready to:
  • Add a path parameter endpoint that retrieves a single bookmark by ID.
  • Discover what happens when a client requests a bookmark that does not exist.
Add a GET endpoint for a single bookmark

A path parameter lets you capture a value directly from the URL. When someone requests /bookmarks/3, FastAPI extracts the 3 and passes it to your function as an argument.

  • In main.py, add the following endpoint at the bottom of your file, below the list_bookmarks function:
# Retrieve a single bookmark by its ID from the URL path
@app.get("/bookmarks/{bookmark_id}")
async def get_bookmark(bookmark_id: int) -> Bookmark:
    return bookmarks[bookmark_id]

What does this code do?

  • {bookmark_id} in the URL path tells FastAPI to capture that segment as a variable. The bookmark_id: int type hint makes FastAPI automatically convert the URL segment to an integer.
  • bookmarks[bookmark_id] looks up the bookmark directly in your dictionary using that ID as the key.
  • Save main.py.
  • The dev server auto-reloads. Switch back to the Swagger UI at http://127.0.0.1:8000/docs and refresh the page.
  • You should see a new GET /bookmarks/{bookmark_id} endpoint listed alongside your existing endpoints.

Don't see the new endpoint?

  • Make sure you saved main.py. The dev server only reloads when the file is saved.
  • Check your terminal for any red error text. A syntax error (like a missing colon or wrong indentation) prevents the server from reloading.
  • Confirm the new function is outside and below the list_bookmarks function, not nested inside it.

Why isn't my new endpoint showing up in the docs?

✔️ Awesome, I've got everything!

Great. Make sure you have saved main.py and confirmed the new endpoint appears in your Swagger UI.

ⓧ I'd like to double check the full code

Your main.py should now contain, in order:

  • Imports (FastAPI, BaseModel).
  • The BookmarkCreate and Bookmark Pydantic models.
  • The bookmarks dict and next_id counter.
  • The root endpoint (GET /).
  • The create_bookmark endpoint (POST /bookmarks).
  • The list_bookmarks endpoint (GET /bookmarks).
  • The new get_bookmark endpoint (GET /bookmarks/{bookmark_id}) at the very bottom.

Scroll up and confirm each section is present in your file.

Test with a valid bookmark ID

The server reload cleared your in-memory bookmarks, so you need to create one fresh before testing the new endpoint.

  • In the Swagger UI, expand the POST /bookmarks endpoint and click Try it out.
  • Submit a bookmark with any URL and title (for example, https://nextwork.org and NextWork).
  • Click Execute. You should see a 201 response with "id": 1 in the response body.
  • Now expand the GET /bookmarks/{bookmark_id} endpoint and click Try it out.
  • Enter 1 in the bookmark_id field and click Execute.

You should see a 200 response with your bookmark returned, including the URL, title, and ID.

Test with a non-existent ID

Your endpoint works when the bookmark exists. But what happens when a client asks for a bookmark that was never created?

Before you try this, predict: what do you think will happen if you request bookmark ID 999?

  • In the same GET /bookmarks/{bookmark_id} endpoint, change the bookmark_id value to 999 and click Execute.

You get a 500 Internal Server Error. Check your terminal where the dev server is running. You will see a KeyError traceback.

Why did this crash?

Your code uses bookmarks[bookmark_id] to look up the bookmark. When the key does not exist in a Python dictionary, it raises a KeyError. FastAPI has no idea what to do with that unhandled exception, so it returns a generic 500 error to the client.

This is a real problem. A 500 error tells the client nothing useful. It does not say "that bookmark doesn't exist." The next step fixes this by teaching you proper error handling with meaningful HTTP status codes.

Your API works for bookmarks that exist, but crashes ungracefully for ones that do not. Next up, you will fix this with proper error handling and complete the full set of CRUD operations.

Handle Errors and Complete CRUD

In the last step, you saw your API crash with an ugly 500 Internal Server Error when you requested a bookmark that doesn't exist. That's a problem. A real API should tell the client exactly what went wrong with a clear, helpful response.

FastAPI provides HTTPException to return proper HTTP status codes like 404 (Not Found) instead of letting your server crash. You'll fix the error from the previous step, then add Update and Delete endpoints to complete the full CRUD pattern.

In this step, get ready to:
  • Fix the 500 error with proper 404 handling using HTTPException.
  • Add a PUT endpoint to update existing bookmarks.
  • Add a DELETE endpoint to remove bookmarks entirely.
Import HTTPException and fix the get endpoint

The fix requires two changes: importing HTTPException from fastapi, and adding a check before you access the dictionary.

  • In main.py, find the import line at the very top of your file:
from fastapi import FastAPI
  • Replace it with this updated import that adds HTTPException:
from fastapi import FastAPI, HTTPException
  • Now scroll down to the get_bookmark function (the last function in your file). Find this code:
@app.get("/bookmarks/{bookmark_id}")
async def get_bookmark(bookmark_id: int) -> Bookmark:
    return bookmarks[bookmark_id]
  • Replace that entire function with this version that checks if the bookmark exists before trying to access it:
@app.get("/bookmarks/{bookmark_id}")
async def get_bookmark(bookmark_id: int) -> Bookmark:
    # Check if the bookmark exists before accessing it
    if bookmark_id not in bookmarks:
        raise HTTPException(status_code=404, detail="Bookmark not found")
    return bookmarks[bookmark_id]

What does this code do?

Instead of blindly accessing the dictionary (which crashes if the key doesn't exist), you first check whether bookmark_id is in the bookmarks dictionary.

If it's not there, raise HTTPException(status_code=404, detail="Bookmark not found") immediately stops the function and sends back a clean JSON response with the 404 status code and your custom message.

  • Save main.py.
  • The dev server auto-reloads your changes.

Before you test this, think back to the previous step. What do you expect to see now when you request bookmark ID 999?

  • Return to the Swagger UI at http://127.0.0.1:8000/docs in your browser.
  • Expand the GET /bookmarks/{bookmark_id} endpoint.
  • Click Try it out.
  • Enter 999 in the bookmark_id field.
  • Click Execute.

You now see a clean 404 response with the body {"detail": "Bookmark not found"} instead of the ugly 500 crash from before.

Still seeing a 500 error?

  • Make sure you imported HTTPException in the first line. It should read from fastapi import FastAPI, HTTPException.
  • Check that your if statement uses not in (two words) and references bookmarks (the dictionary), not bookmark.
  • Confirm you saved main.py and the terminal shows the server reloaded.

Why isn't my 404 error handling working in FastAPI?

Add the PUT endpoint for updating bookmarks

Now that your API handles missing bookmarks gracefully, you can add the ability to update existing ones. A PUT endpoint accepts both a path parameter (which bookmark to update) and a request body (the new data).

  • In main.py, add the following function below your get_bookmark function:
@app.put("/bookmarks/{bookmark_id}")
async def update_bookmark(bookmark_id: int, bookmark: BookmarkCreate) -> Bookmark:
    # Return 404 if bookmark doesn't exist
    if bookmark_id not in bookmarks:
        raise HTTPException(status_code=404, detail="Bookmark not found")
    # Replace the bookmark with updated data
    updated = Bookmark(id=bookmark_id, **bookmark.model_dump())
    bookmarks[bookmark_id] = updated
    return updated

What does this code do?

  • The @app.put decorator tells FastAPI this endpoint handles PUT requests for updating resources.
  • FastAPI automatically takes bookmark_id from the URL path and bookmark from the request body (since it's a Pydantic model).
  • The same 404 check prevents updating a bookmark that doesn't exist.
  • A new Bookmark object is created with the original ID but the updated fields, then stored back in the dictionary.
  • Save main.py.
  • In Swagger UI, first create a bookmark using the POST /bookmarks endpoint (enter any URL and title).
  • Expand the new PUT /bookmarks/{bookmark_id} endpoint and click Try it out.
  • Enter 1 for the bookmark_id and provide a new title and URL in the request body.
  • Click Execute.

You should see a 200 response with your bookmark returned containing the updated fields but the same ID.

Seeing a validation error?

  • Make sure your request body JSON includes at least url and title fields. The tag field is optional.
  • Check that the bookmark_id you entered matches one you already created with POST.

Why is my PUT endpoint returning an error?

Add the DELETE endpoint and test the full flow

The final piece of CRUD is Delete. Once you add this, your API covers every operation a backend developer needs.

  • In main.py, add the following function below your update_bookmark function:
@app.delete("/bookmarks/{bookmark_id}")
async def delete_bookmark(bookmark_id: int):
    # Return 404 if bookmark doesn't exist
    if bookmark_id not in bookmarks:
        raise HTTPException(status_code=404, detail="Bookmark not found")
    # Remove the bookmark from storage
    del bookmarks[bookmark_id]
    return {"message": "Bookmark deleted"}

What does this code do?

  • The @app.delete decorator handles HTTP DELETE requests.
  • The same 404 guard prevents deleting something that doesn't exist.
  • del bookmarks[bookmark_id] removes the entry from the dictionary permanently.
  • A simple confirmation message is returned so the client knows the delete succeeded.
  • Save main.py.

Now test the complete CRUD flow end-to-end. Before you start, predict what will happen if you try to GET a bookmark after you've deleted it.

  • In Swagger UI, create a new bookmark using POST /bookmarks. Note the id in the response.
  • Update that bookmark using PUT /bookmarks/{bookmark_id} with new values.
  • Verify the update persisted by calling GET /bookmarks/{bookmark_id} with the same ID.
  • Delete the bookmark using DELETE /bookmarks/{bookmark_id}.
  • Try to GET that same bookmark ID again.

The final GET returns a 404 with {"detail": "Bookmark not found"} because the bookmark no longer exists. Your error handling works consistently across all endpoints.

DELETE returning a 404 unexpectedly?

  • Remember that the in-memory storage resets every time the dev server reloads. If the server reloaded between creating and deleting, your bookmarks are gone.
  • Create a fresh bookmark with POST first, then use the ID from that response for your DELETE.

Why is my bookmark not found when I try to delete it?

✔️ Awesome, I've got everything!

Great. Double check you've saved main.py and all five CRUD endpoints are working in Swagger UI.

ⓧ I'd like to double check the full code

Your main.py should now contain these sections, in order. Scroll up through your file and confirm each is present:

  • The import line: from fastapi import FastAPI, HTTPException.
  • The from pydantic import BaseModel import.
  • The app = FastAPI() line.
  • The BookmarkCreate and Bookmark model classes.
  • The bookmarks dictionary and next_id counter.
  • The root function (GET /).
  • The create_bookmark function (POST /bookmarks).
  • The list_bookmarks function (GET /bookmarks).
  • The get_bookmark function (GET /bookmarks/{bookmark_id}) with the if bookmark_id not in bookmarks check.
  • The update_bookmark function (PUT /bookmarks/{bookmark_id}).
  • The delete_bookmark function (DELETE /bookmarks/{bookmark_id}).

Your API now handles all four CRUD operations with proper error handling. Next up, you'll extend this with a Secret Mission that adds filtering so you can search bookmarks by tag.

Secret mission

Filter Bookmarks by Tag

Your API can create, read, update, and delete bookmarks. But what if you have dozens of bookmarks and only want to see the ones tagged "work"? Add tag-based filtering so users can search bookmarks by category.

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely locally, so there are no ongoing costs. Decide whether to keep your resources running, pause them to come back later, or delete them entirely.

Resources you used:

  • The FastAPI development server (running in your terminal).
  • The bookmark-api project folder on your Desktop.
  • The uv package manager (installed to ~/.local/bin/).

Keep everything running

No action needed. Choose this if you are still actively building or experimenting with your API.

  • Your FastAPI dev server stays running in the terminal. You can keep sending requests through the Swagger UI at http://127.0.0.1:8000/docs.
  • The uv tool remains installed and available for future Python projects.
  • Your bookmark-api folder stays on your Desktop.

Pause - I'll come back to this later

Stop the running server to free up your terminal, but keep all files so you can pick up where you left off.

  • Switch to the terminal where the FastAPI dev server is running.
  • Press Ctrl+C to stop the server.
  • Your bookmark-api folder and all code remain on your Desktop. To resume later, open a terminal in that folder and run uv run fastapi dev main.py.

Delete - I don't want to use this again

Remove all project files and optionally uninstall uv if you do not plan to use it for other Python projects.

  • Switch to the terminal where the FastAPI dev server is running and press Ctrl+C to stop it.
  • Delete the project folder by running:

macOS

cd ~/Desktop
rm -rf bookmark-api

Windows

cd ~\Desktop
rmdir /s /q bookmark-api
  • To uninstall uv, clean its stored data and remove the binaries by running:

macOS

uv cache clean
rm -r "$(uv python dir)"
rm -r "$(uv tool dir)"
rm ~/.local/bin/uv ~/.local/bin/uvx

Windows

uv cache clean
rm -r "$(uv python dir)"
rm -r "$(uv tool dir)"
rm $HOME\.local\bin\uv.exe
rm $HOME\.local\bin\uvx.exe
rm $HOME\.local\bin\uvw.exe

Nice Work!

Nice Work!

You just built a fully functional REST API from scratch using FastAPI. You can demo it to anyone by opening the interactive docs in a browser and running live requests.

Here's what you learned:

  • Built a complete REST API with full CRUD operations (Create, Read, Update, Delete) using FastAPI and tested every endpoint through auto-generated Swagger UI documentation.
  • Defined Pydantic data models that automatically validate incoming requests and reject bad data before your code even runs.
  • Used proper HTTP status codes and HTTPException to return clear error responses instead of crashing with unhelpful 500 errors.
  • Secret Mission: Added tag-based filtering using query parameters so users can search bookmarks by category.

Ready to quiz yourself?