Point0by 1gr14
  • Меню
    • GitHub
    • Блог
  • Introduction
    • Overview
    • Getting Started
    • Full Overview
    • Benchmarks
    • Points
  • Points
    • Page
    • Layout
    • Component
    • Provider
    • Mountable
    • Query
    • Infinite Query
    • Mutation
    • Action
    • Root
    • Base
    • Plugin
  • Methods
    • Validation
    • Loader
    • Context
    • Middleware
    • Loading & error
    • .with
    • Mapper
    • Transformer
    • Stage Methods
  • Core
    • Navigation
    • SSR
    • RSC
    • Request
    • Response
    • Error handling
    • Env
    • Head
    • MDX
    • Assets
    • File upload
    • OpenAPI
    • Query client
    • Events
    • Infer
  • Engine
    • Engine Config
    • Engine Runtime
    • CLI
    • Dev
    • Build
    • Compiler
    • Generator
    • Project MCP
    • Docs MCP
    • Importer
    • Public dir
    • Testing
    • Deploy
    • Bun or Vite
  • Extra
    • SsrStore
    • CookieStore
    • Basic Auth
    • CORS
    • Cache-Control
    • Compress
  • Examples
    • Basic
    • Vite
    • Better Auth
    • Capacitor
    • Expo
  • 1gr14
    • Главная
    • Start0
    • Поддержать
    • Обучение
    • Группа
    • Блог
    • Автор
  • Сообщество
    • Discord
    • Telegram
  • Опенсорс
    • Point0
    • Route0
    • Error0
    • Flat
  • Аккаунт
    • Войти
    • Регистрация
Point0by 1gr14
Создаю опенсорс во славу Господа Иисуса Христа ☦️
С любовью ко всем разработчикам на свете ❤️
Условия использованияПолитика конфиденциальностиСергей Дмитриев 2026 😎

Events

  • Раздел: Core

On this page

  1. Subscribing
  2. By name, by list, or everything
  3. The full event set
  4. Lifecycle phases
  5. Why engineFetch* is server-only
  6. The 'error' shorthand
  7. The event object
  8. meta: the log-friendly projection
  9. Binaries in the input are sanitized
  10. Subscriber errors and emitError
  11. Wiring events to your observability
  12. Reference
  13. Event names
  14. Subscription methods
  15. Event object fields
  16. Per-event data
  17. Public types

Point0 emits a lifecycle event for every query, mutation, and fetch it runs. Subscribe to them on any point with .on / .serverOn / .clientOn to log, report errors, or collect metrics — without touching the loaders themselves. The common case is one error subscriber on the root:

export const root = Point0.lets
  .root()
  .on('error', ({ side, name, error, meta }) => {
    // 'error' is sugar for the four error events; meta is a log-friendly projection
    console.error({ side, name, error, ...meta })
  })
  .root()

Every failed query or mutation now reaches your handler, with side, the event name, the typed error, and a slim meta you can spread straight in.

Subscribing

Three methods, all on every point type, all chainable (they return the same point):

.on('pointQuerySuccess', (e) => { /* kept in both bundles */ })
.serverOn('engineFetchError', (e) => { /* cut from the client bundle */ })
.clientOn('pointMutationSuccess', (e) => { /* cut from the server bundle */ })
  • .on — not cut from either bundle: kept in both (isomorphic), so it fires on both sides.
  • .serverOn — cut from the client bundle: its body and the imports it uses are removed, so the handler — server secrets included — never ships to the browser. It fires only for server-side events.
  • .clientOn — cut from the server bundle: body and its imports removed. It fires only for client-side events.

The side is decided when the event is emitted, from where the emitting code runs — not from the event name. A .serverOn callback never sees a client-side event, and vice versa.

Subscriptions accumulate: each call adds to the list, and a point inherits every subscription from its parents up the chain. Put app-wide logging on the root and it covers everything beneath.

By name, by list, or everything

.on('pointQueryStart', (e) => { /* one event */ })
.on(['pointQuerySuccess', 'pointMutationSuccess'], (e) => { /* several */ })
.on('*', (e) => { /* every event */ })

With '*' the callback receives the full event union; with a single name it's narrowed to that event. The wildcard still respects the side filter — .serverOn('*') fires only for server-side events.

The full event set

Six families, each with four lifecycle phases — Start, Settled, Success, Error — plus one emitError event: twenty-five events in all. The table below lists the families; the Reference enumerates every name:

