Skip to content

Composables

All composables are auto-imported and fully typed from your ZModel schema. They communicate with the auto-generated server endpoints and keep a normalized store in sync.

useZenstackCreate

Creates a new record.

ts
const { mutate, data, error, status, loading, schema, reset } =
  useZenstackCreate("User");

Usage

NOTE

The useZenstackCreate composable does not allow setting relational fields.

ts
const { mutate: createUser, error, loading } = useZenstackCreate("User");

const state = ref<$ZcreateData<"User">>({
  email: "[email protected]",
  name: "Alice",
});

async function onSubmit() {
  await createUser(state.value);
}

Returns

PropertyTypeDescription
dataRef<Item | null>The created record
errorRef<$Zerror | null>Error from the last call
statusRef<'idle' | 'pending' | 'success' | 'error'>Current status
loadingComputedRef<boolean>true while the status is pending
schemaZodObjectThe Zod object schema for the model (see Input Validation)
mutate(input) => Promise<Item | null>Trigger the create operation
reset() => voidReset data, error, and status to initial values

useZenstackRead

Fetches a single record by ID.

ts
const { data, error, status, loading, query } = await useZenstackRead(
  "User",
  userId,
);

Usage

ts
const { data: user } = await useZenstackRead("User", userId);

// With related models
const { data: user } = await useZenstackRead("User", userId, {
  include: { posts: true },
});

Options

OptionTypeDefaultDescription
fetchPolicyFetchPolicymodule defaultOverride the global fetch policy for this call
immediatebooleantrueRun the query immediately on call
includeobjectRelated models to include (typed from schema)

Returns

PropertyTypeDescription
dataRef<Item | null>The fetched record
errorRef<$Zerror | null>Error from the last query
statusRef<Status>Current status
loadingComputedRef<boolean>true while fetching
query() => Promise<void>Re-run the query manually

useZenstackReadMany

Fetches a list of records with optional filtering, ordering, and pagination.

ts
const { data, error, status, loading, query, queryMore, canQueryMore, reset } =
  await useZenstackReadMany("User");

Usage

ts
// Basic list
const { data: users } = await useZenstackReadMany("User");

// Filtered and ordered
const search = ref("");

const { data: users } = await useZenstackReadMany("User", {
  where: () => ({ name: { contains: search.value } }),
  orderBy: { createdAt: "desc" },
  take: 20,
  watch: true, // re-query automatically when reactive `where`/`orderBy` change
});

// Load more (cursor-based pagination)
const {
  data: posts,
  queryMore,
  canQueryMore,
} = await useZenstackReadMany("Post", {
  take: 10,
});

// Call queryMore() to append the next page
await queryMore();

// Example with VueUse useInfiniteScroll
import { useInfiniteScroll } from "@vueuse/core";

const containerRef = ref<HTMLElement | null>(null);

useInfiniteScroll(containerRef, () => queryMore(), {
  distance: 10,
  canLoadMore: () => canQueryMore.value,
});

Options

OptionTypeDefaultDescription
fetchPolicyFetchPolicymodule defaultOverride the global fetch policy
immediatebooleantrueRun the query immediately
includeobjectRelated models to include
whereMaybeRefOrGetter<Where>Filter criteria
orderByMaybeRefOrGetter<OrderBy>Sort criteria
takenumberMax records per page
watchbooleanfalseRe-query when where/orderBy refs change

Returns

PropertyTypeDescription
dataRef<Item[] | null>The fetched records
errorRef<$Zerror | null>Error from the last query
statusRef<Status>Current status
loadingComputedRef<boolean>true while fetching
canQueryMoreRef<boolean>false when all records have been loaded
query() => Promise<void>Re-run the query (resets to first page)
queryMore() => Promise<void>Append the next page of results
reset() => voidReset data and pagination state

useZenstackUpdate

Updates an existing record by ID.

ts
const { mutate, data, error, status, loading, schema, reset } =
  useZenstackUpdate("User");

Usage

NOTE

The useZenstackUpdate composable does not allow setting relational fields.

ts
import type { $ZupdateData } from "#imports";

const { mutate: updateUser, loading } = useZenstackUpdate("User");

const state = ref<$ZupdateData<"User">>({
  name: "Current Name",
});

async function onSubmit(userId: string) {
  await updateUser(userId, state.value);
}

Returns

PropertyTypeDescription
dataRef<Item | null>The updated record
errorRef<$Zerror | null>Error from the last call
statusRef<Status>Current status
loadingComputedRef<boolean>true while the status is pending
schemaZodObjectThe Zod object schema for the model (see Input Validation)
mutate(id, input) => Promise<Item | null>Trigger the update
reset() => voidReset state

useZenstackDelete

Deletes a record by ID.

ts
const { mutate, data, error, status, loading, reset } =
  useZenstackDelete("User");

Usage

IMPORTANT

Deletion of relational entities triggered by cascading operations does not automatically update the store. The related entities will not be automatically removed.

ts
const { mutate: deleteUser, loading } = useZenstackDelete("User");

await deleteUser(userId);

Returns

PropertyTypeDescription
dataRef<Item | null>The deleted record
errorRef<$Zerror | null>Error from the last call
statusRef<Status>Current status
loadingComputedRef<boolean>true while the status is pending
mutate(id) => Promise<Item | null>Trigger the delete
reset() => voidReset state

Error Handling

All composables return an error ref typed as $Zerror | null. This type is an extension of FetchError that includes ZenStack's specific ORMError properties when a database or policy error occurs.

$Zerror Properties

When the server encounters a ZenStack error, the error object will contain the following properties inside its data payload:

PropertyTypeDescription
reasonORMErrorReasonThe reason code for the error (e.g., 'rejected-by-policy', 'not-found', 'db-query-error', 'invalid-input').
modelstringThe name of the ZModel that the error pertains to.
dbErrorCodeunknownThe raw error code given by the underlying database driver.
dbErrorMessagestringThe raw error message given by the underlying database driver.
rejectedByPolicyReasonRejectedByPolicyReasonIf reason is 'rejected-by-policy', this indicates why ('no-access', 'cannot-read-back', etc).
statusCodenumberThe HTTP status code returned by the auto-generated API endpoint (e.g., 403 for policy rejections).

For more details on ZenStack's error handling, refer to the ZenStack ORM Errors Documentation.


Type Generics

The module exports several TypeScript generics that are useful for strongly typing your application state and payload. These types are auto-imported and can also be explicitly imported from #imports.

TypeDescription
$ZschemaThe main SchemaType generated by ZenStack
$ZmodelUnion type of all your defined models (e.g. 'User' | 'Post')
$Zid<Model>The ID type of a given model
$Zitem<Model, Include?>The structure of a given model record, optionally with relations
$ZcreateData<Model>Payload type for useZenstackCreate (omits relational fields)
$ZupdateData<Model>Payload type for useZenstackUpdate (omits relational fields)
$Zwhere<Model>Where filter argument types for read queries
$ZorderBy<Model>Order by argument types for read queries

Usage Example

ts
import type { $Zitem, $Zwhere, $Zmodel } from "#imports";

type UserModel = $Zitem<"User">;
type PostWithAuthor = $Zitem<"Post", { author: true }>;

const customFilter: $Zwhere<"User"> = {
  age: { gt: 18 },
};

Input Validation

The schema returned by the Create and Update composables is a Zod object schema automatically generated by ZenStack. It accurately reflects the types and validation rules defined in your ZModel.

You can securely use this schema directly with your frontend form validation libraries (e.g. Nuxt UI) to perform client-side validation that perfectly mirrors your server's validation rules. ZenStack supports a wide range of validation attributes like @email, @regex, @length, and more.

For more details on defining validation rules in your ZModel, see the official ZenStack documentation:

Example

Given the following ZModel definition:

prisma
model User {
    id    String @id @default(cuid())
    email String @unique

    @@validate(isEmail(email), 'Email is invalid', ['email'])
    @@validate(length(email) >= 10, 'Email must have at least 10 characters', ['email'])
}

When you use the composables useZenstackCreate('User') or useZenstackUpdate('User'), the returned schema will automatically enforce these exact same validation rules on your frontend forms before submission.