Skip to content

Commit c7cf003

Browse files
committed
Inline d1 calls
1 parent 4318b4a commit c7cf003

2 files changed

Lines changed: 39 additions & 81 deletions

File tree

18-flask-todo-app/src/app.py

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
"""Flask JSON API for the notes app, backed directly by D1."""
22

3+
from asyncio import get_event_loop
4+
35
from flask import Flask, g, jsonify, request
46
from werkzeug.exceptions import HTTPException
57

6-
from d1 import D1, D1Error
7-
88
app = Flask(__name__)
99
# Preserve our own key ordering in JSON responses rather than sorting them.
1010
app.json.sort_keys = False
@@ -14,14 +14,27 @@
1414
VALID_FILTERS = ("all", "active", "done")
1515

1616

17-
def get_db() -> D1:
17+
class D1Error(RuntimeError):
18+
"""A D1 query failed. Wraps the underlying error message."""
19+
20+
21+
def get_db():
1822
"""Return the D1 wrapper for the current request."""
1923
if "db" not in g:
2024
# Cache on g for the lifetime of the request
21-
g.db = D1(request.environ["workers.env"].DB)
25+
g.db = request.environ["workers.env"].DB
2226
return g.db
2327

2428

29+
def run_db(task):
30+
try:
31+
return get_event_loop().run_until_complete(task)
32+
except Exception as exc:
33+
# D1 surfaces SQL errors as JS exceptions; re-raise as a Python error so
34+
# we can register a custom error handler.
35+
raise D1Error(f"{exc}") from exc
36+
37+
2538
# --------------------------------------------------------------------------
2639
# Serialization / validation
2740
# --------------------------------------------------------------------------
@@ -117,13 +130,15 @@ def list_notes():
117130

118131
sql = sql.format(where=where)
119132

120-
rows = get_db().query(sql, params)
121-
return jsonify({"notes": [serialize(r) for r in rows]})
133+
rows = run_db(get_db().prepare(sql).bind(*params).all())["results"]
134+
return jsonify({"notes": [serialize(dict(r)) for r in rows]})
122135

123136

124137
@app.get("/api/notes/<int:note_id>")
125138
def get_note(note_id: int):
126-
row = get_db().first("SELECT * FROM notes WHERE id = ?", (note_id,))
139+
row = run_db(
140+
get_db().prepare("SELECT * FROM notes WHERE id = ?").bind(note_id).first()
141+
)
127142
if row is None:
128143
return jsonify({"error": "Note not found."}), 404
129144
return jsonify(serialize(row))
@@ -138,9 +153,13 @@ def create_note():
138153
body = _clean_body(data.get("body", ""))
139154

140155
# RETURNING to insert and read the stored row in a single round trip.
141-
row = get_db().first(
142-
"INSERT INTO notes (title, body) VALUES (?, ?) RETURNING *",
143-
(title, body),
156+
row = run_db(
157+
get_db()
158+
.prepare(
159+
"INSERT INTO notes (title, body) VALUES (?, ?) RETURNING *",
160+
)
161+
.bind(title, body)
162+
.first()
144163
)
145164
return jsonify(serialize(row)), 201
146165

@@ -169,9 +188,11 @@ def update_note(note_id: int):
169188
# The f-string interpolates the updates fragments, every one of which is a
170189
# hardcoded literal from the branches above so this expands to one of seven
171190
# fixed statements. Values are not interpolated.
172-
row = get_db().first(
173-
f"UPDATE notes SET {', '.join(updates)} WHERE id = ? RETURNING *",
174-
params,
191+
row = run_db(
192+
get_db()
193+
.prepare(f"UPDATE notes SET {', '.join(updates)} WHERE id = ? RETURNING *")
194+
.bind(*params)
195+
.first()
175196
)
176197
if row is None:
177198
return jsonify({"error": "Note not found."}), 404
@@ -180,7 +201,9 @@ def update_note(note_id: int):
180201

181202
@app.delete("/api/notes/<int:note_id>")
182203
def delete_note(note_id: int):
183-
meta = get_db().run("DELETE FROM notes WHERE id = ?", (note_id,))
204+
meta = run_db(
205+
get_db().prepare("DELETE FROM notes WHERE id = ?").bind(note_id).run()
206+
)["meta"]
184207
if not meta.get("changes"):
185208
return jsonify({"error": "Note not found."}), 404
186209
return "", 204
@@ -189,14 +212,14 @@ def delete_note(note_id: int):
189212
@app.delete("/api/notes/delete_completed")
190213
def delete_completed():
191214
"""Bulk-delete every completed note."""
192-
meta = get_db().run("DELETE FROM notes WHERE done = 1")
215+
meta = run_db(get_db().prepare("DELETE FROM notes WHERE done = 1").run())["meta"]
193216
return jsonify({"deleted": meta.get("changes", 0)})
194217

195218

196219
@app.get("/api/health")
197220
def health():
198221
"""Confirms the Worker is up and the D1 binding actually responds."""
199-
get_db().first("SELECT 1 AS ok")
222+
run_db(get_db().prepare("SELECT 1 AS ok").first())
200223
return jsonify({"status": "ok"})
201224

202225

18-flask-todo-app/src/d1.py

Lines changed: 0 additions & 65 deletions
This file was deleted.

0 commit comments

Comments
 (0)