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
includeobject—Related 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
includeobject—Related models to include
whereMaybeRefOrGetter<Where>—Filter criteria
orderByMaybeRefOrGetter<OrderBy>—Sort criteria
takenumber—Max 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.