refactor(categories): adopt Kysely for the /groups query (real production use)
Proves the POC in production: the category-groups list is now built with the typed Kysely builder (category_groups added to the schema; COLLATE NOCASE via sql``) and executed synchronously via all(). Byte-identical behavior — categoryGroups test 4/4, suite 248/248, typecheck:server clean. Establishes the migration recipe for rolling typed SQL out query-by-query. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
35a0e4262e
commit
d9545d6fd5
|
|
@ -27,8 +27,19 @@ export interface CategoriesTable {
|
|||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface CategoryGroupsTable {
|
||||
id: number;
|
||||
user_id: number;
|
||||
name: string;
|
||||
sort_order: number;
|
||||
is_seeded: number;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface Database {
|
||||
categories: CategoriesTable;
|
||||
category_groups: CategoryGroupsTable;
|
||||
}
|
||||
|
||||
// Build-only Kysely instance. The dialect supplies the SQLite compiler; its driver
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ const { ValidationError, NotFoundError, ConflictError } = require('../utils/apiE
|
|||
const router = express.Router();
|
||||
const { getDb, ensureUserDefaultCategories } = require('../db/database.cts');
|
||||
const { accountingActiveSql } = require('../services/paymentAccountingService.cts');
|
||||
const { kysely, all } = require('../db/kysely.cts');
|
||||
const { sql } = require('kysely');
|
||||
|
||||
// Maps a SQLite UNIQUE-constraint violation to a friendly 409; re-throws anything
|
||||
// else so the terminal handler returns a safe 500.
|
||||
|
|
@ -198,19 +200,16 @@ router.put('/:id', (req: Req, res: Res) => {
|
|||
|
||||
// ── Category groups ──────────────────────────────────────────────────────────
|
||||
|
||||
// GET /api/category-groups
|
||||
// GET /api/category-groups — typed via Kysely (built, then run synchronously).
|
||||
router.get('/groups', (req: Req, res: Res) => {
|
||||
const db = getDb();
|
||||
const groups = db
|
||||
.prepare(
|
||||
`
|
||||
SELECT id, name, sort_order, created_at, updated_at
|
||||
FROM category_groups
|
||||
WHERE user_id = ?
|
||||
ORDER BY sort_order ASC, name COLLATE NOCASE ASC
|
||||
`,
|
||||
)
|
||||
.all(req.user.id);
|
||||
const groups = all(
|
||||
kysely()
|
||||
.selectFrom('category_groups')
|
||||
.select(['id', 'name', 'sort_order', 'created_at', 'updated_at'])
|
||||
.where('user_id', '=', req.user.id)
|
||||
.orderBy('sort_order', 'asc')
|
||||
.orderBy(sql`name collate nocase`, 'asc'),
|
||||
);
|
||||
res.json(groups);
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue