Free Ebook cover FastAPI for Beginners: Build a Production-Ready REST API

FastAPI for Beginners: Build a Production-Ready REST API

New course

14 pages

FastAPI Project Setup and Developer Workflow

Capítulo 1

Estimated reading time: 5 minutes

+ Exercise

Goals of a Clean Setup

A good FastAPI setup makes local development fast (reload, clear entry point), keeps code easy to navigate (predictable folders), and supports growth (routers separated from business logic, tests from day one). In this chapter you will create a minimal but scalable project layout and run it with an ASGI server.

Create the Project and Virtual Environment

1) Create a folder and initialize a virtual environment

Create a new directory and a virtual environment inside it. The commands vary slightly by OS.

# create project folder
mkdir fastapi-project
cd fastapi-project

# macOS/Linux
python -m venv .venv
source .venv/bin/activate

# Windows (PowerShell)
py -m venv .venv
.\.venv\Scripts\Activate.ps1

Keep the virtual environment inside the project (commonly named .venv) so your editor can detect it easily. Add it to .gitignore so it is never committed.

2) Install core dependencies

Install FastAPI and an ASGI server for local development. You will use uvicorn with auto-reload so code changes restart the server automatically.

python -m pip install --upgrade pip
pip install fastapi uvicorn[standard]

uvicorn[standard] includes useful extras (like better reloading and performance dependencies) that are convenient in development.

Continue in our app.

You can listen to the audiobook with the screen off, receive a free certificate for this course, and also have access to 5,000 other free online courses.

Or continue reading below...
Download App

Download the app

3) Capture dependencies

For a simple beginner-friendly workflow, you can pin dependencies into requirements.txt.

pip freeze > requirements.txt

Whenever you add a new package, re-run the freeze command to keep the file updated.

Organize the Folder Structure

Start with a structure that is small today but won’t collapse when you add features. A common approach is to keep the FastAPI application code under app/ and split responsibilities by layer.

fastapi-project/
  app/
    __init__.py
    main.py
    routers/
      __init__.py
      health.py
    models/
      __init__.py
    schemas/
      __init__.py
    services/
      __init__.py
  tests/
    __init__.py
  .gitignore
  requirements.txt

What each folder is for

  • app/main.py: application entry point (creates FastAPI(), includes routers).
  • app/routers/: API layer (path operations, request/response handling, status codes). Routers should be thin.
  • app/schemas/: Pydantic models that define request and response shapes (DTOs). These are your API contracts.
  • app/models/: domain or persistence models (for example ORM models). Keep them separate from API schemas.
  • app/services/: business logic (use cases). Services should not depend on FastAPI objects like Request or Response.
  • tests/: automated tests. Keeping this from the start encourages a stable workflow.

Minimal Application Entry Point

Create app/main.py as the single place where the FastAPI app is instantiated and routers are registered. This keeps startup predictable and avoids circular imports as the project grows.

from fastapi import FastAPI

from app.routers.health import router as health_router

app = FastAPI(title="FastAPI Project")

app.include_router(health_router)

Notice that main.py does not contain endpoint logic. It only wires the application together.

Define a Router (API Layer)

Routers group endpoints by feature and can be mounted with prefixes and tags. Create app/routers/health.py:

from fastapi import APIRouter

router = APIRouter(tags=["health"])

@router.get("/health")
def health_check() -> dict:
    return {"status": "ok"}

This is intentionally small: it proves the project boots, routing works, and OpenAPI documentation is generated.

Structuring for Growth: Routers vs Services

As soon as endpoints do more than return static data, separate concerns:

  • Router responsibilities: parse inputs, call a service, return outputs (and HTTP status codes).
  • Service responsibilities: implement business rules and orchestration. Services should be testable without running the web server.

Even for a health check, you can see the pattern by adding a service function. Create app/services/health_service.py:

def get_health_status() -> dict:
    return {"status": "ok"}

Then update app/routers/health.py to call the service:

from fastapi import APIRouter

from app.services.health_service import get_health_status

router = APIRouter(tags=["health"])

@router.get("/health")
def health_check() -> dict:
    return get_health_status()

This separation pays off when you add validation, persistence, or external calls: routers remain thin, services remain reusable, and tests can target services directly.

Run the App with Uvicorn (Reload Enabled)

From the project root (where app/ lives), run:

uvicorn app.main:app --reload

How to read app.main:app:

  • app.main is the Python module path (app/main.py).
  • app is the variable inside that module that holds the FastAPI instance.

The --reload flag watches your files and restarts the server when you edit code. Use it for local development; in production you typically run without reload.

Verify the Health Check Endpoint

With the server running, open a browser or use curl:

curl http://127.0.0.1:8000/health

Expected response:

{"status":"ok"}

OpenAPI Schema and Interactive Docs

FastAPI generates an OpenAPI schema automatically from your routes and Pydantic schemas.

  • Raw OpenAPI JSON: http://127.0.0.1:8000/openapi.json
  • Swagger UI (interactive docs): http://127.0.0.1:8000/docs
  • ReDoc (alternative docs): http://127.0.0.1:8000/redoc

After adding the /health route, you should see it listed under the health tag in the docs, and the OpenAPI schema will include a GET /health path entry.

Now answer the exercise about the content:

In a scalable FastAPI project, what is the main reason to keep routers thin and move business logic into services?

You are right! Congratulations, now go to the next page

You missed! Try again.

Routers should handle inputs/outputs and HTTP concerns, while services implement business logic. This keeps code reusable and allows testing services directly without starting the server.

Next chapter

Routing and REST Endpoints in FastAPI

Arrow Right Icon
Download the app to earn free Certification and listen to the courses in the background, even with the screen off.