At a Glance
elements man database/at-a-glance Read as markdownA schema, a migration, and the queries that read and write it.
import { sql, tx, session } from "@elements/app";
interface User { id: string; name: string; email: string; createdAt: Date; }
interface Order { id: string; userId: string; total: number; }
/** @rpc */
export function placeOrder(items: { sku: string; qty: number }[]): Order {
session.isLoggedInOrThrow();
let userId = session.getOrThrow("userId");
return tx(() => {
let order = sql<Order>(`
insert into orders (userId, total) values (${userId}, ${total(items)}) returning *
`).firstOrThrow();
for (let item of items) {
sql(`
insert into orderItems (orderId, sku, qty) values (${order.id}, ${item.sku}, ${item.qty})
`);
}
return order;
});
}
function total(items: { sku: string; qty: number }[]): number {
let result = 0;
for (let row of sql<{ sku: string; price: number }>(
`select sku, price from products where sku = any(${items.map(i => i.sku)})`
)) {
let item = items.find(i => i.sku === row.sku);
if (item) {
result += row.price * item.qty;
}
}
return result;
}
The code reads as a sequence of statements with no await keywords. Column
names stay camelCase throughout. Each ${value} is extracted to a positional
parameter at build time, so the query is parameterized at runtime. The entire
body runs inside a Postgres transaction because of the surrounding tx().