* Reorganize project directory structure
* Allow edit users/items, add useAuth and useCustomToast, password confirmation
* Minor improvements for consistency
* Add 'Cancel' button to UserInformation in editMode
* Refactor UserSettings
* Enable user password changes and improve error handling
* Enable user information update
* Add logout to Sidebar in mobile devices, conditional tabs depending on role and other improvements
* Add badges
* Remove comment
* Appearance tab updates
* Change badge color
* Reset inputs when clicking on 'Cancel' button
* Disable actions menu for Superuser when logged in
* Modify Logout and update stores
* 🔧 Add first Copier config
* 🔧 Add custom copier answers file
* 🔨 Add Copier script to update .env after generating/copying
* 🙈 Update .gitignores from Copier updates
* 🔧 Update .env, restructure in order of relevance
* 🔧 Remove Copier config for SMTP port, if necessary, it can be overwritten in .env
* ♻️ Refactor Copier files, make them less visible
* 🔧 Update Copier config
* 🔥 Remove Cookiecutter files
* 🔧 Add STACK_NAME to .env for Traefik labels
* ✨ Upgrade Docker Compose to use Traefik v2
* ✨ Enable Traefik v2 in Docker Compose override for local development
* 🐛 Use internal HTTPS redirect in case the deployment is not through DockerSwarm.rocks
* ♻️ Refactor backend, update DB session handling
* ✨ Add mypy config and plugins
* ➕ Use Python-jose instead of PyJWT
as it has some extra functionalities and features
* ✨ Add/update scripts for test, lint, format
* 🔧 Update lint and format configs
* 🎨 Update import format, comments, and types
* 🎨 Add types to config
* ✨ Add types for all the code, and small fixes
* 🎨 Use global imports to simplify exploring with Jupyter
* ♻️ Import schemas and models, instead of each class
* 🚚 Rename db_session to db for simplicity
* 📌 Update dependencies installation for testing
* 🔧 Update scripts and configs
* 🔧 Add shebang to script
* 🔥 Remove test and dev configs, pass inline
* ✨ Add local development dev-link set up
* 📝 Update generated docs with refactor
* 📝 Add Contributing guide
* Ignore Flake8 unused import error F401
https://flake8.readthedocs.io/en/latest/user/error-codes.html
The apparently unused imports may be needed for SQLAlchemy.
As the code comment says:
make sure all SQL Alchemy models are imported before initializing DB
otherwise, SQL Alchemy might fail to initialize properly relationships
See GitHub 28 and 29
* Ignore Flake8 unused variable error F841
https://flake8.readthedocs.io/en/latest/user/error-codes.html
The apparently unused variables may be needed for tests.
* Bring line length into compliance with Black
Should be 88 characters.
* Format alembic code with Black
* Add some information how to run backand test for local backand development
* Bug fixes in backend app
* 🎨 Update format
* ✅ Use random_email for test_update_user
Co-authored-by: Mocsar Kalman <mocsar.kalman@gravityrd.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
* Use Pydantic BaseSettings for config settings
* Update fastapi dep to >=0.47.0 and email_validator to email-validator
* Fix deprecation warning for Pydantic >=1.0
* Properly support old-format comma separated strings for BACKEND_CORS_ORIGINS
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
* Update CRUD utils to use types better.
* Simplify Pydantic model names, from `UserInCreate` to `UserCreate`, etc.
* Upgrade packages.
* Add new generic "Items" models, crud utils, endpoints, and tests. To facilitate re-using them to create new functionality. As they are simple and generic (not like Users), it's easier to copy-paste and adapt them to each use case.
* Update endpoints/*path operations* to simplify code and use new utilities, prefix and tags in `include_router`.
* Update testing utils.
* Update linting rules, relax vulture to reduce false positives.
* Update migrations to include new Items.
* Update project README.md with tips about how to start with backend.
Please follow these instructions, fill every question, and do every step. 🙏
I'm asking this because answering questions and solving problems in GitHub is what consumes most of the time.
I end up not being able to add new features, fix bugs, review pull requests, etc. as fast as I wish because I have to spend too much time handling questions.
All that, on top of all the incredible help provided by a bunch of community members, that give a lot of their time to come here and help others.
That's a lot of work, but if more users came to help others like them just a little bit more, it would be much less effort for them (and you and me 😅).
By asking questions in a structured way (following this) it will be much easier to help you.
And there's a high chance that you will find the solution along the way and you won't even have to submit it and wait for an answer. 😎
As there are too many questions, I'll have to discard and close the incomplete ones. That will allow me (and others) to focus on helping people like you that follow the whole process and help us help you. 🤓
- type:checkboxes
id:checks
attributes:
label:First Check
description:Please confirm and check all the following options.
options:
- label:I added a very descriptive title here.
required:true
- label:I used the GitHub search to find a similar question and didn't find it.
required:true
- label:I searched in the documentation/README.
required:true
- label:I already searched in Google "How to do X" and didn't find any information.
required:true
- label:I already read and followed all the tutorial in the docs/README and didn't find an answer.
required:true
- type:checkboxes
id:help
attributes:
label:Commit to Help
description:|
After submitting this, I commit to one of:
* Read open questions until I find 2 where I can help someone and add a comment to help there.
* I already hit the "watch" button in this repository to receive notifications and I commit to help at least 2 people that ask questions in the future.
options:
- label:I commit to help with one of those options 👆
required:true
- type:textarea
id:example
attributes:
label:Example Code
description:|
Please add a self-contained, [minimal, reproducible, example](https://stackoverflow.com/help/minimal-reproducible-example) with your use case.
If I (or someone) can copy it, run it, and see it right away, there's a much higher chance I (or someone) will be able to help you.
placeholder:|
Write your example code here.
render:Text
validations:
required:true
- type:textarea
id:description
attributes:
label:Description
description:|
What is the problem, question, or error?
Write a short description telling me what you are doing, what you expect to happen, and what is currently happening.
placeholder:|
* Open the browser and call the endpoint `/`.
* It returns a JSON with `{"message": "Hello World"}`.
* But I expected it to return `{"message": "Hello Morty"}`.
validations:
required:true
- type:dropdown
id:os
attributes:
label:Operating System
description:What operating system are you on?
multiple:true
options:
- Linux
- Windows
- macOS
- Other
validations:
required:true
- type:textarea
id:os-details
attributes:
label:Operating System Details
description:You can add more details about your operating system here, in particular if you chose "Other".
validations:
required:true
- type:input
id:python-version
attributes:
label:Python Version
description:|
What Python version are you using?
You can find the Python version with:
```bash
python --version
```
validations:
required:true
- type:textarea
id:context
attributes:
label:Additional Context
description:Add any additional context information or screenshots you think are useful.
about:To suggest an idea or ask about a feature, please start with a question saying what you would like to achieve. There might be a way to do it already.
description:You are @tiangolo or he asked you directly to create an issue here. If not, check the other options. 👇
body:
- type:markdown
attributes:
value:|
Thanks for your interest in this project! 🚀
If you are not @tiangolo or he didn't ask you directly to create an issue here, please start the conversation in a [Question in GitHub Discussions](https://github.com/tiangolo/full-stack-fastapi-template/discussions/categories/questions) instead.
- type:checkboxes
id:privileged
attributes:
label:Privileged issue
description:Confirm that you are allowed to create an issue here.
options:
- label:I'm @tiangolo or he asked me directly to create an issue here.
"message": "Assuming the original need was handled, this will be automatically closed now. But feel free to add more comments or create new issues or PRs."
},
"waiting": {
"delay": 2628000,
"message": "As this PR has been waiting for the original user for a while but seems to be inactive, it's now going to be closed. But if there's anyone interested, feel free to create a new PR.",
"reminder": {
"before": "P3D",
"message": "Heads-up: this will be closed in 3 days unless there's new activity."
}
},
"invalid": {
"delay": 0,
"message": "This was marked as invalid and will be closed now. If this is an error, please provide additional details."
* **Fast**: Very high performance, on par with **NodeJS** and **Go** (thanks to Starlette and Pydantic).
* **Intuitive**: Great editor support. <abbr title="also known as auto-complete, autocompletion, IntelliSense">Completion</abbr> everywhere. Less time debugging.
* **Easy**: Designed to be easy to use and learn. Less time reading docs.
* **Short**: Minimize code duplication. Multiple features from each parameter declaration.
* **Robust**: Get production-ready code. With automatic interactive documentation.
* **Standards-based**: Based on (and fully compatible with) the open standards for APIs: <a href="https://github.com/OAI/OpenAPI-Specification" target="_blank">OpenAPI</a> and <a href="http://json-schema.org/" target="_blank">JSON Schema</a>.
* [**Many other features**](https://github.com/tiangolo/fastapi) including automatic validation, serialization, interactive documentation, authentication with OAuth2 JWT tokens, etc.
* **Secure password** hashing by default.
* **JWT token** authentication.
* **SQLAlchemy** models (independent of Flask extensions, so they can be used with Celery workers directly).
* Basic starting models for users (modify and remove as you need).
* **Alembic** migrations.
* **CORS** (Cross Origin Resource Sharing).
* **Celery** worker that can import and use models and code from the rest of the backend selectively (you don't have to install the complete app in each worker).
* REST backend tests based on **Pytest**, integrated with Docker, so you can test the full API interaction, independent on the database. As it runs in Docker, it can build a new data store from scratch each time (so you can use ElasticSearch, MongoDB, CouchDB, or whatever you want, and just test that the API works).
* Easy Python integration with **Jupyter Kernels** for remote or in-Docker development with extensions like Atom Hydrogen or Visual Studio Code Jupyter.
* **Vue** frontend:
* Generated with Vue CLI.
* **JWT Authentication** handling.
* Login view.
* After login, main dashboard view.
* Main dashboard with user creation and edition.
* Self user edition.
* **Vuex**.
* **Vue-router**.
* **Vuetify** for beautiful material design components.
* **TypeScript**.
* Docker server based on **Nginx** (configured to play nicely with Vue-router).
* Docker multi-stage building, so you don't need to save or commit compiled code.
* Frontend tests ran at build time (can be disabled too).
* Made as modular as possible, so it works out of the box, but you can re-generate with Vue CLI or create it as you need, and re-use what you want.
* **PGAdmin** for PostgreSQL database, you can modify it to use PHPMyAdmin and MySQL easily.
* **Flower** for Celery jobs monitoring.
* Load balancing between frontend and backend with **Traefik**, so you can have both under the same domain, separated by path, but served by different containers.
* Traefik integration, including Let's Encrypt **HTTPS** certificates automatic generation.
* GitLab **CI** (continuous integration), including frontend and backend testing.
If you have `pipx` and you didn't install `copier`, you can run it directly:
```bash
pipx run copier copy https://github.com/fastapi/full-stack-fastapi-template my-awesome-project --trust
```
**Note** the `--trust` option is necessary to be able to execute a [post-creation script](https://github.com/fastapi/full-stack-fastapi-template/blob/master/.copier/update_dotenv.py) that updates your `.env` files.
### Input Variables
Copier will ask you for some data, you might want to have at hand before generating the project.
But don't worry, you can just update any of that in the `.env` files afterwards.
The input variables, with their default values (some auto generated) are:
The input variables, with their default values (some auto generated) are:
*`project_name`: The name of the project
-`project_name`: (default: `"FastAPI Project"`) The name of the project, shown to API users (in .env).
*`project_slug`: The development friendly name of the project. By default, based on the project name
-`stack_name`: (default: `"fastapi-project"`) The name of the stack used for Docker Compose labels and project name (no spaces, no periods) (in .env).
*`domain_main`: The domain in where to deploy the project for production (from the branch `production`), used by the load balancer, backend, etc. By default, based on the project slug.
-`secret_key`: (default: `"changethis"`) The secret key for the project, used for security, stored in .env, you can generate one with the method above.
*`domain_staging`: The domain in where to deploy while staging (before production) (from the branch `master`). By default, based on the main domain.
-`first_superuser`: (default: `"admin@example.com"`) The email of the first superuser (in .env).
-`first_superuser_password`: (default: `"changethis"`) The password of the first superuser (in .env).
-`smtp_host`: (default: "") The SMTP server host to send emails, you can set it later in .env.
-`smtp_user`: (default: "") The SMTP server user to send emails, you can set it later in .env.
-`smtp_password`: (default: "") The SMTP server password to send emails, you can set it later in .env.
-`emails_from_email`: (default: `"info@example.com"`) The email account to send emails from, you can set it later in .env.
-`postgres_password`: (default: `"changethis"`) The password for the PostgreSQL database, stored in .env, you can generate one with the method above.
-`sentry_dsn`: (default: "") The DSN for Sentry, if you are using it, you can set it later in .env.
*`docker_swarm_stack_name_main`: The name of the stack while deploying to Docker in Swarm mode for production. By default, based on the domain.
## Backend Development
*`docker_swarm_stack_name_staging`: The name of the stack while deploying to Docker in Swarm mode for staging. By default, based on the domain.
*`secret_key`: Backend server secret key. Use the method above to generate it.
*`first_superuser`: The first superuser generated, with it you will be able to create more users, etc. By default, based on the domain.
*`first_superuser_password`: First superuser password. Use the method above to generate it.
*`backend_cors_origins`: Origins (domains, more or less) that are enabled for CORS (Cross Origin Resource Sharing). This allows a frontend in one domain (e.g. `https://dashboard.example.com`) to communicate with this backend, that could be living in another domain (e.g. `https://api.example.com`). It can also be used to allow your local frontend (with a custom `hosts` domain mapping, as described in the project's `README.md`) that could be living in `http://dev.example.com:8080` to cummunicate with the backend at `https://stag.example.com`. Notice the `http` vs `https` and the `dev.` prefix for local development vs the "staging" `stag.` prefix. By default, it includes origins for production, staging and development, with ports commonly used during local development by several popular frontend frameworks (Vue with `:8080`, React, Angular).
*`smtp_port`: Port to use to send emails via SMTP. By default `587`.
*`smtp_host`: Host to use to send emails, it would be given by your email provider, like Mailgun, Sparkpost, etc.
*`smtp_user`: The user to use in the SMTP connection. The value will be given by your email provider.
*`smtp_password`: The password to be used in the SMTP connection. The value will be given by the email provider.
*`smtp_emails_from_email`: The email account to use as the sender in the notification emails, it would be something like `info@your-custom-domain.com`.
*`postgres_password`: Postgres database password. Use the method above to generate it. (You could easily modify it to use MySQL, MariaDB, etc).
## Frontend Development
*`pgadmin_default_user`: PGAdmin default user, to log-in to the PGAdmin interface.
*`pgadmin_default_user_password`: PGAdmin default user password. Generate it with the method above.
*`traefik_constraint_tag`: The tag to be used by the internal Traefik load balancer (for example, to divide requests between backend and frontend) for production. Used to separate this stack from any other stack you might have. This should identify each stack in each environment (production, staging, etc).
*`traefik_constraint_tag_staging`: The Traefik tag to be used while on staging.
*`traefik_public_network`: This assumes you have another separate publicly facing Traefik at the server / cluster level. This is the network that main Traefik lives in.
*`traefik_public_constraint_tag`: The tag that should be used by stack services that should communicate with the public.
*`flower_auth`: Basic HTTP authentication for flower, in the form`user:password`. By default: "`root:changethis`".
## Deployment
*`sentry_dsn`: Key URL (DSN) of Sentry, for live error reporting. If you are not using it yet, you should, is open source. E.g.: `https://1234abcd:5678ef@sentry.example.com/30`.
*`docker_image_prefix`: Prefix to use for Docker image names. If you are using GitLab Docker registry it would be based on your code repository. E.g.: `git.example.com/development-team/my-awesome-project/`.
## Development
*`docker_image_backend`: Docker image name for the backend. By default, it will be based on your Docker image prefix, e.g.: `git.example.com/development-team/my-awesome-project/backend`. And depending on your environment, a different tag will be appended ( `prod`, `stag`, `branch` ). So, the final image names used will be like: `git.example.com/development-team/my-awesome-project/backend:prod`.
*`docker_image_celeryworker`: Docker image for the celery worker. By default, based on your Docker image prefix.
*`docker_image_frontend`: Docker image for the frontend. By default, based on your Docker image prefix.
## How to deploy
General development docs: [development.md](./development.md).
This stack can be adjusted and used with several deployment options that are compatible with Docker Compose, but it is designed to be used in a cluster controlled with pure Docker in Swarm Mode with a Traefik main load balancer proxy handling automatic HTTPS certificates, using the ideas from <a href="https://dockerswarm.rocks" target="_blank">DockerSwarm.rocks</a>.
This includes using Docker Compose, custom local domains, `.env` configurations, etc.
Please refer to <a href="https://dockerswarm.rocks" target="_blank">DockerSwarm.rocks</a> to see how to deploy such a cluster in 20 minutes.
## More details
After using this generator, your new project (the directory created) will contain an extensive `README.md` with instructions for development, deployment, etc. You can pre-read [the project `README.md` template here too](./{{cookiecutter.project_slug}}/README.md).
## Sibling project generators
* Based on Couchbase: [https://github.com/tiangolo/full-stack-fastapi-couchbase](https://github.com/tiangolo/full-stack-fastapi-couchbase).
## Release Notes
## Release Notes
### Next
Check the file [release-notes.md](./release-notes.md).
* Set `/start-reload.sh` as a command override for development by default.
Security is very important for this project and its community. 🔒
Learn more about it below. 👇
## Versions
The latest version or release is supported.
You are encouraged to write tests for your application and update your versions frequently after ensuring that your tests are passing. This way you will benefit from the latest features, bug fixes, and **security fixes**.
## Reporting a Vulnerability
If you think you found a vulnerability, and even if you are not sure about it, please report it right away by sending an email to: security@tiangolo.com. Please try to be as explicit as possible, describing all the steps and example code to reproduce the security issue.
I (the author, [@tiangolo](https://twitter.com/tiangolo)) will review it thoroughly and get back to you.
## Public Discussions
Please restrain from publicly discussing a potential security vulnerability. 🙊
It's better to discuss privately and try to find a solution first, to limit the potential impact as much as possible.
* [uv](https://docs.astral.sh/uv/) for Python package and environment management.
## Docker Compose
Start the local development environment with Docker Compose following the guide in [../development.md](../development.md).
## General Workflow
By default, the dependencies are managed with [uv](https://docs.astral.sh/uv/), go there and install it.
From `./backend/` you can install all the dependencies with:
```console
$ uv sync
```
Then you can activate the virtual environment with:
```console
$source .venv/bin/activate
```
Make sure your editor is using the correct Python virtual environment, with the interpreter at `backend/.venv/bin/python`.
Modify or add SQLModel models for data and SQL tables in `./backend/app/models.py`, API endpoints in `./backend/app/api/`, CRUD (Create, Read, Update, Delete) utils in `./backend/app/crud.py`.
## VS Code
There are already configurations in place to run the backend through the VS Code debugger, so that you can use breakpoints, pause and explore variables, etc.
The setup is also already configured so you can run the tests through the VS Code Python tests tab.
## Docker Compose Override
During development, you can change Docker Compose settings that will only affect the local development environment in the file `docker-compose.override.yml`.
The changes to that file only affect the local development environment, not the production environment. So, you can add "temporary" changes that help the development workflow.
For example, the directory with the backend code is synchronized in the Docker container, copying the code you change live to the directory inside the container. That allows you to test your changes right away, without having to build the Docker image again. It should only be done during development, for production, you should build the Docker image with a recent version of the backend code. But during development, it allows you to iterate very fast.
There is also a command override that runs `fastapi run --reload` instead of the default `fastapi run`. It starts a single server process (instead of multiple, as would be for production) and reloads the process whenever the code changes. Have in mind that if you have a syntax error and save the Python file, it will break and exit, and the container will stop. After that, you can restart the container by fixing the error and running again:
```console
$ docker compose watch
```
There is also a commented out `command` override, you can uncomment it and comment the default one. It makes the backend container run a process that does "nothing", but keeps the container alive. That allows you to get inside your running container and execute commands inside, for example a Python interpreter to test installed dependencies, or start the development server that reloads when it detects changes.
To get inside the container with a `bash` session you can start the stack with:
```console
$ docker compose watch
```
and then in another terminal, `exec` inside the running container:
```console
$ docker compose exec backend bash
```
You should see an output like:
```console
root@7f2607af31c3:/app#
```
that means that you are in a `bash` session inside your container, as a `root` user, under the `/app` directory, this directory has another directory called "app" inside, that's where your code lives inside the container: `/app/app`.
There you can use the `fastapi run --reload` command to run the debug live reloading server.
```console
$ fastapi run --reload app/main.py
```
...it will look like:
```console
root@7f2607af31c3:/app# fastapi run --reload app/main.py
```
and then hit enter. That runs the live reloading server that auto reloads when it detects code changes.
Nevertheless, if it doesn't detect a change but a syntax error, it will just stop with an error. But as the container is still alive and you are in a Bash session, you can quickly restart it after fixing the error, running the same command ("up arrow" and "Enter").
...this previous detail is what makes it useful to have the container alive doing nothing and then, in a Bash session, make it run the live reload server.
## Backend tests
To test the backend run:
```console
$ bash ./scripts/test.sh
```
The tests run with Pytest, modify and add tests to `./backend/tests/`.
If you use GitHub Actions the tests will run automatically.
### Test running stack
If your stack is already up and you just want to run the tests, you can use:
That `/app/scripts/tests-start.sh` script just calls `pytest` after making sure that the rest of the stack is running. If you need to pass extra arguments to `pytest`, you can pass them to that command and they will be forwarded.
When the tests are run, a file `htmlcov/index.html` is generated, you can open it in your browser to see the coverage of the tests.
## Migrations
As during local development your app directory is mounted as a volume inside the container, you can also run the migrations with `alembic` commands inside the container and the migration code will be in your app directory (instead of being only inside the container). So you can add it to your git repository.
Make sure you create a "revision" of your models and that you "upgrade" your database with that revision every time you change them. As this is what will update the tables in your database. Otherwise, your application will have errors.
* Start an interactive session in the backend container:
```console
$ docker compose exec backend bash
```
* Alembic is already configured to import your SQLModel models from `./backend/app/models.py`.
* After changing a model (for example, adding a column), inside the container, create a revision, e.g.:
```console
$ alembic revision --autogenerate -m "Add column last_name to User model"
```
* Commit to the git repository the files generated in the alembic directory.
* After creating the revision, run the migration in the database (this is what will actually change the database):
```console
$ alembic upgrade head
```
If you don't want to use migrations at all, uncomment the lines in the file at `./backend/app/core/db.py` that end in:
```python
SQLModel.metadata.create_all(engine)
```
and comment the line in the file `scripts/prestart.sh` that contains:
```console
$ alembic upgrade head
```
If you don't want to start with the default models and want to remove them / modify them, from the beginning, without having any previous revision, you can remove the revision files (`.py` Python files) under `./backend/app/alembic/versions/`. And then create a first migration as described above.
## Email Templates
The email templates are in `./backend/app/email-templates/`. Here, there are two directories: `build` and `src`. The `src` directory contains the source files that are used to build the final email templates. The `build` directory contains the final email templates that are used by the application.
Before continuing, ensure you have the [MJML extension](https://marketplace.visualstudio.com/items?itemName=attilabuti.vscode-mjml) installed in your VS Code.
Once you have the MJML extension installed, you can create a new email template in the `src` directory. After creating the new email template and with the `.mjml` file open in your editor, open the command palette with `Ctrl+Shift+P` and search for `MJML: Export to HTML`. This will convert the `.mjml` file to a `.html` file and now you can save it in the build directory.
}</style><styletype="text/css"></style></head><bodystyle="background-color:#fafbfc;"><divstyle="background-color:#fafbfc;"><!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]--><divstyle="background:#ffffff;background-color:#ffffff;Margin:0px auto;max-width:600px;"><tablealign="center"border="0"cellpadding="0"cellspacing="0"role="presentation"style="background:#ffffff;background-color:#ffffff;width:100%;"><tbody><tr><tdstyle="direction:ltr;font-size:0px;padding:40px 20px;text-align:center;vertical-align:top;"><!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:middle;width:560px;" ><![endif]--><divclass="mj-column-per-100 outlook-group-fix"style="font-size:13px;text-align:left;direction:ltr;display:inline-block;vertical-align:middle;width:100%;"><tableborder="0"cellpadding="0"cellspacing="0"role="presentation"style="vertical-align:middle;"width="100%"><tr><tdalign="center"style="font-size:0px;padding:35px;word-break:break-word;"><divstyle="font-family:Arial, Helvetica, sans-serif;font-size:20px;line-height:1;text-align:center;color:#333333;">{{ project_name }} - Password Recovery</div></td></tr><tr><tdalign="center"style="font-size:0px;padding:10px 25px;padding-right:25px;padding-left:25px;word-break:break-word;"><divstyle="font-family:Arial, Helvetica, sans-serif;font-size:16px;line-height:1;text-align:center;color:#555555;"><span>Hello {{ username }}</span></div></td></tr><tr><tdalign="center"style="font-size:0px;padding:10px 25px;padding-right:25px;padding-left:25px;word-break:break-word;"><divstyle="font-family:Arial, Helvetica, sans-serif;font-size:16px;line-height:1;text-align:center;color:#555555;">We've received a request to reset your password. You can do it by clicking the button below:</div></td></tr><tr><tdalign="center"vertical-align="middle"style="font-size:0px;padding:15px 30px;word-break:break-word;"><tableborder="0"cellpadding="0"cellspacing="0"role="presentation"style="border-collapse:separate;line-height:100%;"><tr><tdalign="center"bgcolor="#009688"role="presentation"style="border:none;border-radius:8px;cursor:auto;padding:10px 25px;background:#009688;"valign="middle"><ahref="{{ link }}"style="background:#009688;color:#ffffff;font-family:Ubuntu, Helvetica, Arial, sans-serif;font-size:18px;font-weight:normal;line-height:120%;Margin:0;text-decoration:none;text-transform:none;"target="_blank">Reset password</a></td></tr></table></td></tr><tr><tdalign="center"style="font-size:0px;padding:10px 25px;padding-right:25px;padding-left:25px;word-break:break-word;"><divstyle="font-family:Arial, Helvetica, sans-serif;font-size:16px;line-height:1;text-align:center;color:#555555;">Or copy and paste the following link into your browser:</div></td></tr><tr><tdalign="center"style="font-size:0px;padding:10px 25px;padding-right:25px;padding-left:25px;word-break:break-word;"><divstyle="font-family:Arial, Helvetica, sans-serif;font-size:16px;line-height:1;text-align:center;color:#555555;"><ahref="{{ link }}">{{ link }}</a></div></td></tr><tr><tdalign="center"style="font-size:0px;padding:10px 25px;padding-right:25px;padding-left:25px;word-break:break-word;"><divstyle="font-family:Arial, Helvetica, sans-serif;font-size:16px;line-height:1;text-align:center;color:#555555;">This password will expire in {{ valid_hours }} hours.</div></td></tr><tr><tdstyle="font-size:0px;padding:10px 25px;word-break:break-word;"><pstyle="border-top:solid 2px #cccccc;font-size:1;margin:0px auto;width:100%;"></p><!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" style="border-top:solid 2px #cccccc;font-size:1;margin:0px auto;width:510px;" role="presentation" width="510px" ><tr><td style="height:0;line-height:0;">
</td></tr></table><![endif]--></td></tr><tr><tdalign="center"style="font-size:0px;padding:10px 25px;padding-right:25px;padding-left:25px;word-break:break-word;"><divstyle="font-family:Arial, Helvetica, sans-serif;font-size:14px;line-height:1;text-align:center;color:#555555;">If you didn't request a password recovery you can disregard this email.</div></td></tr></table></div><!--[if mso | IE]></td></tr></table><![endif]--></td></tr></tbody></table></div><!--[if mso | IE]></td></tr></table><![endif]--></div></body></html>
<mj-text align="center" padding="35px" font-size="20px" color="#333">{{ project_name }} - New Account</mj-text>
<mj-text align="center" font-size="16px" padding-left="25px" padding-right="25px" font-family="Arial, Helvetica, sans-serif" color="#555"><span>Welcome to your new account!</span></mj-text>
<mj-text align="center" font-size="16px" padding-left="25px" padding-right="25px" font-family="Arial, Helvetica, sans-serif" color="#555">Here are your account details:</mj-text>
<mj-text align="center" font-size="16px" padding-left="25px" padding-right="25px" font-family="Arial, Helvetica, sans-serif" color="#555">We've received a request to reset your password. You can do it by clicking the button below:</mj-text>
<mj-text align="center" font-size="16px" padding-left="25px" padding-right="25px" font-family="Arial, Helvetica, sans-serif" color="#555">Or copy and paste the following link into your browser:</mj-text>
<mj-text align="center" font-size="16px" padding-left="25px" padding-right="25px" font-family="Arial, Helvetica, sans-serif" color="#555"><a href="{{ link }}">{{ link }}</a></mj-text>
<mj-text align="center" font-size="16px" padding-left="25px" padding-right="25px" font-family="Arial, Helvetica, sans-serif" color="#555">This password will expire in {{ valid_hours }} hours.</mj-text>
<mj-text align="center" font-size="14px" padding-left="25px" padding-right="25px" font-family="Arial, Helvetica, sans-serif" color="#555">If you didn't request a password recovery you can disregard this email.</mj-text>
You can deploy the project using Docker Compose to a remote server.
This project expects you to have a Traefik proxy handling communication to the outside world and HTTPS certificates.
You can use CI/CD (continuous integration and continuous deployment) systems to deploy automatically, there are already configurations to do it with GitHub Actions.
But you have to configure a couple things first. 🤓
## Preparation
* Have a remote server ready and available.
* Configure the DNS records of your domain to point to the IP of the server you just created.
* Configure a wildcard subdomain for your domain, so that you can have multiple subdomains for different services, e.g. `*.fastapi-project.example.com`. This will be useful for accessing different components, like `dashboard.fastapi-project.example.com`, `api.fastapi-project.example.com`, `traefik.fastapi-project.example.com`, `adminer.fastapi-project.example.com`, etc. And also for `staging`, like `dashboard.staging.fastapi-project.example.com`, `adminer.staging.fastapi-project.example.com`, etc.
* Install and configure [Docker](https://docs.docker.com/engine/install/) on the remote server (Docker Engine, not Docker Desktop).
## Public Traefik
We need a Traefik proxy to handle incoming connections and HTTPS certificates.
You need to do these next steps only once.
### Traefik Docker Compose
* Create a remote directory to store your Traefik Docker Compose file:
```bash
mkdir -p /root/code/traefik-public/
```
Copy the Traefik Docker Compose file to your server. You could do it by running the command `rsync` in your local terminal:
```bash
rsync -a docker-compose.traefik.yml root@your-server.example.com:/root/code/traefik-public/
```
### Traefik Public Network
This Traefik will expect a Docker "public network" named `traefik-public` to communicate with your stack(s).
This way, there will be a single public Traefik proxy that handles the communication (HTTP and HTTPS) with the outside world, and then behind that, you could have one or more stacks with different domains, even if they are on the same single server.
To create a Docker "public network" named `traefik-public` run the following command in your remote server:
```bash
docker network create traefik-public
```
### Traefik Environment Variables
The Traefik Docker Compose file expects some environment variables to be set in your terminal before starting it. You can do it by running the following commands in your remote server.
* Create the username for HTTP Basic Auth, e.g.:
```bash
export USERNAME=admin
```
* Create an environment variable with the password for HTTP Basic Auth, e.g.:
```bash
export PASSWORD=changethis
```
* Use openssl to generate the "hashed" version of the password for HTTP Basic Auth and store it in an environment variable:
To verify that the hashed password is correct, you can print it:
```bash
echo $HASHED_PASSWORD
```
* Create an environment variable with the domain name for your server, e.g.:
```bash
export DOMAIN=fastapi-project.example.com
```
* Create an environment variable with the email for Let's Encrypt, e.g.:
```bash
export EMAIL=admin@example.com
```
**Note**: you need to set a different email, an email `@example.com` won't work.
### Start the Traefik Docker Compose
Go to the directory where you copied the Traefik Docker Compose file in your remote server:
```bash
cd /root/code/traefik-public/
```
Now with the environment variables set and the `docker-compose.traefik.yml` in place, you can start the Traefik Docker Compose running the following command:
```bash
docker compose -f docker-compose.traefik.yml up -d
```
## Deploy the FastAPI Project
Now that you have Traefik in place you can deploy your FastAPI project with Docker Compose.
**Note**: You might want to jump ahead to the section about Continuous Deployment with GitHub Actions.
## Environment Variables
You need to set some environment variables first.
Set the `ENVIRONMENT`, by default `local` (for development), but when deploying to a server you would put something like `staging` or `production`:
```bash
export ENVIRONMENT=production
```
Set the `DOMAIN`, by default `localhost` (for development), but when deploying you would use your own domain, for example:
```bash
export DOMAIN=fastapi-project.example.com
```
You can set several variables, like:
* `PROJECT_NAME`: The name of the project, used in the API for the docs and emails.
* `STACK_NAME`: The name of the stack used for Docker Compose labels and project name, this should be different for `staging`, `production`, etc. You could use the same domain replacing dots with dashes, e.g. `fastapi-project-example-com` and `staging-fastapi-project-example-com`.
* `BACKEND_CORS_ORIGINS`: A list of allowed CORS origins separated by commas.
* `SECRET_KEY`: The secret key for the FastAPI project, used to sign tokens.
* `FIRST_SUPERUSER`: The email of the first superuser, this superuser will be the one that can create new users.
* `FIRST_SUPERUSER_PASSWORD`: The password of the first superuser.
* `SMTP_HOST`: The SMTP server host to send emails, this would come from your email provider (E.g. Mailgun, Sparkpost, Sendgrid, etc).
* `SMTP_USER`: The SMTP server user to send emails.
* `SMTP_PASSWORD`: The SMTP server password to send emails.
* `EMAILS_FROM_EMAIL`: The email account to send emails from.
* `POSTGRES_SERVER`: The hostname of the PostgreSQL server. You can leave the default of `db`, provided by the same Docker Compose. You normally wouldn't need to change this unless you are using a third-party provider.
* `POSTGRES_PORT`: The port of the PostgreSQL server. You can leave the default. You normally wouldn't need to change this unless you are using a third-party provider.
* `POSTGRES_PASSWORD`: The Postgres password.
* `POSTGRES_USER`: The Postgres user, you can leave the default.
* `POSTGRES_DB`: The database name to use for this application. You can leave the default of `app`.
* `SENTRY_DSN`: The DSN for Sentry, if you are using it.
## GitHub Actions Environment Variables
There are some environment variables only used by GitHub Actions that you can configure:
* `LATEST_CHANGES`: Used by the GitHub Action [latest-changes](https://github.com/tiangolo/latest-changes) to automatically add release notes based on the PRs merged. It's a personal access token, read the docs for details.
* `SMOKESHOW_AUTH_KEY`: Used to handle and publish the code coverage using [Smokeshow](https://github.com/samuelcolvin/smokeshow), follow their instructions to create a (free) Smokeshow key.
### Generate secret keys
Some environment variables in the `.env` file have a default value of `changethis`.
You have to change them with a secret key, to generate secret keys you can run the following command:
Copy the content and use that as password / secret key. And run that again to generate another secure key.
### Deploy with Docker Compose
With the environment variables in place, you can deploy with Docker Compose:
```bash
docker compose -f docker-compose.yml up -d
```
For production you wouldn't want to have the overrides in `docker-compose.override.yml`, that's why we explicitly specify `docker-compose.yml` as the file to use.
## Continuous Deployment (CD)
You can use GitHub Actions to deploy your project automatically. 😎
You can have multiple environment deployments.
There are already two environments configured, `staging` and `production`. 🚀
### Install GitHub Actions Runner
* On your remote server, create a user for your GitHub Actions:
```bash
sudo adduser github
```
* Add Docker permissions to the `github` user:
```bash
sudo usermod -aG docker github
```
* Temporarily switch to the `github` user:
```bash
sudo su - github
```
* Go to the `github` user's home directory:
```bash
cd
```
* [Install a GitHub Action self-hosted runner following the official guide](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/adding-self-hosted-runners#adding-a-self-hosted-runner-to-a-repository).
* When asked about labels, add a label for the environment, e.g. `production`. You can also add labels later.
After installing, the guide would tell you to run a command to start the runner. Nevertheless, it would stop once you terminate that process or if your local connection to your server is lost.
To make sure it runs on startup and continues running, you can install it as a service. To do that, exit the `github` user and go back to the `root` user:
```bash
exit
```
After you do it, you will be on the previous user again. And you will be on the previous directory, belonging to that user.
Before being able to go the `github` user directory, you need to become the `root` user (you might already be):
```bash
sudo su
```
* As the `root` user, go to the `actions-runner` directory inside of the `github` user's home directory:
```bash
cd /home/github/actions-runner
```
* Install the self-hosted runner as a service with the user `github`:
```bash
./svc.sh install github
```
* Start the service:
```bash
./svc.sh start
```
* Check the status of the service:
```bash
./svc.sh status
```
You can read more about it in the official guide: [Configuring the self-hosted runner application as a service](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/configuring-the-self-hosted-runner-application-as-a-service).
### Set Secrets
On your repository, configure secrets for the environment variables you need, the same ones described above, including `SECRET_KEY`, etc. Follow the [official GitHub guide for setting repository secrets](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions#creating-secrets-for-a-repository).
The current Github Actions workflows expect these secrets:
* `DOMAIN_PRODUCTION`
* `DOMAIN_STAGING`
* `STACK_NAME_PRODUCTION`
* `STACK_NAME_STAGING`
* `EMAILS_FROM_EMAIL`
* `FIRST_SUPERUSER`
* `FIRST_SUPERUSER_PASSWORD`
* `POSTGRES_PASSWORD`
* `SECRET_KEY`
* `LATEST_CHANGES`
* `SMOKESHOW_AUTH_KEY`
## GitHub Action Deployment Workflows
There are GitHub Action workflows in the `.github/workflows` directory already configured for deploying to the environments (GitHub Actions runners with the labels):
* `staging`: after pushing (or merging) to the branch `master`.
* `production`: after publishing a release.
If you need to add extra environments you could use those as a starting point.
## URLs
Replace `fastapi-project.example.com` with your domain.
* Now you can open your browser and interact with these URLs:
Frontend, built with Docker, with routes handled based on the path: <http://localhost:5173>
Backend, JSON based web API based on OpenAPI: <http://localhost:8000>
Automatic interactive documentation with Swagger UI (from the OpenAPI backend): <http://localhost:8000/docs>
Adminer, database web administration: <http://localhost:8080>
Traefik UI, to see how the routes are being handled by the proxy: <http://localhost:8090>
**Note**: The first time you start your stack, it might take a minute for it to be ready. While the backend waits for the database to be ready and configures everything. You can check the logs to monitor it.
To check the logs, run (in another terminal):
```bash
docker compose logs
```
To check the logs of a specific service, add the name of the service, e.g.:
```bash
docker compose logs backend
```
## Mailcatcher
Mailcatcher is a simple SMTP server that catches all emails sent by the backend during local development. Instead of sending real emails, they are captured and displayed in a web interface.
This is useful for:
* Testing email functionality during development
* Verifying email content and formatting
* Debugging email-related functionality without sending real emails
The backend is automatically configured to use Mailcatcher when running with Docker Compose locally (SMTP on port 1025). All captured emails can be viewed at <http://localhost:1080>.
## Local Development
The Docker Compose files are configured so that each of the services is available in a different port in `localhost`.
For the backend and frontend, they use the same port that would be used by their local development server, so, the backend is at `http://localhost:8000` and the frontend at `http://localhost:5173`.
This way, you could turn off a Docker Compose service and start its local development service, and everything would keep working, because it all uses the same ports.
For example, you can stop that `frontend` service in the Docker Compose, in another terminal, run:
```bash
docker compose stop frontend
```
And then start the local frontend development server:
```bash
cd frontend
npm run dev
```
Or you could stop the `backend` Docker Compose service:
```bash
docker compose stop backend
```
And then you can run the local development server for the backend:
```bash
cd backend
fastapi dev app/main.py
```
## Docker Compose in `localhost.tiangolo.com`
When you start the Docker Compose stack, it uses `localhost` by default, with different ports for each service (backend, frontend, adminer, etc).
When you deploy it to production (or staging), it will deploy each service in a different subdomain, like `api.example.com` for the backend and `dashboard.example.com` for the frontend.
In the guide about [deployment](deployment.md) you can read about Traefik, the configured proxy. That's the component in charge of transmitting traffic to each service based on the subdomain.
If you want to test that it's all working locally, you can edit the local `.env` file, and change:
```dotenv
DOMAIN=localhost.tiangolo.com
```
That will be used by the Docker Compose files to configure the base domain for the services.
Traefik will use this to transmit traffic at `api.localhost.tiangolo.com` to the backend, and traffic at `dashboard.localhost.tiangolo.com` to the frontend.
The domain `localhost.tiangolo.com` is a special domain that is configured (with all its subdomains) to point to `127.0.0.1`. This way you can use that for your local development.
After you update it, run again:
```bash
docker compose watch
```
When deploying, for example in production, the main Traefik is configured outside of the Docker Compose files. For local development, there's an included Traefik in `docker-compose.override.yml`, just to let you test that the domains work as expected, for example with `api.localhost.tiangolo.com` and `dashboard.localhost.tiangolo.com`.
## Docker Compose files and env vars
There is a main `docker-compose.yml` file with all the configurations that apply to the whole stack, it is used automatically by `docker compose`.
And there's also a `docker-compose.override.yml` with overrides for development, for example to mount the source code as a volume. It is used automatically by `docker compose` to apply overrides on top of `docker-compose.yml`.
These Docker Compose files use the `.env` file containing configurations to be injected as environment variables in the containers.
They also use some additional configurations taken from environment variables set in the scripts before calling the `docker compose` command.
After changing variables, make sure you restart the stack:
```bash
docker compose watch
```
## The .env file
The `.env` file is the one that contains all your configurations, generated keys and passwords, etc.
Depending on your workflow, you could want to exclude it from Git, for example if your project is public. In that case, you would have to make sure to set up a way for your CI tools to obtain it while building or deploying your project.
One way to do it could be to add each environment variable to your CI/CD system, and updating the `docker-compose.yml` file to read that specific env var instead of reading the `.env` file.
## Pre-commits and code linting
we are using a tool called [pre-commit](https://pre-commit.com/) for code linting and formatting.
When you install it, it runs right before making a commit in git. This way it ensures that the code is consistent and formatted even before it is committed.
You can find a file `.pre-commit-config.yaml` with configurations at the root of the project.
#### Install pre-commit to run automatically
`pre-commit` is already part of the dependencies of the project, but you could also install it globally if you prefer to, following [the official pre-commit docs](https://pre-commit.com/).
After having the `pre-commit` tool installed and available, you need to "install" it in the local repository, so that it runs automatically before each commit.
Using `uv`, you could do it with:
```bash
❯ uv run pre-commit install
pre-commit installed at .git/hooks/pre-commit
```
Now whenever you try to commit, e.g. with:
```bash
git commit
```
...pre-commit will run and check and format the code you are about to commit, and will ask you to add that code (stage it) with git again before committing.
Then you can `git add` the modified/fixed files again and now you can commit.
#### Running pre-commit hooks manually
you can also run `pre-commit` manually on all the files, you can do it using `uv` with:
```bash
❯ uv run pre-commit run --all-files
check for added large files..............................................Passed
Automatic Alternative Docs (ReDoc): <http://api.localhost.tiangolo.com/redoc>
Adminer: <http://localhost.tiangolo.com:8080>
Traefik UI: <http://localhost.tiangolo.com:8090>
MailCatcher: <http://localhost.tiangolo.com:1080>
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.