Skip to content

Commit 0e75e67

Browse files
authored
♻️ Simplify domains with api.example.com for API and dashboard.example.com for frontend, improve local development with localhost (fastapi#1344)
1 parent ee6d04c commit 0e75e67

File tree

10 files changed

+114
-118
lines changed

10 files changed

+114
-118
lines changed

.github/workflows/playwright.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ jobs:
4141
working-directory: frontend
4242
- run: docker compose build
4343
- run: docker compose down -v --remove-orphans
44-
- run: docker compose up -d --wait
44+
- run: docker compose up -d --wait backend mailcatcher
4545
- name: Run Playwright tests
4646
run: npx playwright test --fail-on-flaky-tests --trace=retain-on-failure
4747
working-directory: frontend

backend/README.md

+9-43
Original file line numberDiff line numberDiff line change
@@ -5,45 +5,11 @@
55
* [Docker](https://www.docker.com/).
66
* [Poetry](https://python-poetry.org/) for Python package and environment management.
77

8-
## Local Development
8+
## Docker Compose
99

10-
* Start the stack with Docker Compose:
10+
Start the local development environment with Docker Compose following the guide in [../development.md](../development.md).
1111

12-
```bash
13-
docker compose up -d
14-
```
15-
16-
* Now you can open your browser and interact with these URLs:
17-
18-
Frontend, built with Docker, with routes handled based on the path: http://localhost
19-
20-
Backend, JSON based web API based on OpenAPI: http://localhost/api/
21-
22-
Automatic interactive documentation with Swagger UI (from the OpenAPI backend): http://localhost/docs
23-
24-
Adminer, database web administration: http://localhost:8080
25-
26-
Traefik UI, to see how the routes are being handled by the proxy: http://localhost:8090
27-
28-
**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.
29-
30-
To check the logs, run:
31-
32-
```bash
33-
docker compose logs
34-
```
35-
36-
To check the logs of a specific service, add the name of the service, e.g.:
37-
38-
```bash
39-
docker compose logs backend
40-
```
41-
42-
If your Docker is not running in `localhost` (the URLs above wouldn't work) you would need to use the IP or domain where your Docker is running.
43-
44-
## Backend local development, additional details
45-
46-
### General workflow
12+
## General Workflow
4713

4814
By default, the dependencies are managed with [Poetry](https://python-poetry.org/), go there and install it.
4915

@@ -63,13 +29,13 @@ Make sure your editor is using the correct Python virtual environment.
6329

6430
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`.
6531

66-
### VS Code
32+
## VS Code
6733

6834
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.
6935

7036
The setup is also already configured so you can run the tests through the VS Code Python tests tab.
7137

72-
### Docker Compose Override
38+
## Docker Compose Override
7339

7440
During development, you can change Docker Compose settings that will only affect the local development environment in the file `docker-compose.override.yml`.
7541

@@ -123,7 +89,7 @@ Nevertheless, if it doesn't detect a change but a syntax error, it will just sto
12389

12490
...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.
12591

126-
### Backend tests
92+
## Backend tests
12793

12894
To test the backend run:
12995

@@ -135,7 +101,7 @@ The tests run with Pytest, modify and add tests to `./backend/app/tests/`.
135101

136102
If you use GitHub Actions the tests will run automatically.
137103

138-
#### Test running stack
104+
### Test running stack
139105

140106
If your stack is already up and you just want to run the tests, you can use:
141107

@@ -151,11 +117,11 @@ For example, to stop on first error:
151117
docker compose exec backend bash /app/tests-start.sh -x
152118
```
153119

154-
#### Test Coverage
120+
### Test Coverage
155121

156122
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.
157123

158-
### Migrations
124+
## Migrations
159125

160126
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.
161127

backend/app/core/config.py

+1-9
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,9 @@ class Settings(BaseSettings):
3131
SECRET_KEY: str = secrets.token_urlsafe(32)
3232
# 60 minutes * 24 hours * 8 days = 8 days
3333
ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 24 * 8
34-
DOMAIN: str = "localhost"
34+
FRONTEND_HOST: str = "http://localhost:5173"
3535
ENVIRONMENT: Literal["local", "staging", "production"] = "local"
3636

37-
@computed_field # type: ignore[prop-decorator]
38-
@property
39-
def server_host(self) -> str:
40-
# Use HTTPS for anything other than local development
41-
if self.ENVIRONMENT == "local":
42-
return f"http://{self.DOMAIN}"
43-
return f"https://{self.DOMAIN}"
44-
4537
BACKEND_CORS_ORIGINS: Annotated[
4638
list[AnyUrl] | str, BeforeValidator(parse_cors)
4739
] = []

backend/app/utils.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def generate_test_email(email_to: str) -> EmailData:
6464
def generate_reset_password_email(email_to: str, email: str, token: str) -> EmailData:
6565
project_name = settings.PROJECT_NAME
6666
subject = f"{project_name} - Password recovery for user {email}"
67-
link = f"{settings.server_host}/reset-password?token={token}"
67+
link = f"{settings.FRONTEND_HOST}/reset-password?token={token}"
6868
html_content = render_email_template(
6969
template_name="reset_password.html",
7070
context={
@@ -90,7 +90,7 @@ def generate_new_account_email(
9090
"username": username,
9191
"password": password,
9292
"email": email_to,
93-
"link": settings.server_host,
93+
"link": settings.FRONTEND_HOST,
9494
},
9595
)
9696
return EmailData(html_content=html_content, subject=subject)

deployment.md

+7-7
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ But you have to configure a couple things first. 🤓
1212

1313
* Have a remote server ready and available.
1414
* Configure the DNS records of your domain to point to the IP of the server you just created.
15-
* 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 `traefik.fastapi-project.example.com`, `adminer.fastapi-project.example.com`, etc. And also for `staging`, like `staging.fastapi-project.example.com`, `staging.adminer.fastapi-project.example.com`, etc.
15+
* 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.
1616
* Install and configure [Docker](https://docs.docker.com/engine/install/) on the remote server (Docker Engine, not Docker Desktop).
1717

1818
## Public Traefik
@@ -284,20 +284,20 @@ Traefik UI: `https://traefik.fastapi-project.example.com`
284284

285285
### Production
286286

287-
Frontend: `https://fastapi-project.example.com`
287+
Frontend: `https://dashboard.fastapi-project.example.com`
288288

289-
Backend API docs: `https://fastapi-project.example.com/docs`
289+
Backend API docs: `https://api.fastapi-project.example.com/docs`
290290

291-
Backend API base URL: `https://fastapi-project.example.com/api/`
291+
Backend API base URL: `https://api.fastapi-project.example.com`
292292

293293
Adminer: `https://adminer.fastapi-project.example.com`
294294

295295
### Staging
296296

297-
Frontend: `https://staging.fastapi-project.example.com`
297+
Frontend: `https://dashboard.staging.fastapi-project.example.com`
298298

299-
Backend API docs: `https://staging.fastapi-project.example.com/docs`
299+
Backend API docs: `https://api.staging.fastapi-project.example.com/docs`
300300

301-
Backend API base URL: `https://staging.fastapi-project.example.com/api/`
301+
Backend API base URL: `https://api.staging.fastapi-project.example.com`
302302

303303
Adminer: `https://adminer.staging.fastapi-project.example.com`

development.md

+74-46
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,100 @@
11
# FastAPI Project - Development
22

3-
## Development in `localhost` with a custom domain
3+
## Docker Compose
44

5-
You might want to use something different than `localhost` as the domain. For example, if you are having problems with cookies that need a subdomain, and Chrome is not allowing you to use `localhost`.
5+
* Start the local stack with Docker Compose:
66

7-
In that case, you have two options: you could use the instructions to modify your system `hosts` file with the instructions below in **Development with a custom IP** or you can just use `localhost.tiangolo.com`, it is set up to point to `localhost` (to the IP `127.0.0.1`) and all its subdomains too. And as it is an actual domain, the browsers will store the cookies you set during development, etc.
8-
9-
If you used the default CORS enabled domains while generating the project, `localhost.tiangolo.com` was configured to be allowed. If you didn't, you will need to add it to the list in the variable `BACKEND_CORS_ORIGINS` in the `.env` file.
10-
11-
To configure it in your stack, follow the section **Change the development "domain"** below, using the domain `localhost.tiangolo.com`.
7+
```bash
8+
docker compose up -d
9+
```
1210

13-
After performing those steps you should be able to open: http://localhost.tiangolo.com and it will be served by your stack in `localhost`.
11+
* Now you can open your browser and interact with these URLs:
1412

15-
Check all the corresponding available URLs in the section at the end.
13+
Frontend, built with Docker, with routes handled based on the path: http://localhost:5173
1614

17-
## Development with a custom IP
15+
Backend, JSON based web API based on OpenAPI: http://localhost:8000
1816

19-
If you are running Docker in an IP address different than `127.0.0.1` (`localhost`), you will need to perform some additional steps. That will be the case if you are running a custom Virtual Machine or your Docker is located in a different machine in your network.
17+
Automatic interactive documentation with Swagger UI (from the OpenAPI backend): http://localhost:8000/docs
2018

21-
In that case, you will need to use a fake local domain (`dev.example.com`) and make your computer think that the domain is served by the custom IP (e.g. `192.168.99.150`).
19+
Adminer, database web administration: http://localhost:8080
2220

23-
If you have a custom domain like that, you need to add it to the list in the variable `BACKEND_CORS_ORIGINS` in the `.env` file.
21+
Traefik UI, to see how the routes are being handled by the proxy: http://localhost:8090
2422

25-
* Open your `hosts` file with administrative privileges using a text editor:
23+
**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.
2624

27-
* **Note for Windows**: If you are in Windows, open the main Windows menu, search for "notepad", right click on it, and select the option "open as Administrator" or similar. Then click the "File" menu, "Open file", go to the directory `c:\Windows\System32\Drivers\etc\`, select the option to show "All files" instead of only "Text (.txt) files", and open the `hosts` file.
28-
* **Note for Mac and Linux**: Your `hosts` file is probably located at `/etc/hosts`, you can edit it in a terminal running `sudo nano /etc/hosts`.
25+
To check the logs, run:
2926

30-
* Additional to the contents it might have, add a new line with the custom IP (e.g. `192.168.99.150`) a space character, and your fake local domain: `dev.example.com`.
27+
```bash
28+
docker compose logs
29+
```
3130

32-
The new line might look like:
31+
To check the logs of a specific service, add the name of the service, e.g.:
3332

34-
```
35-
192.168.99.150 dev.example.com
33+
```bash
34+
docker compose logs backend
3635
```
3736

38-
* Save the file.
39-
* **Note for Windows**: Make sure you save the file as "All files", without an extension of `.txt`. By default, Windows tries to add the extension. Make sure the file is saved as is, without extension.
37+
## Local Development
4038

41-
...that will make your computer think that the fake local domain is served by that custom IP, and when you open that URL in your browser, it will talk directly to your locally running server when it is asked to go to `dev.example.com` and think that it is a remote server while it is actually running in your computer.
39+
The Docker Compose files are configured so that each of the services is available in a different port in `localhost`.
4240

43-
To configure it in your stack, follow the section **Change the development "domain"** below, using the domain `dev.example.com`.
41+
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`.
4442

45-
After performing those steps you should be able to open: http://dev.example.com and it will be server by your stack in `192.168.99.150`.
43+
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.
4644

47-
Check all the corresponding available URLs in the section at the end.
45+
For example, you can stop that `frontend` service in the Docker Compose:
4846

49-
## Change the development "domain"
47+
```bash
48+
docker compose stop frontend
49+
```
5050

51-
If you need to use your local stack with a different domain than `localhost`, you need to make sure the domain you use points to the IP where your stack is set up.
51+
And then start the local frontend development server:
5252

53-
To simplify your Docker Compose setup, for example, so that the API docs (Swagger UI) knows where is your API, you should let it know you are using that domain for development.
53+
```bash
54+
cd frontend
55+
npm run dev
56+
```
5457

55-
* Open the file located at `./.env`. It would have a line like:
58+
Or you could stop the `backend` Docker Compose service:
5659

57-
```
58-
DOMAIN=localhost
60+
```bash
61+
docker compose stop backend
5962
```
6063

61-
* Change it to the domain you are going to use, e.g.:
64+
And then you can run the local development server for the backend:
6265

66+
```bash
67+
cd backend
68+
fastapi dev app/main.py
6369
```
70+
71+
## Docker Compose in `localhost.tiangolo.com`
72+
73+
When you start the Docker Compose stack, it uses `localhost` by default, with different ports for each service (backend, frontend, adminer, etc).
74+
75+
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.
76+
77+
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.
78+
79+
If you want to test that it's all working locally, you can edit the local `.env` file, and change:
80+
81+
```dotenv
6482
DOMAIN=localhost.tiangolo.com
6583
```
6684

67-
That variable will be used by the Docker Compose files.
85+
That will be used by the Docker Compose files to configure the base domain for the services.
6886

69-
After that, you can restart your stack with:
87+
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.
88+
89+
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.
90+
91+
After you update it, run again:
7092

7193
```bash
7294
docker compose up -d
7395
```
7496

75-
and check all the corresponding available URLs in the section at the end.
97+
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`.
7698

7799
## Docker Compose files and env vars
78100

@@ -84,6 +106,12 @@ These Docker Compose files use the `.env` file containing configurations to be i
84106

85107
They also use some additional configurations taken from environment variables set in the scripts before calling the `docker compose` command.
86108

109+
After changing variables, make sure you restart the stack:
110+
111+
```bash
112+
docker compose up -d
113+
```
114+
87115
## The .env file
88116

89117
The `.env` file is the one that contains all your configurations, generated keys and passwords, etc.
@@ -92,7 +120,7 @@ Depending on your workflow, you could want to exclude it from Git, for example i
92120

93121
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.
94122

95-
### Pre-commits and code linting
123+
## Pre-commits and code linting
96124

97125
we are using a tool called [pre-commit](https://pre-commit.com/) for code linting and formatting.
98126

@@ -146,29 +174,29 @@ The production or staging URLs would use these same paths, but with your own dom
146174

147175
Development URLs, for local development.
148176

149-
Frontend: http://localhost
177+
Frontend: http://localhost:5173
150178

151-
Backend: http://localhost/api/
179+
Backend: http://localhost:8000
152180

153-
Automatic Interactive Docs (Swagger UI): http://localhost/docs
181+
Automatic Interactive Docs (Swagger UI): http://localhost:8000/docs
154182

155-
Automatic Alternative Docs (ReDoc): http://localhost/redoc
183+
Automatic Alternative Docs (ReDoc): http://localhost:8000/redoc
156184

157185
Adminer: http://localhost:8080
158186

159187
Traefik UI: http://localhost:8090
160188

161-
### Development in localhost with a custom domain URLs
189+
### Development URLs with `localhost.tiangolo.com` Configured
162190

163191
Development URLs, for local development.
164192

165-
Frontend: http://localhost.tiangolo.com
193+
Frontend: http://dashboard.localhost.tiangolo.com
166194

167-
Backend: http://localhost.tiangolo.com/api/
195+
Backend: http://api.localhost.tiangolo.com
168196

169-
Automatic Interactive Docs (Swagger UI): http://localhost.tiangolo.com/docs
197+
Automatic Interactive Docs (Swagger UI): http://api.localhost.tiangolo.comdocs
170198

171-
Automatic Alternative Docs (ReDoc): http://localhost.tiangolo.com/redoc
199+
Automatic Alternative Docs (ReDoc): http://api.localhost.tiangolo.comredoc
172200

173201
Adminer: http://localhost.tiangolo.com:8080
174202

0 commit comments

Comments
 (0)