You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -61,26 +61,32 @@ One use case for this file, if using the Cursor IDE, is to rename it to `.cursor
61
61
62
62
We have also exposed the full Markdown-formatted project documentation as a [single text file](static/documentation.txt) for easy downloading and embedding for RAG workflows.
63
63
64
-
## Project structure
64
+
## Application architecture
65
65
66
-
### Customizable folders and files
66
+
### Post-Redirect-Get pattern
67
+
68
+
In this template, we use FastAPI to define the "API endpoints" of our application. An API endpoint is simply a URL that accepts user requests and returns responses. When a user visits a page, their browser sends what's called a "GET" request to an endpoint, and the server processes it (often querying a database), and returns a response (typically HTML). The browser renders the HTML, displaying the page.
69
+
70
+
We also create POST endpoints, which accept form submissions so the user can create, update, and delete data in the database. This template follows the Post-Redirect-Get (PRG) pattern to handle POST requests. When a form is submitted, the server processes the data and then returns a "redirect" response, which sends the user to a GET endpoint to re-render the page with the updated data. (See [Architecture](https://promptlytechnologies.com/fastapi-jinja2-postgres-webapp/docs/architecture.html) for more details.)
71
+
72
+
#### Customizable folders and files
67
73
68
74
- FastAPI application entry point and GET routes: `main.py`
69
75
- FastAPI POST routes: `routers/`
70
-
- User authentication endpoints: `auth.py`
76
+
- User authentication endpoints: `authentication.py`
- Test database configuration: `docker-compose.yml`
79
84
- Helper functions: `utils/`
80
85
- Auth helpers: `auth.py`
81
86
- Database helpers: `db.py`
82
87
- Database models: `models.py`
83
-
- Environment variables: `.env`
88
+
- Image helpers: `images.py`
89
+
- Environment variables: `.env.example`
84
90
- CI/CD configuration: `.github/`
85
91
- Project configuration: `pyproject.toml`
86
92
- Quarto documentation:
@@ -89,15 +95,11 @@ We have also exposed the full Markdown-formatted project documentation as a [sin
89
95
90
96
Most everything else is auto-generated and should not be manually modified.
91
97
92
-
### Defining a web backend with FastAPI
98
+
##Backend
93
99
94
-
We use FastAPI to define the "API endpoints" of our application. An API endpoint is simply a URL that accepts user requests and returns responses. When a user visits a page, their browser sends what's called a "GET" request to an endpoint, and the server processes it (often querying a database), and returns a response (typically HTML). The browser renders the HTML, displaying the page.
100
+
### Code conventions
95
101
96
-
We also create POST endpoints, which accept form submissions so the user can create, update, and delete data in the database. This template follows the Post-Redirect-Get (PRG) pattern to handle POST requests. When a form is submitted, the server processes the data and then returns a "redirect" response, which sends the user to a GET endpoint to re-render the page with the updated data. (See [Architecture](https://promptlytechnologies.com/fastapi-jinja2-postgres-webapp/docs/architecture.html) for more details.)
97
-
98
-
#### Routing patterns in this template
99
-
100
-
In this template, GET routes are defined in the main entry point for the application, `main.py`. POST routes are organized into separate modules within the `routers/` directory.
102
+
GET routes are defined in the main entry point for the application, `main.py`. POST routes are organized into separate modules within the `routers/` directory.
101
103
102
104
We name our GET routes using the convention `read_<name>`, where `<name>` is the name of the page, to indicate that they are read-only endpoints that do not modify the database.
103
105
@@ -111,108 +113,7 @@ Some of our routes take request parameters, which we pass as keyword arguments t
111
113
112
114
Some parameters are shared across all authenticated or unauthenticated routes, so we define them in the `common_authenticated_parameters` and `common_unauthenticated_parameters` dependencies defined in `main.py`.
113
115
114
-
### HTML templating with Jinja2
115
-
116
-
To generate the HTML pages to be returned from our GET routes, we use Jinja2 templates. Jinja2's hierarchical templates allow creating a base template (`templates/base.html`) that defines the overall layout of our web pages (e.g., where the header, body, and footer should go). Individual pages can then extend this base template. We can also template reusable components that can be injected into our layout or page templates.
117
-
118
-
With Jinja2, we can use the `{% block %}` tag to define content blocks, and the `{% extends %}` tag to extend a base template. We can also use the `{% include %}` tag to include a component in a parent template. See the [Jinja2 documentation on template inheritance](https://jinja.palletsprojects.com/en/stable/templates/#template-inheritance) for more details.
119
-
120
-
### Custom theming with Bootstrap Sass
121
-
122
-
[Install Node.js](https://nodejs.org/en/download/) on your local machine if it is not there already.
123
-
124
-
Install `bootstrap`, `sass`, `gulp`, and `gulp-sass` in your project:
This will create a `node_modules` folder, a `package-lock.json` file, and a `package.json` file in the root directory of the project.
131
-
132
-
Create an `scss` folder and a basic `scss/styles.scss` file:
133
-
134
-
```bash
135
-
mkdir scss
136
-
touch scss/styles.scss
137
-
```
138
-
139
-
Your custom styles will go in `scss/styles.scss`, along with `@import` statements to include the Bootstrap components you want. For example, the default CSS for the template was compiled from the following configuration, which imports all of Bootstrap and overrides the `$theme-colors` and `$font-family-base` variables:
140
-
141
-
```scss
142
-
// styles.scss
143
-
144
-
// Include any default variable overrides here (functions won't be available)
The most common use case for `styles.scss` is to define a custom color scheme and fonts, but it's also possible to other visual details such as border radius and box shadow depth. See the [Bootstrap Sass customization instructions](https://getbootstrap.com/docs/5.3/customize/sass/) and the many free templates available at [Start Bootstrap](https://startbootstrap.com) for examples.
189
-
190
-
To compile the Sass files, we use `gulp`. In the project root directory, create a `gulpfile.js` file with the following content:
.pipe(gulp.dest('static/css')); // Destination folder for compiled CSS
201
-
});
202
-
203
-
// Define a default task
204
-
gulp.task('default', gulp.series('sass'));
205
-
```
206
-
207
-
To compile the Sass file to `static/css`, run this command:
208
-
209
-
```bash
210
-
npx gulp
211
-
```
212
-
213
-
Note that this will overwrite the existing `static/css/styles.css` file, so if you want to define any custom CSS styles, you should do so in either the `scss/styles.scss` file or in `static/css/extras.css`.
214
-
215
-
#### Context variables
116
+
### Context variables
216
117
217
118
Context refers to Python variables passed to a template to populate the HTML. In a FastAPI GET route, we can pass context to a template using the `templates.TemplateResponse` method, which takes the request and any context data as arguments. For example:
In this example, the `welcome.html` template will receive two pieces of context: the user's `request`, which is always passed automatically by FastAPI, and a `username` variable, which we specify as "Alice". We can then use the `{{{ username }}}` syntax in the `welcome.html` template (or any of its parent or child templates) to insert the value into the HTML.
229
130
230
-
#### Form validation strategy
231
-
232
-
While this template includes comprehensive server-side validation through Pydantic models and custom validators, it's important to note that server-side validation should be treated as a fallback security measure. If users ever see the `validation_error.html` template, it indicates that our client-side validation has failed to catch invalid input before it reaches the server.
233
-
234
-
Best practices dictate implementing thorough client-side validation via JavaScript and/or HTML `input` element `pattern` attributes to:
235
-
236
-
- Provide immediate feedback to users
237
-
- Reduce server load
238
-
- Improve user experience by avoiding round-trips to the server
239
-
- Prevent malformed data from ever reaching the backend
240
-
241
-
Server-side validation remains essential as a security measure against malicious requests that bypass client-side validation, but it should rarely be encountered during normal user interaction. See `templates/authentication/register.html` for a client-side form validation example involving both JavaScript and HTML regex `pattern` matching.
242
-
243
-
#### Email templating
131
+
### Email templating
244
132
245
133
Password reset and other transactional emails are also handled through Jinja2 templates, located in the `templates/emails` directory. The email templates follow the same inheritance pattern as web templates, with `base_email.html` providing the common layout and styling.
246
134
@@ -250,7 +138,7 @@ Here's how the default password reset email template looks:
250
138
251
139
The email templates use inline CSS styles to ensure consistent rendering across email clients. Like web templates, they can receive context variables from the Python code (such as `reset_url` in the password reset template).
252
140
253
-
### Writing type annotated code
141
+
### Server-side form validation
254
142
255
143
Pydantic is used for data validation and serialization. It ensures that the data received in requests meets the expected format and constraints. Pydantic models are used to define the structure of request and response data, making it easy to validate and parse JSON payloads.
256
144
@@ -324,7 +212,7 @@ class UserRegister(BaseModel):
324
212
)
325
213
```
326
214
327
-
####Middleware exception handling
215
+
### Middleware exception handling
328
216
329
217
Middlewares—which process requests before they reach the route handlers and responses before they are sent back to the client—are defined in `main.py`. They are commonly used in web development for tasks such as error handling, authentication token validation, logging, and modifying request/response objects.
###Database configuration and access with SQLModel
239
+
## Database configuration and access with SQLModel
352
240
353
241
SQLModel is an Object-Relational Mapping (ORM) library that allows us to interact with our PostgreSQL database using Python classes instead of writing raw SQL. It combines the features of SQLAlchemy (a powerful database toolkit) with Pydantic's data validation.
354
242
355
-
####Models and relationships
243
+
### Models and relationships
356
244
357
245
Our database models are defined in `utils/models.py`. Each model is a Python class that inherits from `SQLModel` and represents a database table. The key models are:
You should create custom `ValidPermissions` enum values for your application and validate that users have the necessary permissions before allowing them to modify organization data resources.
429
317
430
-
####Cascade deletes
318
+
### Cascade deletes
431
319
432
320
Cascade deletes (in which deleting a record from one table deletes related records from another table) can be handled at either the ORM level or the database level. This template handles cascade deletes at the ORM level, via SQLModel relationships. Inside a SQLModel `Relationship`, we set:
433
321
@@ -453,3 +341,125 @@ for role in session.exec(select(Role)).all():
453
341
```
454
342
455
343
This is slower than deleting the records directly, but it makes [many-to-many relationships](https://sqlmodel.tiangolo.com/tutorial/many-to-many/create-models-with-link/#create-the-tables) much easier to manage.
344
+
345
+
## Frontend
346
+
347
+
### HTML templating with Jinja2
348
+
349
+
To generate the HTML pages to be returned from our GET routes, we use Jinja2 templates. Jinja2's hierarchical templates allow creating a base template (`templates/base.html`) that defines the overall layout of our web pages (e.g., where the header, body, and footer should go). Individual pages can then extend this base template. We can also template reusable components that can be injected into our layout or page templates.
350
+
351
+
With Jinja2, we can use the `{% block %}` tag to define content blocks, and the `{% extends %}` tag to extend a base template. We can also use the `{% include %}` tag to include a component in a parent template. See the [Jinja2 documentation on template inheritance](https://jinja.palletsprojects.com/en/stable/templates/#template-inheritance) for more details.
352
+
353
+
### Custom theming with Bootstrap
354
+
355
+
[Install Node.js](https://nodejs.org/en/download/) on your local machine if it is not there already.
356
+
357
+
Install `bootstrap`, `sass`, `gulp`, and `gulp-sass` in your project:
This will create a `node_modules` folder, a `package-lock.json` file, and a `package.json` file in the root directory of the project.
364
+
365
+
Create an `scss` folder and a basic `scss/styles.scss` file:
366
+
367
+
```bash
368
+
mkdir scss
369
+
touch scss/styles.scss
370
+
```
371
+
372
+
Your custom styles will go in `scss/styles.scss`, along with `@import` statements to include the Bootstrap components you want.
373
+
374
+
#### Customizing the Bootstrap SCSS
375
+
376
+
The default CSS for the template was compiled from the following `scss/styles.scss` configuration, which imports all of Bootstrap and overrides the `$theme-colors` and `$font-family-base` variables:
377
+
378
+
```scss
379
+
// styles.scss
380
+
381
+
// Include any default variable overrides here (functions won't be available)
The most common use case for `styles.scss` is to define a custom color scheme and fonts, but it's also possible to customize some other visual details such as border radius and box shadow depth. See the [Bootstrap Sass customization documentation](https://getbootstrap.com/docs/5.3/customize/sass/) and the many free templates available at [Start Bootstrap](https://startbootstrap.com) for examples.
426
+
427
+
#### Compiling the SCSS to CSS
428
+
429
+
To compile the SCSS files to CSS, we use `gulp`. In the project root directory, create a `gulpfile.js` file with the following content:
.pipe(gulp.dest('static/css')); // Destination folder for compiled CSS
440
+
});
441
+
442
+
// Define a default task
443
+
gulp.task('default', gulp.series('sass'));
444
+
```
445
+
446
+
To compile the SCSS file to `static/css`, run this command:
447
+
448
+
```bash
449
+
npx gulp
450
+
```
451
+
452
+
Note that this will overwrite the existing `static/css/styles.css` file, so if you want to define any custom CSS styles, you should do so in either the `scss/styles.scss` file or in `static/css/extras.css`.
453
+
454
+
### Client-side form validation
455
+
456
+
While this template includes comprehensive server-side validation through Pydantic models and custom validators, it's important to note that server-side validation should be treated as a fallback security measure. If users ever see the `validation_error.html` template, it indicates that our client-side validation has failed to catch invalid input before it reaches the server.
457
+
458
+
Best practices dictate implementing thorough client-side validation via JavaScript and/or HTML `input` element `pattern` attributes to:
459
+
460
+
- Provide immediate feedback to users
461
+
- Reduce server load
462
+
- Improve user experience by avoiding round-trips to the server
463
+
- Prevent malformed data from ever reaching the backend
464
+
465
+
Server-side validation remains essential as a security measure against malicious requests that bypass client-side validation, but it should rarely be encountered during normal user interaction. See `templates/authentication/register.html` for a client-side form validation example involving both JavaScript and HTML regex `pattern` matching.
0 commit comments