Files
Uploads stored with your data, readable through queries and links.
db.storage and runtime.files helpers are planned; today upload with PUT /storage/upload (HTTP API).Files are objects of the $files entity. You upload them, link them to your own entities, and read them with a query like any other data.
Upload from the browser
const { fileId } = await db.storage.upload(`invoices/${invoiceId}.pdf`, file);Read
const { data } = db.useQuery({
$files: { $: { where: { path: `invoices/${invoiceId}.pdf` } } },
});
// data.$files[0].url → a signed URL that expires$files objects have path, size, content-type, content-disposition and url. The URL expires: store the path, or link the file, and read url fresh each time.
Link files to your data
links: {
billing_invoicePdf: {
forward: { on: "billing_invoice", has: "one", label: "pdf" },
reverse: { on: "$files", has: "many", label: "invoices" },
},
},db.useQuery({ billing_invoice: { pdf: {} } });Create files inside an action
When a file is the result of business logic — a generated PDF, an export — create it in the action, so it exists only if the action succeeded:
async execute({ input, runtime }) {
const pdf = await renderInvoice(input);
const { fileId } = await runtime.files.upload(`invoices/${input.invoiceId}.pdf`, pdf, {
contentType: "application/pdf",
});
await runtime.tx((tx) => tx.billing_invoice[input.invoiceId].link({ pdf: fileId }));
}Files as action input
An action can take files as input. The client uploads them before the call, and your code receives a handle to read them:
import { file } from "@domainruntime/domain";
input: z.object({ invoiceId: z.string().uuid(), scan: file() }),Up to 16 files, 32 MiB each and 128 MiB per call. See Limits.
Delete
await db.storage.delete(`invoices/${invoiceId}.pdf`);Deleting the $files object removes the file from queries immediately.
Permissions
$files is denied by default: add rules for view, create and delete, usually by path or by the entity the file is linked to. See Permissions.