FamilyWhat it tracksSide
pointQuery*a query running (useQuery / fetchQuery)client | server
pointInfiniteQuery*an infinite query runningclient | server
pointMutation*a mutation runningclient | server
pointFetchServer*a point's server-fetch step (the SSR / fetch machinery)client | server
pointPrefetchPage*a page being prefetched before navigationclient | server
engineFetch*the engine's outgoing HTTP fetch (the actual request)server only
emitErrora subscriber callback itself threw (see below)client | server

Each family gives you <Family>Start, <Family>Settled, <Family>Success, and <Family>Error.

Lifecycle phases

For any one run:

  • Start fires before the work begins.
  • Settled fires on every outcome — success or error.
  • Success fires on a successful result.
  • Error fires only on a genuine error.
// a query that succeeds:  pointQueryStart → pointQuerySettled → pointQuerySuccess
// a query that throws:     pointQueryStart → pointQuerySettled → pointQueryError

One edge case: a redirect is a success, not an error. When a loader redirects (throw redirect(...)), the query settles down the success path — Settled then Success fire, not Error. See Navigation for redirects.

Why engineFetch* is server-only

engineFetch* wraps the actual outgoing HTTP request, which only the server makes — so those events are typed 'server' and are only reachable through .on and .serverOn. Naming engineFetch* inside .clientOn is a type error: it isn't in the client event set.

