Skip to content

Register a converter

Values do not arrive as they travel. A converter is registered per GraphQL named type and runs in both directions – QLive registers its own for Timestamp, Date and the document types, and an application adds or replaces one the same way.

import {registerConverter, Temporal} from "@qlivedev/qlive-ts";
registerConverter("MyScalar", {
fromServer: (value, type) => ...,
toServer: (value, type) => ..., // optional for output-only types
});

Registering a second converter for a type replaces the first, so you can override the ones QLive brings. The built-ins are registered while startup() initializes the config, and init is the point after that and before the first view renders:

await startup({
views: import.meta.glob("./app/**/*.tsx"),
init: async config => {
registerConverter("Timestamp", myTimestampConverter);
},
});

Anywhere earlier and the built-in overwrites yours; anywhere later and a view may already have read a value through the old one.

A converter is never called with null or undefined. QLive handles those before dispatching, so a converter does not have to.

Import Temporal from @qlivedev/qlive-ts, never from temporal-polyfill directly. A second copy of the polyfill produces instants that do not typecheck against the first – which is also why an application does not add temporal-polyfill to its own dependencies.

The Converter shape, the conversion calls the framework makes with it, the list of built-ins, and the generic scalar types a value arrives in are Scalars and conversion in the API reference.