REST API with Express
Build a complete REST API with pagination, filtering, optimistic locking, and bulk operations
Setup
Initialize the ORM and Express app:
server.ts
import express from "express";import { Stabilize, defineModel, DataTypes, DBType, generateUUID } from "stabilize-orm";
const dbConfig = { type: DBType.SQLite, connectionString: "./data/api.db",};
const orm = new Stabilize(dbConfig);const app = express();app.use(express.json());Paginated List Endpoint
List tasks with filtering, search, and pagination:
routes/tasks.ts
app.get("/api/tasks", async (req, res) => { try { const { status, priority, assigneeId, page = "1", pageSize = "20", search } = req.query; const qb = taskRepo.find();
if (status) qb.where("status = ?", status); if (priority) qb.where("priority = ?", priority); if (assigneeId) qb.where("assigneeId = ?", assigneeId); if (search) qb.whereLike("title", `%${search}%`);
qb.orderBy("createdAt", "DESC");
const data = await qb.paginate(Number(page), Number(pageSize)).execute(orm.client); const total = await qb.clone().countExec(orm.client);
res.json({ data, total, page: Number(page), pageSize: Number(pageSize) }); } catch (err: any) { res.status(500).json({ error: err.message }); }});Optimistic Locking
Handle concurrent updates with optimistic locking:
routes/tasks-patch.ts
app.patch("/api/tasks/:id", async (req, res) => { try { const task = await taskRepo.findOneBy({ id: req.params.id }); if (!task) return res.status(404).json({ error: "Not found" });
const version = req.body.version; if (version !== undefined) { try { const updated = await taskRepo.update(req.params.id, { ...req.body, version: version, }); res.json(updated); } catch (err: any) { if (err.code === "CONCURRENT_MODIFICATION") { return res.status(409).json({ error: "Conflict: record was modified" }); } throw err; } } else { const updated = await taskRepo.update(req.params.id, req.body); res.json(updated); } } catch (err: any) { res.status(400).json({ error: err.message }); }});Bulk Operations
Bulk create and delete endpoints:
routes/tasks-bulk.ts
app.post("/api/tasks/bulk", async (req, res) => { try { const tasks = req.body.map((t: any) => ({ id: generateUUID(), ...t })); const created = await taskRepo.bulkCreate(tasks); res.status(201).json(created); } catch (err: any) { res.status(400).json({ error: err.message }); }});
app.post("/api/tasks/bulk-delete", async (req, res) => { try { const { ids } = req.body; await taskRepo.bulkDelete(ids); res.status(204).send(); } catch (err: any) { res.status(500).json({ error: err.message }); }});Stats & Health Endpoints
Aggregate stats and health check endpoints:
routes/stats.ts
app.get("/api/stats", async (req, res) => { try { const stats = await taskRepo.aggregate({ count: "*" }); const byStatus = await taskRepo.rawQuery( "SELECT status, COUNT(*) as count FROM tasks WHERE deletedAt IS NULL GROUP BY status" ); res.json({ total: stats.count_all, byStatus }); } catch (err: any) { res.status(500).json({ error: err.message }); }});
app.get("/api/health", async (req, res) => { try { const health = await orm.healthCheck(); res.status(health.status === "healthy" ? 200 : 503).json(health); } catch (err: any) { res.status(503).json({ status: "unhealthy", error: err.message }); }});
const PORT = process.env.PORT || 3000;app.listen(PORT, () => { console.log(`Server running on http://localhost:${PORT}`);});