Full-stack FastAPI Tutorial 1: Project Setup & Tooling

· 15 min
·
fastapipythontutorial

This is the first tutorial in a series where we’ll build a full-stack todo application with FastAPI and Svelte (and with other modern frontend frameworks such as React later).

In this tutorial, we’ll set up a FastAPI project with modern tooling using uv (a fast Python package manager), configure Pydantic Settings for environment management, set up Ruff for linting and formatting, and optimize VS Code workspace settings. In the next tutorial, we’ll implement the actual CRUD APIs for our todo application.

Building a production app? Check out FastSvelte - a production-ready FastAPI + SvelteKit starter with authentication, payments, and more built-in.

Prerequisites

Before we start, you’ll need:

If Python is already installed on your system, uv will detect and use it automatically. However, uv can also manage Python versions for you - it will install missing Python versions as needed, so you don’t even need Python pre-installed to get started.

  • Basic understanding of Python and REST APIs
  • A terminal and code editor (we’ll use VS Code in this tutorial)

Project Structure

First, we need to create a folder for the backend and another one for the frontend. For the first few tutorials, we’ll be working in the backend folder.

fullstack-fastapi-tutorial/
├── backend/                  # FastAPI (all backend tutorials)
├── frontend/                 # SvelteKit SPA
└── README.md

Navigate to the backend folder and initialize a new uv project:

cd backend
uv init --app

This initializes a uv project with an application structure. Check the generated .python-version and pyproject.toml files to see what uv created for you. Now run uv run main.py inside the backend folder:

uv run main.py

You should see output like this:

Using CPython 3.14.0
Creating virtual environment at: .venv
Hello from backend!

The virtual environment (.venv) was created automatically since this is the first run. If you run uv run main.py again, you’ll only see “Hello from backend!” on subsequent runs.

Installing FastAPI

Now let’s add FastAPI as a dependency:

uv add "fastapi[standard]"

The [standard] extra includes recommended dependencies like uvicorn (ASGI server), pydantic-settings, and python-multipart for file uploads. This gives you everything you need for most FastAPI applications.

Now let’s write a minimal FastAPI application. Replace the code in main.py with this:

# src/backend/main.py
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
    return {"message": "Hello World"}
uv run fastapi dev main.py

You should see logs similar to:

  FastAPI   Starting development server 🚀

             Searching for package file structure from directories with __init__.py files
             Importing from /home/harun/workspace/fullstack-fastapi-tutorial/backend

    module   🐍 main.py

      code   Importing the FastAPI app object from the module with the following code:

             from main import app

       app   Using import string: main:app

    server   Server started at http://127.0.0.1:8000
    server   Documentation at http://127.0.0.1:8000/docs

       tip   Running in development mode, for production use: fastapi run

             Logs:

      INFO   Will watch for changes in these directories: ['.../fullstack-fastapi-tutorial/backend']
      INFO   Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
      INFO   Started reloader process [41080] using WatchFiles
      INFO   Started server process [41094]
      INFO   Waiting for application startup.
      INFO   Application startup complete.
      INFO   127.0.0.1:58782 - "GET / HTTP/1.1" 200
      INFO   127.0.0.1:58782 - "GET /favicon.ico HTTP/1.1" 404

Now if you go to http://127.0.0.1:8000 in your browser, you should see the JSON response from our API.

{ "message": "Hello World" }

Visit http://127.0.0.1:8000/docs to see the auto-generated Swagger/OpenAPI documentation.

Also notice the “Will watch for changes in these directories” line in the logs above. This means any changes you make to your Python files will automatically restart the server. This is called “hot-reload”.

Testing Your API with HTTP Files

Instead of using tools like Postman or cURL commands, we’ll use .http files to test our API. These files let you define and execute HTTP requests directly in VS Code, live in version control, serve as executable documentation, and work great with AI coding assistants.

First, install the REST Client extension for VS Code.

Create a new http/ folder in your backend directory and add a test.http file:

backend/
├── http/
│   └── test.http    # API test requests
├── main.py
├── settings.py
└── ...

Add the following to http/test.http:

### Get root endpoint
GET http://localhost:8000/

In VS Code, you’ll see a “Send Request” link appear above the request. Click it to execute the request, and you’ll see the response in a split pane:

HTTP/1.1 200 OK
content-length: 65
content-type: application/json

{
  "message": "Hello World! Welcome to FastAPI Backend."
}

