resolve and search, commands, message actions, events and routes. This page covers what they have
in common. The pages for each piece cover their options.
Handler forms
A handler takes one of two forms:- A generator function,
function* (context) { … }. This is the recommended form:yield*runs an Effect or reads a service, and the value youreturnis the result. - A function that returns an
Effect.
Promise of JSX. See
Blocks and views.
These two actions do the same thing:
Effect.succeed widens "done" to string, which the state schema rejects, so it needs
as const. A generator’s return value keeps its literal types without it.
Keep handlers unwrapped
Don’t wrap a handler inEffect.fn. It hides the context’s type, so args, state and the rest
become any and mistakes stop being type errors. reintersect check warns about it with
effect-fn-handler. To trace a step, add a span to the Effect inside the handler:
The context
Each handler gets the same context as on the Promise API, with these differences:waitUntil suits work the member doesn’t wait for, such as recording an audit entry:
args, state, tallies, actor, options, message, params and so on) has
the same type and meaning as on the Promise API.
Schemas
Every schema on the Effect API is an Effect Schema with no requirements, aSchema.Schema<A, I, never>. Passing a Zod, ArkType or Valibot schema is a type error.
The SDK decodes props, action arguments, form input, install config and environment values before
your handler sees them. Block state is decoded before your view and actions read it and encoded back
to JSON before Reintersect stores it. That means block state can use a transforming schema: your code
works with a Date and Reintersect stores the ISO string.
initialState when you define the block. A value that doesn’t match the schema
throws at startup with Block "launch" state does not match its schema.
Forms, agent tools and the manifest use JSON Schema, which the SDK builds with JSONSchema.make. A
recursive schema, such as one built with Schema.suspend, is rejected when the manifest syncs.
Environment, secrets and install config
Declare what the app reads oncreateReintersectApp, and declare on each piece what that piece
reads:
src/schemas.ts
src/syncTasks.ts
createReintersectApp({ name: "Tasks", env, installConfig: InstallConfig }). Adding a piece that
reads an environment value or install config the app doesn’t declare is a type error. See
Environment and config for where the values come from.
Environment values always arrive as strings, so each schema must decode from a string, such as
Schema.NumberFromString. A schema that doesn’t, such as Schema.Number, is a type error that names
the key. For an optional value with a default, decode from Schema.UndefinedOr(Schema.String) and
give it a jsonSchema annotation, so reintersect check can describe it in the manifest:
Secrets
secret() declares a secret environment value. Your handlers receive it as a Redacted<string>.
Read it with Redacted.value(env.TRACKER_TOKEN) where you need it. Everywhere else, including logs
and string templates, it prints as <redacted>.