The other point* events report side: 'client' even during SSR, because the query/fetch code that emits them is client-authored (it runs on the server under SSR, but it's the same code). Only engineFetch* — the HTTP layer — reports side: 'server'. During one SSR page load you'll see both:

.on('*', (e) => order.push([e.name, e.side]))
// pointQueryStart        client
// pointFetchServerStart  client
// engineFetchStart       server   ← the actual HTTP request
// engineFetchSettled     server
// engineFetchSuccess     server
// pointFetchServerSettled client
// pointFetchServerSuccess client
// pointQuerySettled      client
// pointQuerySuccess      client

The 'error' shorthand

.on('error', cb) is sugar — it expands to five subscriptions, one per error event:

.on('error', (e) => { /* … */ })
// equivalent to:
.on(['pointMutationError', 'pointQueryError', 'pointInfiniteQueryError', 'engineFetchError', 'rscError'], (e) => { /* … */ })

Inside the callback, error is narrowed to a non-undefined error instance. One user throw can produce more than one of them: a failing server query surfaces as engineFetchError (server) and pointQueryError (client), so an .on('error') logger may see the same failure from two angles.

GOTCHA: the shorthand covers query, infinite-query, mutation, engineFetch, and rscError (a failed defer() subtree) — not pointFetchServerError or pointPrefetchPageError. To catch those two, name them explicitly: .on(['pointFetchServerError', 'pointPrefetchPageError'], cb).

The event object

Every callback receives one object with the same five fields:

.on('pointQueryError', ({ side, name, data, error, meta }) => {
  side  // => 'client' | 'server' — where the emitting code ran
  name  // => 'pointQueryError'
  data  // the raw payload — rich, but heavy to log
  error // the typed error instance (your error class — ErrorPoint0 by default; undefined on non-error events)
  meta  // a slim, log-friendly projection of data
})
  • side — 'client' or 'server', set at emit time (see side above).
  • name — the event name.
  • data — the full payload (the query result, the request object, the QueryClient, …). Prefer meta for logging.
  • error — the error instance, hoisted to the top level so an 'error' handler can read it directly — the same object as data.error. Typed (your error class) on error events, undefined on every other event (the key is always present).
  • meta — the log-friendly projection, below.

meta: the log-friendly projection

meta is a plain record (Record<string, unknown>) built per event from data, meant to go straight into a logger:

.on('pointQueryStart', ({ meta }) => {
  meta.point // => 'root:page:home' — the point's id (<scope>:<type>:<name>), not the object
  meta.input // => { id: 123 }      — the input, sanitized (see below)
})

data carries heavy objects (responses, requests, query results) that you don't want in a log line; meta replaces them with compact forms: points become their string id (<scope>:<type>:<name>), requests become { method, path }, errors and redirects are serialized, and it drops bulky members. For an engineFetch event, meta.result and meta.response are dropped; for the SSR case, a settled event's meta.request.renders reports how many SSR render passes ran.

meta does not carry the error — on an error event, meta.error is undefined. The error lives on the envelope error (and data.error). So a typical error log spreads meta and adds the parts it needs:

.on('error', ({ side, name, error, meta }) => {
  console.error({ ...meta, side, name, error })
})

Binaries in the input are sanitized

meta.input runs through a sanitizer that replaces binary values with placeholder strings, so file uploads never bloat or break a log line:

// a mutation input of { photo: File, note: 'hi' } logs as:
meta.input // => { photo: '[File: photo.png (5120 bytes)]', note: 'hi' }

File → [File: <name> (<size> bytes)], Blob → [Blob: <size> bytes], FormData → [FormData]. Nested binaries (inside arrays/objects) are replaced too; everything else passes through unchanged.

Subscriber errors and emitError

Callbacks are fire-and-forget: they may be sync or async, and Point0 does not await them. A slow handler never blocks a request, and the completion order of async handlers across subscriptions isn't guaranteed.

If a callback throws, the framework does not crash. The error is caught and re-emitted as an emitError event, carrying the original event and the thrown error — so you can observe your own handler failures:

.on('emitError', ({ error, data, meta }) => {
  // error      — what your handler threw (coerced to the error class)
  // data.event — the full original event that was being handled
  // meta.event — its slim { name, meta } projection
  console.error('event handler failed', data.event.name, error)
})

A throw inside an emitError handler is swallowed silently — there's no recursion, so a broken error reporter can't cause an emit loop.

Wiring events to your observability

Funnel events through one subscriber on the root and let your logging stack fan out — the .on('error') subscriber at the top of this page is the whole pattern. Swap console.error for your own logger and you get error reporting with no extra call sites at the points. In Start0, for example, that subscriber writes to a LogTape logger whose sink forwards error records to Sentry — the same single root subscription, just a richer sink behind it.

Subscribe to the success/settled events the same way for request metrics or audit logs.

Reference

Event names

pointQueryStart          pointQuerySettled          pointQuerySuccess          pointQueryError
pointInfiniteQueryStart  pointInfiniteQuerySettled  pointInfiniteQuerySuccess  pointInfiniteQueryError
pointMutationStart       pointMutationSettled       pointMutationSuccess       pointMutationError
pointFetchServerStart    pointFetchServerSettled    pointFetchServerSuccess    pointFetchServerError
pointPrefetchPageStart   pointPrefetchPageSettled   pointPrefetchPageSuccess   pointPrefetchPageError
engineFetchStart         engineFetchSettled         engineFetchSuccess         engineFetchError
rscError                 emitError

Subscription methods

MethodFires onName argument accepts
.onboth sidesany event name, 'error', '*', or an array of names
.serverOnserver-side eventsserver event names, 'error', '*', array
.clientOnclient-side eventsclient event names, 'error', '*', array

All three are on every point type, accumulate, and are inherited down the chain. engineFetch* names are valid in .on / .serverOn only — a type error in .clientOn.

Event object fields

FieldTypeNotes
side'client' | 'server'where the emitting code ran, set at emit time
namethe event name
datathe raw payload (per event)rich; not log-friendly
errorerror instance, or undefinedpresent on error events; same object as data.error
metaRecord<string, unknown>log-friendly projection of data

Per-event data

Familydata carries
pointQuery*{ queryKey, point, input, mode, data?, error?, redirect? }
pointInfiniteQuery*same as query, for the infinite case
pointMutation*{ point, input } + one of output / error / redirect
pointFetchServer*{ input, point } + the fetch-server output on settled/success/error
pointPrefetchPage*{ point, input, options, error? }
engineFetch*{ request, scope, result?, error? }
rscError{ error, label, holeId } — a failed defer() subtree, server-side
emitError{ error, event } — the original event and the thrown error

Public types

The event types are exported from @point0/core: AnyEventerEvent, EventerEvent, EventerEventMeta, EventerSide, and the per-event types. The barrel is type-only (export type * from './eventer.js'), so the runtime uniqEventerErrorEventNames constant — the five names the 'error' shorthand expands to — is not importable as a value.

НазадQuery clientДалееInfer

Enjoying Point0?

Give it a star on GitHub, ask questions, and follow what's new

Star Point0

A star on GitHub helps more developers find Point0
Star on GitHub

Start0

Point0 integrated into the Start0 SaaS boilerplate — the best way to use it
Explore Start0

YouTube

Tutorials, walkthroughs, and updates
YouTube

Discord

Questions and chat in English
Join Discord

Telegram

News channel and chat in Russian
ChannelChat

Twitter

Posts and updates
Twitter