The Full Stack FastAPI Template is a production-ready starter kit for building web applications with a Python backend and a React frontend. You clone (or generate) it once, and you get a complete, working application: a JWT-authenticated REST API, a database with migrations, a single-page admin dashboard, email-based password recovery, automated tests, and a Docker Compose setup that runs everything together.
Rather than wiring FastAPI, SQLModel, PostgreSQL, React, and Traefik together yourself, you start from a stack that is already integrated, tested in CI, and ready to customize into your own product.
Note
This is a template, not a library you install. The goal is to give you a running codebase to build on top of — you own and edit every file after you generate your project.
1. Quick Start
Get the full application running locally in a few minutes. You need Docker (Docker Engine with Compose) installed.
# 1. Get the code (use it as-is for now)
git clone https://github.com/fastapi/full-stack-fastapi-template.git my-app
cd my-app
# 2. Start the whole stack (backend, frontend, database, proxy, mail catcher)
docker compose watch
The first start takes a minute while the backend waits for the database and seeds the first admin user. Once it is up, open these URLs in your browser:
| Service | URL | What it is |
|---|---|---|
| Frontend dashboard | http://localhost:5173 | The React single-page app |
| Backend API | http://localhost:8000 | The FastAPI JSON API |
| Interactive API docs (Swagger UI) | http://localhost:8000/docs | Try every endpoint in the browser |
| Alternative API docs (ReDoc) | http://localhost:8000/redoc | Reference-style API docs |
| Adminer (database UI) | http://localhost:8080 | Browse and edit the database |
| Traefik dashboard | http://localhost:8090 | See how the proxy routes traffic |
| Mailcatcher | http://localhost:1080 | Read emails the app "sends" locally |
Log in to the dashboard with the default superuser credentials that ship in the
.env file:
Email: [email removed]
Password: changethis
Caution
The default
.envships with placeholder secrets set tochangethis(SECRET_KEY,POSTGRES_PASSWORD,FIRST_SUPERUSER_PASSWORD). They are fine for a quick local spin, but you must change them before deploying anywhere. See Generating secure secrets.
Tip
docker compose watchkeeps your running containers in sync with your source code — when you edit a backend or frontend file, the change is synced into the container automatically. To follow logs while it runs, open a second terminal and rundocker compose logs(ordocker compose logs backendfor one service).
2. Starting Your Own Project
You have two ways to turn this template into your own project: clone it directly (simplest) or generate it with Copier (customizes names and secrets for you as it copies). Both leave you with a normal Git repository you fully own.
Clone or fork (simplest)
You can just fork or clone the repository and use it as-is. To start a private project from it (GitHub does not let you make a fork private), clone it under your own name and re-point the remote:
# Clone into a directory named for your project
git clone git@github.com:fastapi/full-stack-fastapi-template.git my-full-stack
cd my-full-stack
# Point "origin" at your own (new, empty) repository
git remote set-url origin git@github.com:your-org/my-full-stack.git
# Keep the template as "upstream" so you can pull future updates
git remote add upstream git@github.com:fastapi/full-stack-fastapi-template.git
# Push to your repository
git push -u origin master
Generate with Copier (customized)
Copier copies all the files, asks you a few
configuration questions, and fills your .env files with your answers (including
auto-generating secure secrets).
# Install copier (pipx recommended)
pipx install copier
# Generate a new project into ./my-awesome-project
copier copy https://github.com/fastapi/full-stack-fastapi-template my-awesome-project --trust
If you have pipx you can skip the install and run it directly:
pipx run copier copy https://github.com/fastapi/full-stack-fastapi-template my-awesome-project --trust
Important
The
--trustflag is required. The template runs a post-generation script (.copier/update_dotenv.py) that writes your answers into the.envfiles; Copier will not execute it without--trust.
2.1 Copier input variables
When you generate with Copier, you are asked for the following. You can change any of
them later by editing the .env file, so don't overthink them now.
| Variable | Default | Purpose |
|---|---|---|
project_name |
FastAPI Project |
Project name shown to API users (in API docs and emails) |
stack_name |
fastapi-project |
Name used for Docker Compose labels — no spaces or periods |
secret_key |
changethis |
Secret key used to sign tokens — generate a real one |
first_superuser |
[email removed] |
Email of the first admin user |
first_superuser_password |
changethis |
Password of the first admin user — change it |
smtp_host |
(empty) | SMTP server host for sending email (optional, set later) |
smtp_user |
(empty) | SMTP username (optional) |
smtp_password |
(empty) | SMTP password (optional) |
emails_from_email |
[email removed] |
"From" address for outgoing email |
postgres_password |
changethis |
PostgreSQL password — generate a real one |
sentry_dsn |
(empty) | Sentry DSN for error tracking (optional) |
2.2 Pulling updates from the template later
Because you added the template as upstream, you can pull its later improvements into
your project:
# Download the latest template changes without committing them
git pull --no-commit upstream master
# Resolve any conflicts in your editor, then finish the merge
git merge --continue
Tip
--no-commitlets you review everything the template changed before it lands in your history — useful because you will have edited many of the same files.
3. What You Get — The Application Tour
Once you log in, the template gives you a working dashboard built around two example resources: users (people who can log in) and items (a simple owned resource you replace with your own data model). Everything here is real, working code you can study and adapt.
3.1 Authentication
The app ships with a complete authentication flow, all backed by JWT access tokens:
- Log in (
/login) — email + password, returns an access token stored in the browser. - Sign up (
/signup) — self-service registration for new users. - Forgot password (
/recover-password) — enter your email to receive a reset link. - Reset password (
/reset-password) — set a new password using the emailed token.
Note
Password recovery emails are only sent when SMTP is configured. During local development they are captured by Mailcatcher instead — open http://localhost:1080 to read them. See Email in development.
3.2 The dashboard
After logging in you land on the dashboard, which uses a collapsible sidebar for navigation. The pages available are:
| Page | Route | What you can do |
|---|---|---|
| Dashboard | / |
Landing page greeting the logged-in user |
| Items | /items |
Create, view, edit, and delete your items in a paginated table |
| Admin | /admin |
(Superusers) Manage all users — add, edit, delete |
| User Settings | /settings |
Update your profile, change password, change appearance, delete account |
3.3 Managing items
The Items page is the template's example of a full create-read-update-delete (CRUD) feature, wired end to end from the database to the UI:
- Click Add Item to create one (title required, description optional).
- Each item appears in a sortable, paginated table.
- Use the per-row actions menu to edit or delete an item.
- Regular users see only their own items; superusers see everyone's.
This is the feature you will most likely copy and rename when you build your own resource. See the Admin Guide for how the item model, API routes, and frontend pages fit together.
3.4 Managing users (admin)
Superusers get an Admin page to manage every account:
- Add User — create a user with an email, name, password, and optional superuser/active flags.
- Edit User — change a user's details or reset their password.
- Delete User — remove a user (and, via cascade, their items).
Note
The first superuser is created automatically from
FIRST_SUPERUSER/FIRST_SUPERUSER_PASSWORDin your.envthe first time the stack starts. From there, that superuser can create other users through the Admin page or the API.
3.5 User settings
Every logged-in user can manage their own account from Settings:
- My profile — update full name and email.
- Password — change your password (requires your current password).
- Appearance — switch between light and dark mode (the app ships with dark mode by default).
- Danger zone — permanently delete your own account.
3.6 Dark mode
The interface supports light and dark themes out of the box, defaulting to dark. Your choice is remembered in the browser between visits.
4. Configuration
All configuration lives in a single .env file at the root of the project. The
backend, frontend build, database, and proxy all read from it. After changing any
value, restart the stack with docker compose watch.
4.1 The essentials
These are the values you are most likely to set when starting out:
| Variable | Default | What it does |
|---|---|---|
PROJECT_NAME |
Full Stack FastAPI Project |
Name shown in API docs and emails |
STACK_NAME |
full-stack-fastapi-project |
Docker Compose project/label name (no spaces/dots) |
DOMAIN |
localhost |
Base domain; becomes your real domain in production |
FRONTEND_HOST |
http://localhost:5173 |
Where the frontend lives (used in email links) |
ENVIRONMENT |
local |
One of local, staging, production |
FIRST_SUPERUSER |
[email removed] |
Email of the auto-created admin |
FIRST_SUPERUSER_PASSWORD |
changethis |
Password of the auto-created admin |
4.2 Database, email, and secrets
| Variable | Default | What it does |
|---|---|---|
POSTGRES_SERVER |
localhost |
Database host (db inside Docker Compose) |
POSTGRES_PORT |
5432 |
Database port |
POSTGRES_DB |
app |
Database name |
POSTGRES_USER |
postgres |
Database user |
POSTGRES_PASSWORD |
changethis |
Database password — change this |
SECRET_KEY |
changethis |
Signs login tokens — change this |
BACKEND_CORS_ORIGINS |
local hosts | Comma-separated list of browser origins allowed to call the API |
SMTP_HOST |
(empty) | SMTP server for sending email (leave empty to disable email) |
SMTP_USER / SMTP_PASSWORD |
(empty) | SMTP credentials |
SMTP_PORT |
587 |
SMTP port |
SMTP_TLS / SMTP_SSL |
True / False |
SMTP encryption mode |
EMAILS_FROM_EMAIL |
[email removed] |
"From" address for outgoing email |
SENTRY_DSN |
(empty) | Sentry error-tracking DSN (optional) |
Tip
Email features (password recovery, new-account notifications) turn on automatically once both
SMTP_HOSTandEMAILS_FROM_EMAILare set. Until then, the API simply skips sending — nothing breaks.
For the complete list of every setting, including advanced backend-only options like token lifetimes, see the Admin Guide → Configuration Reference.
4.3 Generate secure secrets
Three values ship as changethis and must be replaced before any real deployment:
SECRET_KEY, POSTGRES_PASSWORD, and FIRST_SUPERUSER_PASSWORD. Generate a strong
random value for each:
python -c "import secrets; print(secrets.token_urlsafe(32))"
Xy7d9F2kQ1mN8pZ3rT6vW0bJ4hL5sG... (example — yours will differ)
Run it once per secret and paste the result into .env.
Warning
If you set
ENVIRONMENTtostagingorproductionwhile any of these three is stillchangethis, the backend refuses to start and raises an error. This is a deliberate safety check, not a bug.
5. Developing on the Template
This is starter code — you are meant to edit it. The stack is designed so you can run each part either inside Docker or directly on your machine, whichever is faster for the change you are making.
5.1 Run a service locally instead of in Docker
Each service uses the same port whether it runs in Docker or locally, so you can stop the container and start a local dev server in its place with no other changes.
For the frontend (live reload, fastest for UI work):
docker compose stop frontend
cd frontend
bun install
bun run dev
For the backend (live reload via FastAPI):
docker compose stop backend
cd backend
uv sync
source .venv/bin/activate
fastapi dev app/main.py
Note
The backend uses uv for Python dependencies and the frontend uses Bun (Node.js also works). Both are the recommended tools but only needed if you run a service outside Docker.
5.2 Where the code lives
| You want to change... | Edit... |
|---|---|
| Data models / database tables | backend/app/models.py |
| API endpoints | backend/app/api/routes/ |
| Database read/write helpers | backend/app/crud.py |
| Backend settings | backend/app/core/config.py (values in .env) |
| Frontend pages | frontend/src/routes/ |
| Reusable UI components | frontend/src/components/ |
| Frontend hooks | frontend/src/hooks/ |
5.3 Keeping the frontend API client in sync
The frontend talks to the backend through an automatically generated TypeScript
client (in frontend/src/client/). Whenever you change the backend API, regenerate the
client so the frontend stays type-safe:
# From the project root, with the backend running
bash ./scripts/generate-client.sh
Then commit the regenerated files. This means you never hand-write API calls — adding a backend endpoint and regenerating gives you a ready-to-use typed function on the frontend.
Tip
Forgetting to regenerate after a backend change is the most common source of frontend type errors. If TypeScript complains about a missing service or field, run the generator first.
5.4 Reading emails in development
When running with Docker Compose locally, the backend is automatically pointed at Mailcatcher instead of a real mail server. Any email the app "sends" (password recovery, new-account notices) is captured and shown at http://localhost:1080. No real email leaves your machine.
5.5 Running the tests
The backend ships with a Pytest suite and the frontend with Playwright end-to-end tests.
Backend tests:
docker compose exec backend bash scripts/tests-start.sh
Frontend end-to-end tests (stack must be running):
cd frontend
bunx playwright test
# Watch the tests run in an interactive UI
bunx playwright test --ui
6. Building an API-Only App
If you only need the backend (no dashboard), you can remove the frontend cleanly:
- [ ] Delete the
./frontenddirectory. - [ ] Remove the
frontendservice fromcompose.yml. - [ ] Remove the
frontendandplaywrightservices fromcompose.override.yml.
That's it — you now have an API-only application. The FRONTEND environment variables
can be left in place; they simply have no effect.
7. Troubleshooting
Error: The value of SECRET_KEY is "changethis"...
Cause: You set ENVIRONMENT to staging or production while SECRET_KEY,
POSTGRES_PASSWORD, or FIRST_SUPERUSER_PASSWORD is still the placeholder changethis.
Solution: Generate real values (see 4.3) and put them
in .env. Prevention: Always change the three secrets before changing ENVIRONMENT.
Error: Incorrect email or password
Cause: The credentials sent to /login/access-token don't match an active user.
Solution: Use the FIRST_SUPERUSER / FIRST_SUPERUSER_PASSWORD from your .env,
or sign up a new account. Prevention: Remember email login is case- and
whitespace-sensitive.
The stack starts but the dashboard can't reach the API
Cause: Your browser's origin isn't in BACKEND_CORS_ORIGINS, so the API rejects the
request. Solution: Add your frontend URL to BACKEND_CORS_ORIGINS in .env (comma
separated) and restart. Prevention: Keep this list in sync whenever you change
DOMAIN or FRONTEND_HOST.
The backend takes a long time to start (or restarts repeatedly)
Cause: The backend waits for PostgreSQL to be ready before it starts (up to five
minutes), retrying once per second. Solution: Watch docker compose logs db and
docker compose logs backend; a healthy database resolves it. Prevention: Make sure
your POSTGRES_* values are consistent across .env.
Frontend type errors after a backend change
Cause: The generated API client is stale. Solution: Run
bash ./scripts/generate-client.sh and commit the result. Prevention: Regenerate
the client every time the backend API changes.
Password recovery "works" but no email arrives
Cause: Email is only sent when SMTP_HOST and EMAILS_FROM_EMAIL are both set;
locally, email goes to Mailcatcher. Solution: Check http://localhost:1080 in
development, or configure SMTP for production. Prevention: Confirm both SMTP values
are present before expecting real delivery.
docker compose watch isn't picking up my changes
Cause: watch syncs source into the container, but some changes (like new
dependencies) require a rebuild. Solution: Stop and re-run docker compose watch;
for dependency changes the backend image rebuilds automatically. Prevention: Restart
the stack after editing pyproject.toml or package.json.
8. Frequently Asked Questions
Q: Do I install this from npm or pip? No. It is a project template you clone or generate with Copier, then own and edit. There is no package to install.
Q: Which database does it use?
PostgreSQL. The connection is assembled from the POSTGRES_* variables in .env.
Q: How long do login sessions last? Access tokens are valid for 8 days by default. This is configurable in the backend (see the Admin Guide).
Q: Can I use a different frontend or no frontend at all?
Yes. The backend is fully independent; see Building an API-Only App.
You can also point the frontend at a remote backend by setting VITE_API_URL.
Q: How do I add my own data model (instead of "items")?
Add a model in backend/app/models.py, create API routes under
backend/app/api/routes/, create an Alembic migration, then regenerate the frontend
client and build a page under frontend/src/routes/. The existing "items" feature is a
complete worked example to copy.
Q: How do I change the database schema?
Edit backend/app/models.py, then create and apply an Alembic migration. The Admin
Guide covers the exact commands.
Q: Where do the API docs come from?
FastAPI generates them automatically from your code. Visit /docs (Swagger UI) or
/redoc while the backend is running.
Q: Is it ready for production? The template is built for it — it includes Traefik with automatic HTTPS, multi-stage Docker builds, and GitHub Actions CI/CD. You still must set real secrets and a domain. See the Admin Guide → Deployment.
Q: How do I get future improvements from the template?
Add it as a Git upstream remote and git pull --no-commit upstream master (see
2.2).
Q: What's the difference between a regular user and a superuser? Superusers can manage all users and see all items; regular users can only manage their own account and their own items.