Recommended resources
The freely available resources this lesson will direct you to.
Hello! Last lesson, you designed an incident API contract: GET /incidents lists incidents, while other routes will eventually create, retrieve, and update them. Today you’ll make the first part executable. You’ll create a FastAPI application, run it with an ASGI development server, and confirm that both its JSON response and generated documentation are available.
This is the first running API, not yet a complete incident service. We’ll keep the route deliberately small so you can see clearly what FastAPI provides and what you still need to implement.
Create the application
Watch these portions of Python FastAPI Tutorial (Part 1): Getting Started - Web App + REST API by Corey Schafer. The video uses a root route and its own project layout; focus on the application object, route decorator, and development server rather than copying that layout into your existing project.
Python FastAPI Tutorial (Part 1): Getting Started - Web App + REST API
Corey Schafer demonstrates the short path from installing FastAPI to serving a JSON response.
Watch installation choices for what the standard FastAPI installation includes. Then watch the first route for the application instance, @app.get, and JSON return value. Finish with development mode to see startup, automatic reload, and a browser check. We’ll use an explicit package entry point below instead of the video’s main.py command.
Keep the isolated environment and src-layout project you established earlier. Add FastAPI as a runtime dependency using your project’s existing dependency workflow. For a project managed with uv, the command is:
uv add "fastapi[standard]"
If your project uses pip and an already activated virtual environment, install it there and record the dependency using the project workflow you established earlier:
python -m pip install "fastapi[standard]"
The standard extra includes the FastAPI command-line tool and Uvicorn, the server used to run the app. You do not need a second environment or a new project directory.
Suppose your existing importable package is called incident_api. Add main.py inside that package:
src/
└── incident_api/
├── __init__.py
└── main.py
If your package has another name, use that name consistently in the paths and commands that follow. Put this in src/incident_api/main.py:
from fastapi import FastAPI
app = FastAPI()
@app.get("/incidents")
def list_incidents() -> list[dict[str, str | int]]:
return []
There are three separate roles here. FastAPI() creates the application object. @app.get("/incidents") registers a handler for the method and collection path chosen last lesson. When a matching request arrives, FastAPI calls list_incidents() and serializes its returned Python list as JSON. An empty list is an honest initial response: the collection route exists, but this lesson has not introduced storage or request models.
The handler uses ordinary def. FastAPI supports both synchronous and asynchronous handlers; choosing between them matters when we introduce I/O behavior later. Writing async def is not a prerequisite for running a FastAPI application on an ASGI server.
Tell the CLI what to import
With a src layout, it is better to identify the application explicitly than to rely on the CLI finding a top-level main.py. Read the entry-point portion of FastAPI’s First Steps before editing pyproject.toml.
FastAPI’s official guide shows how its CLI locates an application object. Read it to connect your package path to the object you just created.
In “Configure the app entrypoint in pyproject.toml,” read the entry-point examples and the import statement immediately following them. Focus on why backend.main:app identifies a module and an object; your package will use the same pattern with its own name.
Add this table to your existing pyproject.toml (or add the setting to an existing [tool.fastapi] table):
[tool.fastapi]
entrypoint = "incident_api.main:app"
The part before the colon is an importable Python module; the part after it is the object named app inside that module. In effect, the CLI must be able to perform:
from incident_api.main import app
This is why retaining the installable package from the earlier module matters. If you see ModuleNotFoundError, first check the package name, the environment running the command, and whether your existing project is installed as intended. Don’t work around the error by copying main.py to several directories.
An ASGI server is the process that accepts HTTP connections and invokes your application using the ASGI interface. FastAPI supplies the application; Uvicorn serves it. The fastapi CLI starts Uvicorn for you.
Start the development server
From your project directory, use the command appropriate to your dependency workflow:
uv run fastapi dev
Or, with the project’s virtual environment activated:
fastapi dev
Because you configured the entry point, neither command needs a file path. Look for startup output showing that incident_api.main:app was imported and that Uvicorn is serving at http://127.0.0.1:8000. The process stays running in this terminal; stop it with Ctrl+C when you are finished.
Open a second terminal or browser and visit http://127.0.0.1:8000/incidents. You should receive:
[]
For a view that includes the HTTP status and headers, use:
curl -i http://127.0.0.1:8000/incidents
Expect 200 OK, a JSON content type, and the [] body. This matches the collection behavior proposed last lesson: an existing collection may contain no incidents.
fastapi dev watches for source changes and reloads the application during development. To see that behavior, temporarily change the function’s return value to:
return [{"id": 42, "title": "Deployment failing", "status": "new"}]
Save the file, wait for the restart message, and request /incidents again. You should see the example object. This is hard-coded demonstration data, not a created or persisted incident; change the return value back to [] afterward. Reloading is convenient locally, but it also means module-level code may run again. This is another reason to avoid consequential import-time side effects.
The default address 127.0.0.1 is reachable from your own machine. Development mode’s automatic reload is not a production deployment strategy; server configuration and container delivery come much later in the course.
Inspect what FastAPI generated
FastAPI also exposes interactive documentation at http://127.0.0.1:8000/docs, alternative documentation at /redoc, and a machine-readable API description at /openapi.json. Read the corresponding parts of the official First Steps guide, then inspect all three in your running application.
FastAPI’s official guide connects a running route to its browser response, interactive documentation, and OpenAPI schema.
In “Check it,” “Interactive API docs,” and “Alternative API docs,” read the browser checks. Then, under “OpenAPI” and “What is OpenAPI for,” read the schema explanation. Compare the guide’s example route with the /incidents route in your own docs.
The screenshot below shows what an expanded operation can look like in Swagger UI. It depicts a different example API: GET /items/{item_id}, with path and query inputs. Your documentation should show GET /incidents instead. We’ll add typed inputs like those in the screenshot in the next lesson.

Open your /docs page and find GET /incidents. Its presence confirms that the decorator registered the operation. You can also use Try it out there to send a request, or inspect /openapi.json for the /incidents path. The documentation describes the application you actually registered; it does not create the other incident operations from last lesson’s proposed contract.
That distinction helps interpret two other responses. A request to GET /incidents/42 currently gets 404, because no item route has been registered yet—not because the app looked up incident 42 and failed to find it. A POST /incidents request is not an implemented create operation merely because GET /incidents exists. Route registration is the first step; request models, storage, and the promised status codes still require code.
Takeaways
You now have an importable FastAPI application served locally by Uvicorn through fastapi dev. A route decorator connects GET /incidents to a Python function, FastAPI converts its return value to JSON, and the registered operation appears automatically in the OpenAPI documentation. You also know how to identify the app explicitly from a src-layout project and verify that development reload works.
Next, you’ll add typed path and query parameters. That will turn the route map into requests that can identify one incident or refine a collection request.