Docs

Script Examples

Script Examples Small examples for handlers, hooks, flows, and websocket event scripts. Repository Reads Find Rows const posts = await #post.find({ fields: "id,title", limit: 10 }) Find One Row const result = await #post.find({ filter: { id: { _eq: @PARAMS.id } }, fields: "id,tit

Script Examples

Small examples for handlers, hooks, flows, and websocket event scripts.

Repository Reads

Find Rows

const posts = await #post.find({
  fields: "id,title",
  limit: 10
})

Find One Row

const result = await #post.find({
  filter: { id: { _eq: @PARAMS.id } },
  fields: "id,title",
  limit: 1
})

return result.data[0] || null

Count Rows

Use aggregate() for a computed count instead of loading records or requesting legacy meta aggregate data.

const result = await #post.aggregate({
  filter: { status: { _eq: "published" } },
  measures: {
    posts: { count: "id" }
  }
})

return result.data[0]?.posts ?? 0

Group Rows by Day

const result = await #post.aggregate({
  dimensions: [
    { field: "createdAt", bucket: "day", timezone: "Asia/Ho_Chi_Minh" }
  ],
  measures: {
    posts: { count: "id" }
  },
  sort: [{ field: "createdAt", direction: "asc" }]
})

return result.data

Repository Writes

Create Row

const created = await #post.create({
  data: {
    title: @BODY.title,
    status: "draft"
  }
})

return created.data[0]

Update Row

const updated = await #post.update({
  id: @PARAMS.id,
  data: {
    title: @BODY.title
  }
})

return updated.data[0]

Delete Row

await #post.delete({
  id: @PARAMS.id
})

return { deleted: true }

Errors

Required Body Field

if ([email protected]) {
  @THROW400("Title is required")
}

Forbidden Action

if (!@USER) {
  @THROW403("Login required")
}

Not Found

const row = await #post.find({
  filter: { id: { _eq: @PARAMS.id } },
  fields: "id",
  limit: 1
})

if (!row.data[0]) {
  @THROW404("post", @PARAMS.id)
}

Hooks

Pre-Hook Owner Scope

@QUERY.filter = {
  _and: [
    @QUERY.filter || {},
    { owner: { id: { _eq: @USER.id } } }
  ]
}

Pre-Hook Set Owner On Create

@BODY.owner = { id: @USER.id }

Pre-Hook Strip Server Field

delete @BODY.isAdmin

Post-Hook Shape Response

return {
  id: @DATA.id,
  title: @DATA.title
}

Flows

Trigger Flow From Handler

await @TRIGGER("send-welcome-email", {
  userId: @USER.id
})

return { queued: true }

Flow Step Reads Payload

const userId = @FLOW_PAYLOAD.userId

return { userId }

Flow Step Uses Previous Step

const user = @FLOW.load_user

return {
  email: user.email
}

WebSocket

Reply To Current Client

@SOCKET.reply("pong", {
  at: new Date().toISOString()
})

Join Room

@SOCKET.join(`project:${@BODY.projectId}`)

Emit To Current Room

@SOCKET.emitToCurrentRoom(`project:${@BODY.projectId}`, "project:changed", {
  projectId: @BODY.projectId
})

Emit To User

@SOCKET.emitToUser(@USER.id, "notification", {
  title: "Done"
})

Package Streams

See Package Management for single-consumer, timeout, cancellation, error, and observer/transform semantics.

Preflight Then Relay Exact Bytes

const upstream = await @PKGS.undici.request("https://api.example.com/stream", {
  method: "POST",
  body: JSON.stringify(@BODY)
})

const guarded = await $ctx.$streams.preflight(upstream.body, {
  timeoutMs: 15_000
})

await @RES.stream(guarded.stream, {
  statusCode: upstream.statusCode,
  mimetype: "text/event-stream"
})

Consume Before Returning JSON

const upstream = await @PKGS.undici.request("https://api.example.com/result", {
  method: "POST",
  body: JSON.stringify(@BODY)
})

const text = await $ctx.$streams.readText(upstream.body, {
  timeoutMs: 60_000,
  maxBytes: 8 * 1024 * 1024
})

return { result: JSON.parse(text) }

Cache

Set Cache

await @CACHE.set("report:latest", { count: 12 }, 60000)

Get Cache

const cached = await @CACHE.get("report:latest")
if (cached) return cached