The ### comment syntax lets you define multiple requests in the same file. As we build our todo API, we’ll add more requests here.

Environment Configuration

Let’s set up project settings using a .env file with Pydantic Settings for type-safe configuration management.

Create a settings.py file:

from pydantic_settings import BaseSettings, SettingsConfigDict


class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8")
    project_name: str = "FastAPI Backend" # Default/fallback value


settings = Settings()

And update the main.py to use this setting.

from fastapi import FastAPI

from settings import settings

app = FastAPI()


@app.get("/")
def read_root():
    return {"message": f"Hello World! Welcome to {settings.project_name}."}

The model_config tells Pydantic Settings where to read configuration from. In our case, it’s the .env file.

Creating Environment Files

Create a .env.example file:

PROJECT_NAME=FastAPI Backend  # Name of the project, used in the welcome message.

While .env is gitignored, .env.example is committed to the repository. Never put secrets in .env.example - its purpose is to show other developers what environment variables are needed and provide safe default values.

Copy .env.example to .env - this will hold your actual configuration:

PROJECT_NAME=FastAPI Backend

Now go to http://127.0.0.1:8000 and you should see the config in the message.

{ "message": "Hello World! Welcome to TurtleDev FastAPI Tutorial." }

Pydantic Settings will also read environment variables directly from your shell. For example, running export PROJECT_NAME="My Project" will set settings.project_name even without a .env file. This is typically how configuration works in production environments like staging or prod.

Setting Up Code Quality Tools

Let’s add Ruff for linting and formatting. Ruff is an extremely fast Python linter and formatter written in Rust that replaces tools like Black, isort, flake8, and more.

Add Ruff as a dev dependency to the project:

uv add --dev ruff

Verify that dev dependencies have a separate section in pyproject.toml:

[dependency-groups]
dev = [
    "ruff>=0.15.8",
]

Configuring Ruff

You can now run ruff check . to lint your code and ruff format . to format it. Let’s add project-specific linting and formatting rules to pyproject.toml:

[tool.ruff]
line-length = 100
extend-select = ["I"] # Organize imports with Ruff's "I" rules.

[tool.ruff.format]
# Like Black, use double quotes for strings.
quote-style = "double"

VS Code Integration

Running ruff check . --fix will automatically fix linting and formatting errors. But in VS Code, we can make this even easier. Install the Ruff VS Code extension and add the following workspace settings to .vscode/settings.json:

{
	"[python]": {
		"editor.formatOnSave": true,
		"editor.codeActionsOnSave": {
			"source.fixAll": "explicit",
			"source.organizeImports": "explicit"
		},
		"editor.defaultFormatter": "charliermarsh.ruff"
	}
}

While the VS Code extension is convenient, you can always run ruff check . and ruff format . from the terminal. This is useful for developers not using VS Code and for running Ruff in CI/CD pipelines (we’ll set up GitHub Actions in a future tutorial).

Another important setting to add is python.defaultInterpreterPath. This ensures VS Code recognizes the correct virtual environment and Python interpreter on any machine:

{
	"[python]": {
		"editor.formatOnSave": true,
		"editor.codeActionsOnSave": {
			"source.fixAll": "explicit",
			"source.organizeImports": "explicit"
		},
		"editor.defaultFormatter": "charliermarsh.ruff"
	},
	"python.defaultInterpreterPath": "${workspaceFolder}/backend/.venv/bin/python"
}

See the Ruff VS Code documentation for more configuration options.

Since Ruff handles both linting and formatting, you can disable or uninstall other Python linter/formatter extensions (like pylint, autopep8, Black, etc.) to avoid conflicts and redundancy.

Managing .gitignore Files

Each folder will have its own .gitignore file for its specific needs. For example, the backend folder should have a .gitignore that ignores .venv/, .env, __pycache__/, and *.pyc files. We can also add another .gitignore to the root directory if needed.

Next Steps

Building a production app? Check out FastSvelte - a production-ready FastAPI + SvelteKit starter with authentication, payments, and more built-in.

We’ve completed the initial project setup! In the next tutorial, we’ll implement the actual CRUD APIs for our todo application.

You can access the final code for this tutorial on GitHub: fullstack-fastapi-tutorial/tree/01-backend-setup

Smooth Coding!

Comments

© 2026 Harun. All rights reserved.

Software development blog - quality technical content and insights