Skip to content

Commit da32bee

Browse files
Merge pull request #32 from Promptly-Technologies-LLC/31-fix-documentation-page-routing
31 fix documentation page routing
2 parents a32e5da + 05e78b6 commit da32bee

File tree

12 files changed

+264
-114
lines changed

12 files changed

+264
-114
lines changed

README.md

Lines changed: 59 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,76 @@
11
# FastAPI, Jinja2, PostgreSQL Webapp Template
22

3+
34
![Screenshot of homepage](docs/static/Screenshot.png)
45

5-
## Documentation
6+
## Quickstart
67

7-
This README provides a high-level overview. See the **[full documentation website](https://promptlytechnologies.com/fastapi-jinja2-postgres-webapp/docs/)** for more detailed information on installation, features, architecture, conventions and code style, customization, and deployment to cloud platforms.
8+
This quickstart guide provides a high-level overview. See the full
9+
documentation for comprehensive information on
10+
[features](https://promptlytechnologies.com/fastapi-jinja2-postgres-webapp/index.html),
11+
[installation](https://promptlytechnologies.com/fastapi-jinja2-postgres-webapp/docs/installation.html),
12+
[architecture](https://promptlytechnologies.com/fastapi-jinja2-postgres-webapp/docs/architecture.html),
13+
[conventions, code style, and
14+
customization](https://promptlytechnologies.com/fastapi-jinja2-postgres-webapp/docs/customization.html),
15+
[deployment to cloud
16+
platforms](https://promptlytechnologies.com/fastapi-jinja2-postgres-webapp/docs/deployment.html),
17+
and
18+
[contributing](https://promptlytechnologies.com/fastapi-jinja2-postgres-webapp/docs/contributing.html).
819

920
## Features
1021

11-
*FastAPI, Jinja2, PostgreSQL Webapp Template* combines three of the most lightweight and performant open-source web development frameworks in existence into a customizable webapp template with:
22+
This template combines three of the most lightweight and performant
23+
open-source web development frameworks into a customizable webapp
24+
template with:
1225

1326
- Pure Python backend
14-
- Low-Javascript frontend
15-
- Powerful, easy-to-manage database layer
27+
- Minimal-Javascript frontend
28+
- Powerful, easy-to-manage database
1629

1730
The template also includes full-featured secure auth with:
1831

1932
- Token-based authentication
2033
- Password recovery flow
2134
- Role-based access control system
2235

23-
The design philosophy of the template is to prefer low-level, best-in-class open-source frameworks that offer flexibility, scalability, and performance without vendor-lock-in. You'll find the template amazingly easy not only to understand and customize, but also to deploy to any major cloud hosting platform.
36+
## Design Philosophy
37+
38+
The design philosophy of the template is to prefer low-level,
39+
best-in-class open-source frameworks that offer flexibility,
40+
scalability, and performance without vendor-lock-in. You’ll find the
41+
template amazingly easy not only to understand and customize, but also
42+
to deploy to any major cloud hosting platform.
2443

25-
## Tech stack
44+
## Tech Stack
2645

2746
**Core frameworks:**
28-
- [FastAPI](https://fastapi.tiangolo.com/): scalable, high-performance, type-annotated Python web backend framework
29-
- [PostgreSQL](https://www.postgresql.org/): the world's most advanced open-source database engine
30-
- [Jinja2](https://jinja.palletsprojects.com/en/3.1.x/): frontend HTML templating engine
47+
48+
- [FastAPI](https://fastapi.tiangolo.com/): scalable, high-performance,
49+
type-annotated Python web backend framework
50+
- [PostgreSQL](https://www.postgresql.org/): the world’s most advanced
51+
open-source database engine
52+
- [Jinja2](https://jinja.palletsprojects.com/en/3.1.x/): frontend HTML
53+
templating engine
3154
- [SQLModel](https://sqlmodel.tiangolo.com/): easy-to-use Python ORM
3255

3356
**Additional technologies:**
57+
3458
- [Poetry](https://python-poetry.org/): Python dependency manager
3559
- [Pytest](https://docs.pytest.org/en/7.4.x/): testing framework
3660
- [Docker](https://www.docker.com/): development containerization
3761
- [Github Actions](https://docs.github.com/en/actions): CI/CD pipeline
38-
- [Quarto](https://quarto.org/docs/): simple documentation website renderer
39-
- [MyPy](https://mypy.readthedocs.io/en/stable/): static type checker for Python
62+
- [Quarto](https://quarto.org/docs/): simple documentation website
63+
renderer
64+
- [MyPy](https://mypy.readthedocs.io/en/stable/): static type checker
65+
for Python
4066
- [Bootstrap](https://getbootstrap.com/): HTML/CSS styler
41-
- [Resend](https://resend.com/): zero- or low-cost email service used for password recovery
67+
- [Resend](https://resend.com/): zero- or low-cost email service used
68+
for password recovery
4269

43-
## Quickstart
70+
## Installation
4471

45-
For comprehensive installation instructions, see the [documentation website](https://promptlytechnologies.com/fastapi-jinja2-postgres-webapp/docs/).
72+
For comprehensive installation instructions, see the [installation
73+
page](https://promptlytechnologies.com/fastapi-jinja2-postgres-webapp/docs/installation.html).
4674

4775
### Python and Docker
4876

@@ -87,17 +115,22 @@ poetry install
87115
poetry shell
88116
```
89117

90-
(Note: You will need to activate the shell every time you open a new terminal session. Alternatively, you can use the `poetry run` prefix before other commands to run them without activating the shell.)
118+
(Note: You will need to activate the shell every time you open a new
119+
terminal session. Alternatively, you can use the `poetry run` prefix
120+
before other commands to run them without activating the shell.)
91121

92122
### Set environment variables
93123

94124
Copy .env.example to .env with `cp .env.example .env`.
95125

96-
Generate a 256 bit secret key with `openssl rand -base64 32` and paste it into the .env file.
126+
Generate a 256 bit secret key with `openssl rand -base64 32` and paste
127+
it into the .env file.
97128

98129
Set your desired database name, username, and password in the .env file.
99130

100-
To use password recovery, register a [Resend](https://resend.com/) account, verify a domain, get an API key, and paste the API key into the .env file.
131+
To use password recovery, register a [Resend](https://resend.com/)
132+
account, verify a domain, get an API key, and paste the API key into the
133+
.env file.
101134

102135
### Start development database
103136

@@ -107,7 +140,8 @@ docker compose up -d
107140

108141
### Run the development server
109142

110-
Make sure the development database is running and tables and default permissions/roles are created first.
143+
Make sure the development database is running and tables and default
144+
permissions/roles are created first.
111145

112146
``` bash
113147
uvicorn main:app --host 0.0.0.0 --port 8000 --reload
@@ -123,8 +157,12 @@ mypy .
123157

124158
### Contributing
125159

126-
Your contributions are welcome! See the [issues page](https://github.com/promptly-technologies-llc/fastapi-jinja2-postgres-webapp/issues) for ideas. Fork the repository, create a new branch, make your changes, and submit a pull request.
160+
Your contributions are welcome! See the [issues
161+
page](https://github.com/promptly-technologies-llc/fastapi-jinja2-postgres-webapp/issues)
162+
for ideas. Fork the repository, create a new branch, make your changes,
163+
and submit a pull request.
127164

128165
### License
129166

130-
This project is licensed under the MIT License. See the LICENSE file for more details.
167+
This project is licensed under the MIT License. See the LICENSE file for
168+
more details.

_quarto.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ website:
66
title: "FastAPI Webapp Template"
77
navbar:
88
left:
9-
- href: docs/index.qmd
9+
- href: index.qmd
1010
text: Home
1111
- href: docs/architecture.qmd
1212
text: Architecture
13-
- href: docs/installation.qmd
14-
text: Installation
1513
- href: docs/authentication.qmd
1614
text: Authentication
15+
- href: docs/installation.qmd
16+
text: Installation
1717
- href: docs/customization.qmd
1818
text: Customization
1919
- href: docs/deployment.qmd

docs/architecture.qmd

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
title: "Architecture"
33
---
44

5-
## Architecture
5+
## Data flow
66

77
This application uses a Post-Redirect-Get (PRG) pattern. The user submits a form, which sends a POST request to a FastAPI endpoint on the server. The database is updated, and the user is redirected to a GET endpoint, which fetches the updated data and re-renders the Jinja2 page template with the new data.
88

@@ -12,18 +12,34 @@ This application uses a Post-Redirect-Get (PRG) pattern. The user submits a form
1212
from graphviz import Digraph
1313
1414
dot = Digraph()
15-
16-
dot.node('A', 'User submits form')
17-
dot.node('B', 'HTML/JS form validation')
18-
dot.node('C', 'Convert to Pydantic model')
19-
dot.node('D', 'Optional custom validation')
20-
dot.node('E', 'Update database')
21-
dot.node('F', 'Middleware error handler')
22-
dot.node('G', 'Render error template')
23-
dot.node('H', 'Redirect to GET endpoint')
24-
dot.node('I', 'Fetch updated data')
25-
dot.node('J', 'Re-render Jinja2 page template')
26-
15+
dot.attr(rankdir='TB')
16+
dot.attr('node', shape='box', style='rounded')
17+
18+
# Create client subgraph at top
19+
with dot.subgraph(name='cluster_client') as client:
20+
client.attr(label='Client')
21+
client.attr(rank='topmost')
22+
client.node('A', 'User submits form', fillcolor='lightblue', style='rounded,filled')
23+
client.node('B', 'HTML/JS form validation', fillcolor='lightblue', style='rounded,filled')
24+
25+
# Create server subgraph below
26+
with dot.subgraph(name='cluster_server') as server:
27+
server.attr(label='Server')
28+
server.node('C', 'Convert to Pydantic model', fillcolor='lightgreen', style='rounded,filled')
29+
server.node('D', 'Optional custom validation', fillcolor='lightgreen', style='rounded,filled')
30+
server.node('E', 'Update database', fillcolor='lightgreen', style='rounded,filled')
31+
server.node('F', 'Middleware error handler', fillcolor='lightgreen', style='rounded,filled')
32+
server.node('G', 'Render error template', fillcolor='lightgreen', style='rounded,filled')
33+
server.node('H', 'Redirect to GET endpoint', fillcolor='lightgreen', style='rounded,filled')
34+
server.node('I', 'Fetch updated data', fillcolor='lightgreen', style='rounded,filled')
35+
server.node('K', 'Re-render Jinja2 page template', fillcolor='lightgreen', style='rounded,filled')
36+
37+
with dot.subgraph(name='cluster_client_post') as client_post:
38+
client_post.attr(label='Client')
39+
client_post.attr(rank='bottommost')
40+
client_post.node('J', 'Display rendered page', fillcolor='lightblue', style='rounded,filled')
41+
42+
# Add visible edges
2743
dot.edge('A', 'B')
2844
dot.edge('B', 'A')
2945
dot.edge('B', 'C', label='POST Request to FastAPI endpoint')
@@ -33,12 +49,14 @@ dot.edge('D', 'E', label='Valid data')
3349
dot.edge('D', 'F', label='Custom Validation Error')
3450
dot.edge('E', 'H', label='Data updated')
3551
dot.edge('H', 'I')
36-
dot.edge('I', 'J')
52+
dot.edge('I', 'K')
53+
dot.edge('K', 'J', label='Return HTML')
3754
dot.edge('F', 'G')
55+
dot.edge('G', 'J', label='Return HTML')
3856
39-
dot.render('static/webapp_flow', format='png', cleanup=True)
57+
dot.render('static/data_flow', format='png', cleanup=True)
4058
```
4159

42-
![Webapp Flow](static/webapp_flow.png)
60+
![Data flow diagram](static/data_flow.png)
4361

4462
The advantage of the PRG pattern is that it is very straightforward to implement and keeps most of the rendering logic on the server side. The disadvantage is that it requires an extra round trip to the database to fetch the updated data, and re-rendering the entire page template may be less efficient than a partial page update on the client side.

docs/authentication.qmd

Lines changed: 26 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
title: "Authentication"
33
---
44

5-
## Authentication Flow
5+
## Security features
66

77
This template implements a comprehensive authentication system with security best practices:
88

@@ -30,9 +30,9 @@ This template implements a comprehensive authentication system with security bes
3030
- Security-related errors don't leak information
3131
- Comprehensive error logging
3232

33-
The diagrams below show the main authentication flows and security measures.
33+
The diagrams below show the main authentication flows.
3434

35-
### Registration and Login Flow
35+
### Registration and login flow
3636

3737
``` {python}
3838
#| echo: false
@@ -47,25 +47,25 @@ auth.attr('node', shape='box', style='rounded')
4747
# Client-side nodes
4848
with auth.subgraph(name='cluster_client') as client:
4949
client.attr(label='Client')
50-
client.node('register_form', 'Submit registration')
51-
client.node('login_form', 'Submit login')
52-
client.node('store_cookies', 'Store secure cookies')
50+
client.node('register_form', 'Submit registration', fillcolor='lightblue', style='rounded,filled')
51+
client.node('login_form', 'Submit login', fillcolor='lightblue', style='rounded,filled')
52+
client.node('store_cookies', 'Store secure cookies', fillcolor='lightblue', style='rounded,filled')
5353
5454
# Server-side nodes
5555
with auth.subgraph(name='cluster_server') as server:
5656
server.attr(label='Server')
5757
# Registration path
58-
server.node('validate_register', 'Validate registration data')
59-
server.node('hash_new', 'Hash new password')
60-
server.node('store_user', 'Store user in database')
58+
server.node('validate_register', 'Validate registration data', fillcolor='lightgreen', style='rounded,filled')
59+
server.node('hash_new', 'Hash new password', fillcolor='lightgreen', style='rounded,filled')
60+
server.node('store_user', 'Store user in database', fillcolor='lightgreen', style='rounded,filled')
6161
6262
# Login path
63-
server.node('validate_login', 'Validate login data')
64-
server.node('verify_password', 'Verify password hash')
65-
server.node('fetch_user', 'Fetch user from database')
63+
server.node('validate_login', 'Validate login data', fillcolor='lightgreen', style='rounded,filled')
64+
server.node('verify_password', 'Verify password hash', fillcolor='lightgreen', style='rounded,filled')
65+
server.node('fetch_user', 'Fetch user from database', fillcolor='lightgreen', style='rounded,filled')
6666
6767
# Common path
68-
server.node('generate_tokens', 'Generate JWT tokens')
68+
server.node('generate_tokens', 'Generate JWT tokens', fillcolor='lightgreen', style='rounded,filled')
6969
7070
# Registration path
7171
auth.edge('register_form', 'validate_register', 'POST /register')
@@ -85,9 +85,9 @@ auth.edge('generate_tokens', 'store_cookies', 'Set-Cookie')
8585
auth.render('static/auth_flow', format='png', cleanup=True)
8686
```
8787

88-
![Registration and Login Flow](static/auth_flow.png)
88+
![Registration and login flow](static/auth_flow.png)
8989

90-
### Password Reset Flow
90+
### Password reset flow
9191

9292
``` {python}
9393
#| echo: false
@@ -99,19 +99,17 @@ reset = Digraph(name='reset_flow')
9999
reset.attr(rankdir='TB')
100100
reset.attr('node', shape='box', style='rounded')
101101
102-
# Client-side nodes
103-
reset.attr(label='Client')
104-
reset.node('forgot', 'User submits forgot password form')
105-
reset.node('reset', 'User submits reset password form')
106-
reset.node('email_client', 'User clicks reset link')
102+
# Client-side nodes - using light blue fill
103+
reset.node('forgot', 'User submits forgot password form', fillcolor='lightblue', style='rounded,filled')
104+
reset.node('reset', 'User submits reset password form', fillcolor='lightblue', style='rounded,filled')
105+
reset.node('email_client', 'User clicks reset link', fillcolor='lightblue', style='rounded,filled')
107106
108-
# Server-side nodes
109-
reset.attr(label='Server')
110-
reset.node('validate', 'Validation')
111-
reset.node('token_gen', 'Generate reset token')
112-
reset.node('hash', 'Hash password')
113-
reset.node('email_server', 'Send email with Resend')
114-
reset.node('db', 'Database', shape='cylinder')
107+
# Server-side nodes - using light green fill
108+
reset.node('validate', 'Validation', fillcolor='lightgreen', style='rounded,filled')
109+
reset.node('token_gen', 'Generate reset token', fillcolor='lightgreen', style='rounded,filled')
110+
reset.node('hash', 'Hash password', fillcolor='lightgreen', style='rounded,filled')
111+
reset.node('email_server', 'Send email with Resend', fillcolor='lightgreen', style='rounded,filled')
112+
reset.node('db', 'Database', shape='cylinder', fillcolor='lightgreen', style='filled')
115113
116114
# Add edges with labels
117115
reset.edge('forgot', 'token_gen', 'POST')
@@ -126,4 +124,4 @@ reset.edge('hash', 'db', 'Update')
126124
reset.render('static/reset_flow', format='png', cleanup=True)
127125
```
128126

129-
![Password Reset Flow](static/reset_flow.png)
127+
![Password reset flow](static/reset_flow.png)

docs/contributing.qmd

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,17 @@ Fork the repository, create a new branch, make your changes, and submit a pull r
88

99
### Render the documentation
1010

11-
The documentation is rendered with [Quarto](https://quarto.org/docs/). Make changes to the `.qmd` files in the `docs` folder. Then run the following command to render:
11+
The README and documentation website are rendered with [Quarto](https://quarto.org/docs/). Make changes to the `.qmd` files in the root folder and the `docs` folder. Then run the following commands to render:
1212

1313
``` bash
14+
# To render the documentation website
1415
quarto render
16+
# To render the README
17+
quarto render index.qmd --output-dir . --output README.md --to gfm
1518
```
1619

20+
Due to a quirk of Quarto, an unnecessary `index.html` file is created in the root folder when the README is rendered. This file can be safely deleted.
21+
1722
## Maintainers
1823

1924
### Increment the version

0 commit comments

Comments
 (0)