Skip to content
effect-api-query

Custom Key Encoders

Supply safe semantic identity for RPC payloads and HTTP requests.

By default, the factory constructs the RPC payload and synchronously encodes it with its Schema. Supply a custom encoder when encoding requires Effect services or the payload contains Schema.Redacted values, including schemas reached through Schema.suspend.

const rpcQuery = createRpcQueryUtils(rpcGroup, {
client,
keyPrefix: ['admin'] as const,
keyEncoders: {
'secrets.read': (payload) => ({ secretId: identifySecret(payload.secret) }),
},
runPromiseExit,
})

An encoder receives the normalized payload and must return a strict JsonValue synchronously. It must not reveal secrets. Return a stable public identifier, digest, or other safe semantic identity.

Define encoders as own enumerable properties keyed by literal payload-bearing RPC tags. TypeScript requires entries for unsafe payloads; the factory also rejects missing or unknown entries at runtime.

Inputs that can produce different RPC results must produce different keys.

HTTP encoders receive the complete decoded request input: the declared params, query, payload, and headers containers. RPC encoders receive the normalized payload after constructor defaults. Configure each HTTP encoder in createHttpApiQueryUtils under its literal declaration group and endpoint identifier, even for a top-level group or an identifier containing dots.

Suppose a forms group’s submit endpoint accepts a number encoded as text or a string encoded as JSON. Both schemas can encode to "1", but the HTTP bodies differ. Preserve that distinction in the key:

const http = createHttpApiQueryUtils(contract, {
client,
keyPrefix: ['forms'],
keyEncoders: {
forms: {
submit: ({ payload }) => ({
format: typeof payload === 'number' ? 'text' : 'json',
value: String(payload),
}),
},
},
})

Multiple buffered alternatives require an encoder even when they share a content type. For buffered binary input, return a JSON-safe representation such as { bytes: Array.from(payload) }; the ready client still receives the original Uint8Array.

Encoding services or explicit Redacted values in any request part also require an encoder. The factory treats opaque encoding middleware conservatively at runtime. An encoder supplies identity only; the ready client still needs an execution runner when it requires services. The factory synchronously rejects encoder entries for unknown, omitted, or inputless endpoints during construction.

Keep ordinary authentication in client middleware. Partition the cache with safe tenant and user identifiers in keyPrefix, for example ['tenant', 'north', 'user', 'ada']. The factory cannot infer identity from a client, base URL, or middleware. Retain every safe value that distinguishes results when excluding a secret.

Return strict JsonValue synchronously. The factory copies and freezes the result; it rejects undefined object members, undefined or sparse array entries, non-finite numbers, cycles, and non-JSON objects. Custom output does not receive the default HTTP omission or header normalization. See the HTTP reference for failure codes. Underlying error Causes remain intact and may contain values supplied by Effect.

The public RPC consumer and public HTTP consumer check encoder inputs and required services. The HTTP key tests verify alternative payload and binary projections.