Solid
createStitch and createStitchStream primitives that reconcile a Solid store from a stitch call, over the framework-agnostic @stitchapi/query-core store — plus an optional TanStack Query adapter.
Use @stitchapi/solid when you call stitches from Solid components and want the
call's lifecycle — pending / success / error, cancellation, refetch, and
streaming re-renders — managed for you. createStitch is the request/response
primitive; createStitchStream reconciles each delta chunk into the store as it
arrives, which is the differentiator over plain request/response query libraries.
The primitives are a thin layer over @stitchapi/query-core,
the framework-agnostic store that owns the reactive lifecycle. Solid mirrors each
snapshot into a createStore, so reads inside JSX track fine-grained.
Example
Install the primitives, the store, core, and Solid:
npm install @stitchapi/solid @stitchapi/query-core stitchapi solid-jsstitchapi, @stitchapi/query-core, and solid-js (^1.8) are peer
dependencies; @tanstack/solid-query is an optional peer, needed only for the
queryOptions adapter below.
createStitch — request / response
Declare a stitch once, then drive it from a component. input and options may be
plain values (read once) or accessors — pass an accessor (() => props.id) so the
query re-fetches when its value changes. Read state off the returned store's
state proxy:
import { createStitch } from '@stitchapi/solid';
import { Show } from 'solid-js';
import { stitch } from 'stitchapi';
const getUser = stitch({
baseUrl: 'https://api.example.com',
path: '/users/{id}',
});
function Profile(props: { id: string }) {
// Pass an accessor so the query re-fetches when `props.id` changes.
const user = createStitch(getUser, () => ({ params: { id: props.id } }));
return (
<Show when={!user.state.isPending} fallback={<Spinner />}>
<Show
when={!user.state.isError}
fallback={<Retry onClick={user.refetch} />}
>
<h1>{user.state.data?.name}</h1>
</Show>
</Show>
);
}The query is re-created (and re-fetched) when the stitch's name or a structural key
of input changes. The in-flight run is aborted when the component's scope is
disposed (onCleanup).
Anti-pattern: don't destructure state — const {data} = user.state
reads the field once and loses reactivity, because state is a Solid store
proxy that tracks on property access. Read user.state.data directly inside
JSX (or an effect) so Solid re-renders fine-grained when it changes.
createStitchStream — live deltas
For a streaming stitch (an sse or stream surface),
createStitchStream reconciles each chunk into the store as it arrives. chunks
is the running list, data is the accumulated array (mode: 'append', default) or
the latest chunk (mode: 'replace'), and status is 'streaming' until the
terminal result, then 'success':
import { createStitchStream } from '@stitchapi/solid';
import { For, Show } from 'solid-js';
import { sse } from 'stitchapi/sse';
const chat = sse({ url: 'https://api.example.com/chat' });
function Chat(props: { prompt: string }) {
const c = createStitchStream(chat, () => ({
body: { prompt: props.prompt },
}));
return (
<div>
<For each={c.state.chunks as string[]}>
{(chunk) => <span>{chunk}</span>}
</For>
<Show when={c.state.isStreaming}>
<Cursor />
</Show>
</div>
);
}Both primitives return the same shape — a state proxy carrying data, error,
status, chunks, and the isPending / isError / isSuccess / isStreaming
flags, plus refetch and cancel methods.
The shared store: @stitchapi/query-core
The primitives hold almost no logic. All of it — running the call under an
AbortController, publishing status transitions, folding delta chunks into
state, cancel() by aborting, refetch() by re-running — lives in
@stitchapi/query-core's createStitchQuery, a subscribe / getSnapshot handle
with no framework imports and no node:*, so it is browser- and edge-safe.
Solid subscribes to it and reconciles each snapshot into a createStore:
import { } from '@stitchapi/query-core';
import { } from 'stitchapi';
import { } from 'zod';
const = ({
: 'https://api.example.com',
: '/users',
: .(.({ : .(), : .() })),
});
const = (, { : { : 'ada' } });
const = .(() => {
const = .();
if (.) .(.);
});Because the whole reactive lifecycle lives in the store and not the binding, the
React, Vue, and
Svelte bindings are the same few lines against their
own reactive primitive — Solid's is createStore + onCleanup.
Optional: TanStack Query
Already on TanStack Query?
queryOptions(stitch, input) returns a plain { queryKey, queryFn } object you
pass straight to createQuery — it imports nothing from @tanstack/solid-query,
so it works even if you never install it:
import { queryOptions } from '@stitchapi/solid';
import { createQuery } from '@tanstack/solid-query';
import { stitch } from 'stitchapi';
const getUser = stitch({
baseUrl: 'https://api.example.com',
path: '/users/{id}',
});
function User(props: { id: string }) {
const query = createQuery(() =>
queryOptions(getUser, { params: { id: props.id } }),
);
return <span>{query.data?.name}</span>;
}Reach for queryOptions when TanStack Query already owns your view state
and you want a stitch as the queryFn; reach for createStitch /
createStitchStream when you want StitchAPI to own the lifecycle directly —
especially for streaming, which createQuery does not model. See the
TanStack Query guide for how the two
layers split.
See also
Svelte
stitchStore and stitchStreamStore — real Svelte stores wrapping a stitch call, over the framework-agnostic @stitchapi/query-core store. Works on Svelte 4 and 5, plus an optional TanStack Query adapter.
Angular
injectStitch and injectStitchStream expose a stitch's lifecycle as both Angular signals and an RxJS observable over the framework-agnostic @stitchapi/query-core store — plus an optional TanStack Query adapter.