QUESTPIE
Build Your BackendData Modeling

Relations

Define relationships between collections — belongsTo, hasMany, and many-to-many through junction tables.

Relations link collections together. QUESTPIE supports belongs-to, has-many, and many-to-many relationships.

Belongs-To (Single Relation)

A field that references one record from another collection:

collections/appointments.ts
import { collection } from "#questpie/factories";

export const appointments = collection("appointments").fields(({ f }) => ({
	customer: f.relation("user").required(),
	barber: f.relation("barbers").required(),
	service: f.relation("services").required(),
	// ...
}));

This creates a foreign key column customeruser.id, barberbarbers.id, etc.

Options

OptionTypeDescription
tostringTarget collection name
requiredbooleanWhether the relation is required
labelstring | i18nDisplay label
onDelete"cascade" | "set null" | "restrict"Foreign key behavior

Has-Many (Reverse Relation)

Define the reverse side of a belongs-to — one record has many related records:

collections/services.ts
import { collection } from "#questpie/factories";

export const services = collection("services").fields(({ f }) => ({
	name: f.text().required(),
	// Reverse: this service has many barbers (via barberServices junction)
	barbers: f.relation("barbers").manyToMany({
		through: "barberServices",
		sourceField: "service",
		targetField: "barber",
	}),
}));

Many-to-Many (Through Junction)

Many-to-many requires a junction collection:

collections/barber-services.ts
import { collection } from "#questpie/factories";

export const barberServices = collection("barberServices")
	.fields(({ f }) => ({
		barber: f.relation("barbers").required().onDelete("cascade"),
		service: f.relation("services").required().onDelete("cascade"),
	}))
	.admin(({ c }) => ({ hidden: true })); // Hide from admin sidebar

Then reference it from both sides:

collections/barbers.ts
services: f.relation("services").manyToMany({ through: "barberServices", sourceField: "barber", targetField: "service" }),
collections/services.ts
barbers: f.relation("barbers").manyToMany({ through: "barberServices", sourceField: "service", targetField: "barber" }),

Through Options

OptionTypeDescription
throughstringJunction collection name
sourceFieldstringFK in junction → this collection
targetFieldstringFK in junction → target collection

Querying Relations

const barber = await collections.barbers.findOne({
	where: { id: "abc" },
	with: {
		services: true, // Include related services
	},
});
// barber.services: Service[]

Filter by relation

const appointments = await collections.appointments.find({
	where: {
		barber: barberId, // Filter by relation ID
		status: "pending",
	},
});

Client-side

const barbers = await client.collections.barbers.find({
	with: { services: true },
	where: { isActive: true },
});

On this page