QUESTPIE

CRUD API

Collection CRUD operations — find, findOne, create, update, delete, count, versions, and workflow transitions.

Collections expose typed CRUD operations on the server runtime and through questpie/client. Server code receives collection APIs from handler context; client code calls the generated client.

find(options)

const result = await collections.posts.find({
	where: { status: "published" },
	orderBy: { createdAt: "desc" },
	limit: 20,
	offset: 0,
	with: { author: true },
	stage: "published",
});

Returns: { docs: T[], totalDocs: number }

findOne(options)

const record = await collections.posts.findOne({
	where: { id: "post_123" },
	with: { author: true },
	stage: "published",
});

Returns: T | null

When versioning workflow is enabled, stage reads the latest version snapshot for that stage. Omit it to read the working stage.

create(data)

const record = await collections.posts.create(
	{
		title: "Hello",
		body: "World",
	},
	{ stage: "draft" },
);

Returns: T (created record)

updateById(options)

const record = await collections.posts.updateById(
	{
		id: "post_123",
		data: { title: "Updated title" },
	},
	{ stage: "draft" },
);

Returns: T (updated record)

On the client SDK, the same operation is exposed as update({ id, data }):

const updated = await client.collections.posts.update(
	{
		id: "post_123",
		data: { title: "Updated title" },
	},
	{ stage: "draft" },
);

deleteById(options)

await collections.posts.deleteById({ id: "post_123" });

count(options)

const count = await collections.posts.count({
	where: { status: "published" },
});

Returns: number

update(options)

await collections.posts.update({
	where: { status: "draft" },
	data: { title: "Needs review" },
});

Server update() updates every record matching where. On the client SDK this operation is named updateMany().

delete(options)

await collections.posts.delete({
	where: { status: "archived" },
});

Server delete() deletes every record matching where. On the client SDK this operation is named deleteMany().

findVersions(options)

const versions = await collections.posts.findVersions({
	id: "post_123",
	limit: 20,
});

Returns: version rows with versionId, versionNumber, versionOperation, versionUserId, and versionCreatedAt.

revertToVersion(options)

const restored = await collections.posts.revertToVersion({
	id: "post_123",
	version: 4,
});

Pass versionId instead of version when you already have the version row ID.

transitionStage(options)

const published = await collections.posts.transitionStage({
	id: "post_123",
	stage: "published",
});

To schedule a future transition:

await collections.posts.transitionStage({
	id: "post_123",
	stage: "published",
	scheduledAt: new Date("2026-05-01T09:00:00Z"),
});

transitionStage() requires versioning.workflow, validates the target stage and transition graph, checks access.transition with fallback to access.update, runs transition hooks, and creates a version snapshot without mutating the working row.

Client API

The client SDK mirrors the same resource behavior with browser-friendly method names:

const posts = await client.collections.posts.find({ stage: "published" });
const post = await client.collections.posts.findOne({
	where: { id: "post_123" },
});
const created = await client.collections.posts.create({ title: "Hello" });
const updated = await client.collections.posts.update({
	id: "post_123",
	data: { title: "Updated title" },
});
await client.collections.posts.delete({ id: "post_123" });
const count = await client.collections.posts.count({
	where: { status: "published" },
});
await client.collections.posts.transitionStage({
	id: "post_123",
	stage: "published",
	scheduledAt: "2026-05-01T09:00:00.000Z",
});

Upload Methods

For upload collections:

const asset = await client.collections.assets.upload(file, {
	onProgress: (percent) => {
		console.log(percent);
	},
});

const assets = await client.collections.assets.uploadMany(files, {
	onProgress: (percent, fileIndex) => {
		console.log(percent, fileIndex);
	},
});

On this page