Build Your BackendData Modeling
Globals
Globals are singleton data objects for site-wide configuration like settings, branding, and SEO.
A global is a singleton — a single record with no list view, just a form. Use globals for site-wide configuration: settings, SEO defaults, navigation, footer content.
Defining a Global
globals/site-settings.ts
import { global } from "#questpie/factories";
export const siteSettings = global("site_settings")
.fields(({ f }) => ({
shopName: f.text().required().default("My App"),
tagline: f.text().localized(),
logo: f.upload({ to: "assets" }),
contactEmail: f.email().required(),
}))
.admin(({ c }) => ({
label: { en: "Site Settings", sk: "Nastavenia webu" },
icon: c.icon("ph:gear"),
}))
.options({
timestamps: true,
versioning: true,
})
.access({
read: true,
update: ({ session }) => (session?.user as any)?.role === "admin",
});Builder Chain
Globals share most methods with collections:
| Method | Purpose |
|---|---|
.fields(({ f }) => {...}) | Define data fields |
.admin(({ c }) => {...}) | Admin label and icon |
.form(({ v, f }) => v.globalForm({...})) | Form layout |
.hooks({...}) | Lifecycle hooks |
.access({...}) | Read/update access control |
.options({...}) | Timestamps, versioning |
Globals do not support .list(), .indexes(), .title(), or .preview() since there's only ever one record.
Server-Side API
// Read
const settings = await globals.siteSettings.get();
// Update
await globals.siteSettings.update({
shopName: "New Name",
tagline: "New tagline",
});Client-Side API
// Read
const settings = await client.globals.siteSettings.get();
// Update (requires access)
await client.globals.siteSettings.update({
shopName: "New Name",
});Real-World Example
From the barbershop — site settings with nested objects, arrays, and access control:
globals/site-settings.ts
import { global } from "#questpie/factories";
export const siteSettings = global("site_settings")
.fields(({ f }) => ({
shopName: f.text().required().default("Sharp Cuts"),
tagline: f.text().localized(),
logo: f.upload({ to: "assets" }),
navigation: f
.object({
label: f.text().required(),
href: f.text().required(),
isExternal: f.boolean().default(false),
})
.array()
.localized(),
contactEmail: f.email().required(),
contactPhone: f.text(),
address: f.text(),
businessHours: f.object({
monday: f.object({
isOpen: f.boolean().default(true),
start: f.time(),
end: f.time(),
}),
// ... other days
}),
bookingSettings: f.object({
minAdvanceHours: f.number().required(),
maxAdvanceDays: f.number().required(),
slotDurationMinutes: f.number().required(),
allowCancellation: f.boolean().required(),
}),
metaTitle: f.text().localized(),
metaDescription: f.textarea().localized(),
}))
.admin(({ c }) => ({
label: { en: "Site Settings", sk: "Nastavenia webu" },
icon: c.icon("ph:gear"),
}))
.form(({ v, f }) =>
v.globalForm({
fields: [
{
type: "section",
label: { en: "Branding" },
layout: "grid",
columns: 2,
fields: [f.shopName, f.tagline, f.logo],
},
{
type: "section",
label: { en: "Contact" },
layout: "grid",
columns: 2,
fields: [f.contactEmail, f.contactPhone, f.address],
},
// ... more sections
],
}),
)
.options({ timestamps: true, versioning: true })
.access({
read: true,
update: ({ session }) => (session?.user as any)?.role === "admin",
});Related Pages
- Fields — All field types and options
- Access Control — Read/update rules
- Form Views — Admin form layout