Skip to content

Commit dbcc1e0

Browse files
committed
document setup, session & pagination
1 parent 14fd2ee commit dbcc1e0

File tree

6 files changed

+247
-32
lines changed

6 files changed

+247
-32
lines changed

README.md

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@ _Async SQLAlchemy 2 for FastAPI — boilerplate, pagination, and seamless sessio
88
[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-brightgreen.svg)](https://conventionalcommits.org)
99
[![GitHub License](https://img.shields.io/github/license/hadrien/fastsqla)](https://github.com/hadrien/FastSQLA/blob/main/LICENSE)
1010

11-
**Documentation**: https://hadrien.github.io/FastSQLA/
11+
**Documentation**: [https://hadrien.github.io/FastSQLA/](https://hadrien.github.io/FastSQLA/)
12+
13+
**Github Repo:** [https://github.com/hadrien/fastsqla](https://github.com/hadrien/fastsqla)
14+
15+
-----------------------------------------------------------------------------------------
1216

1317
`FastSQLA` is an [`SQLAlchemy 2`](https://docs.sqlalchemy.org/en/20/) extension for
1418
[`FastAPI`](https://fastapi.tiangolo.com/).
@@ -147,11 +151,13 @@ class Hero(Base):
147151
id: Mapped[int] = mapped_column(primary_key=True)
148152
name: Mapped[str] = mapped_column(unique=True)
149153
secret_identity: Mapped[str]
154+
age: Mapped[int]
150155

151156

152157
class HeroBase(BaseModel):
153158
name: str
154159
secret_identity: str
160+
age: int
155161

156162

157163
class HeroModel(HeroBase):
@@ -160,21 +166,21 @@ class HeroModel(HeroBase):
160166

161167

162168
@app.get("/heros", response_model=Page[HeroModel])
163-
async def list_users(paginate: Paginate):
169+
async def list_heros(paginate: Paginate):
164170
stmt = select(Hero)
165171
return await paginate(stmt)
166172

167173

168174
@app.get("/heros/{hero_id}", response_model=Item[HeroModel])
169-
async def get_user(hero_id: int, session: Session):
175+
async def get_hero(hero_id: int, session: Session):
170176
hero = await session.get(Hero, hero_id)
171177
if hero is None:
172178
raise HTTPException(HTTPStatus.NOT_FOUND, "Hero not found")
173179
return {"data": hero}
174180

175181

176182
@app.post("/heros", response_model=Item[HeroModel])
177-
async def create_user(new_hero: HeroBase, session: Session):
183+
async def create_hero(new_hero: HeroBase, session: Session):
178184
hero = Hero(**new_hero.model_dump())
179185
session.add(hero)
180186
try:
@@ -197,22 +203,23 @@ sqlite3 db.sqlite <<EOF
197203
CREATE TABLE hero (
198204
id INTEGER PRIMARY KEY AUTOINCREMENT,
199205
name TEXT NOT NULL UNIQUE, -- Unique hero name (e.g., Superman)
200-
secret_identity TEXT NOT NULL -- Secret identity (e.g., Clark Kent)
206+
secret_identity TEXT NOT NULL, -- Secret identity (e.g., Clark Kent)
207+
age INTEGER NOT NULL -- Age of the hero (e.g., 30)
201208
);
202209
203-
-- Insert heroes with their name and secret identity
204-
INSERT INTO hero (name, secret_identity) VALUES ('Superman', 'Clark Kent');
205-
INSERT INTO hero (name, secret_identity) VALUES ('Batman', 'Bruce Wayne');
206-
INSERT INTO hero (name, secret_identity) VALUES ('Wonder Woman', 'Diana Prince');
207-
INSERT INTO hero (name, secret_identity) VALUES ('Iron Man', 'Tony Stark');
208-
INSERT INTO hero (name, secret_identity) VALUES ('Spider-Man', 'Peter Parker');
209-
INSERT INTO hero (name, secret_identity) VALUES ('Captain America', 'Steve Rogers');
210-
INSERT INTO hero (name, secret_identity) VALUES ('Black Widow', 'Natasha Romanoff');
211-
INSERT INTO hero (name, secret_identity) VALUES ('Thor', 'Thor Odinson');
212-
INSERT INTO hero (name, secret_identity) VALUES ('Scarlet Witch', 'Wanda Maximoff');
213-
INSERT INTO hero (name, secret_identity) VALUES ('Doctor Strange', 'Stephen Strange');
214-
INSERT INTO hero (name, secret_identity) VALUES ('The Flash', 'Barry Allen');
215-
INSERT INTO hero (name, secret_identity) VALUES ('Green Lantern', 'Hal Jordan');
210+
-- Insert heroes with their name, secret identity, and age
211+
INSERT INTO hero (name, secret_identity, age) VALUES ('Superman', 'Clark Kent', 30);
212+
INSERT INTO hero (name, secret_identity, age) VALUES ('Batman', 'Bruce Wayne', 35);
213+
INSERT INTO hero (name, secret_identity, age) VALUES ('Wonder Woman', 'Diana Prince', 30);
214+
INSERT INTO hero (name, secret_identity, age) VALUES ('Iron Man', 'Tony Stark', 45);
215+
INSERT INTO hero (name, secret_identity, age) VALUES ('Spider-Man', 'Peter Parker', 25);
216+
INSERT INTO hero (name, secret_identity, age) VALUES ('Captain America', 'Steve Rogers', 100);
217+
INSERT INTO hero (name, secret_identity, age) VALUES ('Black Widow', 'Natasha Romanoff', 35);
218+
INSERT INTO hero (name, secret_identity, age) VALUES ('Thor', 'Thor Odinson', 1500);
219+
INSERT INTO hero (name, secret_identity, age) VALUES ('Scarlet Witch', 'Wanda Maximoff', 30);
220+
INSERT INTO hero (name, secret_identity, age) VALUES ('Doctor Strange', 'Stephen Strange', 40);
221+
INSERT INTO hero (name, secret_identity, age) VALUES ('The Flash', 'Barry Allen', 28);
222+
INSERT INTO hero (name, secret_identity, age) VALUES ('Green Lantern', 'Hal Jordan', 35);
216223
EOF
217224
```
218225

docs/pagination.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Pagination
2+
3+
## `fastsqla.Paginate[T]`
4+
5+
::: fastsqla.Paginate
6+
options:
7+
heading_level: false
8+
show_source: false

docs/session.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# `SQLAlchemy` Session
2+
3+
## Lifecycle
4+
5+
[`SQLAlchemy` documentation](https://docs.sqlalchemy.org/en/20/orm/session_basics.html#when-do-i-construct-a-session-when-do-i-commit-it-and-when-do-i-close-it)
6+
recommends the following:
7+
8+
* Keep the lifecycle of the session **separate and external** from functions and
9+
objects that access and/or manipulate database data.
10+
* Make sure you have a clear notion of where transactions begin and end, and keep
11+
transactions **short**, meaning, they end at the series of a sequence of operations,
12+
instead of being held open indefinitely.
13+
14+
`FastSQLA` automatically manages the session lifecycle:
15+
16+
* If the request is successful, the session is committed.
17+
* If the request fails, the session is rolled back.
18+
* In all cases, at the end of the request, the session is closed and the associated
19+
connection is returned to the connection pool.
20+
21+
22+
To learn more about `SQLAlchemy` sessions:
23+
24+
* [Session Basics](https://docs.sqlalchemy.org/en/20/orm/session_basics.html#)
25+
26+
27+
## Session dependency
28+
29+
::: fastsqla.Session
30+
options:
31+
heading_level: false
32+
show_source: false
33+
34+
## Session context manager
35+
36+
::: fastsqla.open_session
37+
options:
38+
heading_level: false
39+
show_source: false
Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,22 @@
1-
# Configuration
1+
# Setup
22

3-
Configuration is exclusively done via environment variables to follow the
3+
## `fastsqla.lifespan`
4+
5+
::: fastsqla.lifespan
6+
options:
7+
heading_level: false
8+
show_source: false
9+
10+
## Configuration
11+
12+
Configuration is done exclusively via environment variables, adhering to the
413
[**Twelve-Factor App methodology**](https://12factor.net/config).
514

6-
The only required key is **`SQLALCHEMY_URL`**, which specifies the database URL and
7-
supports embedding specific database driver parameters in the query string.
15+
The only required key is **`SQLALCHEMY_URL`**, which defines the database URL. It
16+
specifies the database driver in the URL's scheme and allows embedding driver parameters
17+
in the query string. Example:
18+
19+
sqlite+aiosqlite:////tmp/test.db?check_same_thread=false
820

921
All parameters of [`sqlalchemy.create_engine`][] can be configured by setting environment
1022
variables, with each parameter name prefixed by **`SQLALCHEMY_`**.
@@ -14,14 +26,14 @@ variables, with each parameter name prefixed by **`SQLALCHEMY_`**.
1426
FastSQLA is **case-insensitive** when reading environment variables, so parameter
1527
names prefixed with **`SQLALCHEMY_`** can be provided in any letter case.
1628

17-
## Examples
29+
### Examples
1830

1931
1. :simple-postgresql: PostgreSQL url using
2032
[`asyncpg`][sqlalchemy.dialects.postgresql.asyncpg] driver with a
2133
[`pool_recycle`][sqlalchemy.create_engine.params.pool_recycle] of 30 minutes:
2234

2335
```bash
24-
export SQLALCHEMY_URL=postgresql+asyncpg://postgres@localhost
36+
export SQLALCHEMY_URL=postgresql+asyncpg://postgres@localhost/postgres
2537
export SQLALCHEMY_POOL_RECYCLE=1800
2638
```
2739

@@ -30,7 +42,7 @@ variables, with each parameter name prefixed by **`SQLALCHEMY_`**.
3042
[`pool_size`][sqlalchemy.create_engine.params.pool_size] of 50:
3143

3244
```bash
33-
export sqlalchemy_url=sqlite+aiosqlite:////tmp/test.db?check_same_thread=false
45+
export sqlalchemy_url=sqlite+aiosqlite:///tmp/test.db?check_same_thread=false
3446
export sqlalchemy_pool_size=10
3547
```
3648

@@ -40,4 +52,4 @@ variables, with each parameter name prefixed by **`SQLALCHEMY_`**.
4052
```bash
4153
export sqlalchemy_url=mysql+aiomysql://bob:password!@db.example.com/app
4254
export sqlalchemy_echo=true
43-
```
55+
```

mkdocs.yml

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,18 @@ edit_uri: edit/main/docs/
77
nav:
88
- Get Started:
99
- Welcome to FastSQLA: index.md
10-
- Configuration: configuration.md
11-
- Changelog: changelog.md
10+
- Usage:
11+
- Setup: setup.md
12+
- SQLAlchemy Session: session.md
13+
- Pagination: pagination.md
14+
- Changelog: changelog.md
1215

1316
theme:
1417
favicon: images/favicon.png
1518
icon:
1619
logo: material/database
1720
name: material
1821
features:
19-
- admonition
2022
- announce.dismiss
2123
- content.code.annotate
2224
- content.code.copy
@@ -63,6 +65,7 @@ plugins:
6365
- search
6466

6567
markdown_extensions:
68+
- abbr
6669
- admonition
6770
- attr_list
6871
- md_in_html
@@ -71,7 +74,16 @@ markdown_extensions:
7174
- pymdownx.emoji:
7275
emoji_index: !!python/name:material.extensions.emoji.twemoji
7376
emoji_generator: !!python/name:material.extensions.emoji.to_svg
77+
- pymdownx.highlight:
78+
anchor_linenums: true
79+
line_spans: __span
80+
pygments_lang_class: true
81+
- pymdownx.inlinehilite
82+
- pymdownx.snippets
7483
- pymdownx.superfences
84+
7585
- toc:
7686
permalink: true
77-
87+
watch:
88+
- docs
89+
- src

0 commit comments

Comments
 (0)