Skip to content

Chapter 3.4: CRUD Operations for Spacecraft

Time to learn: 1 hour


1. CRUD: The Full Lifecycle of Space Mission Management

So far, we have only been reading data (Read). But a real Mission Control Center must be able to do everything:

  • Create: Launch a new satellite into orbit.
  • Read: Request the status of an existing spacecraft.
  • Update: Adjust an orbit or update software.
  • Delete: De-orbit an old satellite.

These four operations—CRUD—form the basis of most APIs. In this chapter, we will implement the full cycle for managing our fleet.


2. Create: Launching a New Ship (POST)

To create a new spacecraft, we will use the POST method. The data for the new ship will be sent in the request body in JSON format.

Step 1: Create a new Pydantic model for incoming data Why a new model? Because when creating a ship, we don't know its id—it must be assigned by the server. We also need to update our main Spaceship model to include the id for responses.

Update your models in main.py:

# main.py
from pydantic import BaseModel, Field

# This is our main model, now including an ID.
# It will be used for RESPONSE data.
class Spaceship(BaseModel):
    id: int
    name: str = Field(..., min_length=3, max_length=50)
    type: str
    launch_year: int = Field(..., gt=1950)
    status: str

# This model is for INCOMING data when creating a ship.
# It has no ID.
class SpaceshipCreate(BaseModel):
    name: str = Field(..., min_length=3, max_length=50)
    type: str
    launch_year: int = Field(..., gt=1950)
    status: str

Step 2: Implement the POST /spaceships endpoint

# main.py
import random # Add this import at the top of the file
from fastapi import FastAPI, HTTPException, Response, status # Update this import

# ... other code ...

@app.post("/spaceships", response_model=Spaceship, status_code=status.HTTP_201_CREATED)
def create_spaceship(ship: SpaceshipCreate):
    """
    Adds a new spacecraft to the registry.
    """
    # Generate a new unique ID for the ship
    new_id = max(db_spaceships.keys() or [0]) + 1

    # Create a spaceship object conforming to the full Spaceship model
    # by combining the generated ID with the incoming data.
    new_ship_data = ship.dict()
    new_ship_data["id"] = new_id
    new_ship = Spaceship(**new_ship_data)

    # Save to our "database"
    db_spaceships[new_id] = new_ship.dict()

    return new_ship
Breakdown:

  • @app.post(...): We use the decorator for POST requests.
  • status_code=201: We specify that on successful creation, the status 201 Created should be returned.
  • ship: SpaceshipCreate: This is the magic! FastAPI will automatically take the request body (JSON), validate it against the SpaceshipCreate model, and pass it into the function as a ship object.
  • new_id = ...: Simple logic to generate a new ID.
  • **ship.dict(): We "unpack" the data from the received ship model.
  • response_model=Spaceship: The response will conform to the full model, including the id.

3. Update: Course Correction (PUT)

The PUT method is used for a full update of an existing resource.

Implement the PUT /spaceships/{ship_id} endpoint:

# main.py

# ... other code ...

@app.put("/spaceships/{ship_id}", response_model=Spaceship)
def update_spaceship(ship_id: int, ship_update: SpaceshipCreate):
    """
    Completely updates the data for a spacecraft.
    """
    if ship_id not in db_spaceships:
        raise HTTPException(status_code=404, detail="Spacecraft not found")

    updated_ship_data = ship_update.dict()
    updated_ship_data["id"] = ship_id
    updated_ship = Spaceship(**updated_ship_data)
    db_spaceships[ship_id] = updated_ship.dict()

    return updated_ship

  • ship_update: SpaceshipCreate: We again use our model to validate the incoming data.
  • HTTPException: If a ship with the given id is not found, we "raise" a standard FastAPI exception, which will be converted into a nice JSON response with a 404 code.

4. Delete: De-orbiting (DELETE)

The DELETE method is used to remove a resource. Typically, such an endpoint does not return a response body.

Implement the DELETE /spaceships/{ship_id} endpoint:

# main.py

# ... other code ...

@app.delete("/spaceships/{ship_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_spaceship(ship_id: int):
    """
    Removes a spacecraft from the registry.
    """
    if ship_id not in db_spaceships:
        raise HTTPException(status_code=404, detail="Spacecraft not found")

    del db_spaceships[ship_id]

    # Return an empty response with a 204 status
    return Response(status_code=status.HTTP_204_NO_CONTENT)

  • status_code=status.HTTP_204_NO_CONTENT: We explicitly specify the 204 No Content status.
  • del db_spaceships[ship_id]: We delete the entry from our dictionary.
  • return Response(...): We return an empty response, as the client does not need data about the deleted object.

5. Testing the Full Cycle in /docs

Your uvicorn should have reloaded.

  1. Open http://127.0.0.1:8000/docs. You now have a full set of CRUD operations!
  2. POST: Expand the POST /spaceships endpoint, click "Try it out," fill in the JSON body (e.g., create the "James Webb Telescope"), and click "Execute." You should get a 201 response with the new telescope's data.
  3. GET: Now execute GET /spaceships. Your new telescope should appear in the list.
  4. PUT: Use the new telescope's ID to update its data via PUT /spaceships/{ship_id}. For example, change its status.
  5. DELETE: Use the same ID to delete the telescope via DELETE /spaceships/{ship_id}. You should receive an empty response with a 204 status.
  6. Check: Execute GET /spaceships again to ensure the telescope has been removed from the list.

Review Quiz

1. Which HTTP method is used to create a new resource?

2. The standard success status code for a `DELETE` operation is:

3. How does FastAPI get data from a POST request body?

4. `raise HTTPException(status_code=404)` is used to:

5. Why did we create a separate `SpaceshipCreate` model for creating a resource (`POST`)?


🚀 Chapter Summary:

You have implemented the full CRUD cycle and transformed your API from a simple "information board" into a full-fledged Fleet Command Center!

  • Create: POST /spaceships to launch new craft.
  • Read: GET /spaceships and GET /spaceships/{id} to retrieve data.
  • Update: PUT /spaceships/{id} to update missions.
  • Delete: DELETE /spaceships/{id} to decommission craft.

Your fleet is under complete control! In the next chapter, we will see how FastAPI has automatically created a detailed "operating manual" for us—the interactive Swagger documentation.

📌 Checkpoint:

  • All 5 endpoints (GET (2), POST, PUT, DELETE) are visible and working in /docs.
  • You can successfully create, read, update, and delete a resource.
  • Requesting a non-existent ID returns a 404 error.

⚠️ If you have errors:

  • NameError: Check that you have imported HTTPException, Response, and status.
  • KeyError: You are likely trying to access an ID that has already been deleted.
  • Incorrect PUT or POST behavior: Make sure you are using the correct Pydantic model in the function argument (SpaceshipCreate).