Integrations
SolidStart
Hand server-function results to live Solid query components.
On this page
Load tasks in a SolidStart server function, then keep the list live with @hulla/db-solid in the browser.
Complete the shared task setup first. The example uses SolidStart 2, Solid 1.9, and Solid Router’s createAsync.
Set database.client in db.config.ts before generating the application:
// Inside defineConfig({ ... })
database: {
server: { url: process.env.DATABASE_URL! },
client: { url: process.env.PUBLIC_DATABASE_SERVICE_URL!, credentials: 'include' },
},Generation copies only these public client settings into browser bindings. Importing
callables requires no setup call and opens no network connection. Use a root-relative
URL such as /db when your application proxies the service on the same origin.
The URL is selected at generation time; regenerate when changing environments.
For authenticated views, call generated setAuth from the provider’s browser-only
session listener. Never put a request’s identity in a shared server module. Component
cleanup releases its subscriptions; it does not dispose the shared application runtime.
SolidStart’s server data functions
are ordinary application code. Alias the router’s query to distinguish it from
the database declaration. Keep the database import inside the server function:
// src/data/tasks.ts
import { query as serverQuery } from '@solidjs/router'
export const getInitialTasks = serverQuery(async () => {
'use server'
const { db } = await import('../../db/_generated/server')
const { readTasks } = await import('../../db/task-data')
return await readTasks(db)
}, 'initial-tasks')Resolve the snapshot through Solid Router and pass it into a separate live component. The conditional creates the binding only when initial data exists:
// src/routes/index.tsx
import { createAsync } from '@solidjs/router'
import { Show, Suspense } from 'solid-js'
import { getInitialTasks } from '../data/tasks'
import { Tasks } from '../components/Tasks'
export default function Page() {
const initial = createAsync(() => getInitialTasks())
return (
<Suspense fallback={<p>Loading tasks…</p>}>
<Show when={initial()}>{(rows) => <Tasks initialTasks={rows()} />}</Show>
</Suspense>
)
}The binding is created during component initialization and observes after mount:
// src/components/Tasks.tsx
import type { Output } from '@hulla/db'
import { createMutation, createQuery } from '@hulla/db-solid'
import { For, Show } from 'solid-js'
import { create, list } from '../../db/_generated/client/tasks'
export function Tasks(props: { initialTasks: Output<typeof list> }) {
const tasks = createQuery(list, { initialData: props.initialTasks })
const add = createMutation(create)
return (
<>
<Show when={tasks().status === 'error'}>
<p>Could not load tasks.</p>
</Show>
<Show when={add.state().status === 'error'}>
<p>Could not save the task.</p>
</Show>
<ul>
<For each={tasks().data}>{(task) => <li>{task.title}</li>}</For>
</ul>
<button
onClick={() => {
void add({ title: 'New task' })
}}>
Add task
</button>
</>
)
}Read query state through tasks() and mutation state through add.state().
Queries with input can read signals through a getter. The router resource handles
the initial server loading boundary; database snapshots handle subsequent live
updates. Add a normal Solid error boundary for rejected server loads.
Obtain the request with getRequestEvent() from solid-js/web inside the server
function, reject a missing request/session, and verify its request.headers
through your provider before direct reads. Return the verified account key with
the rows and pass it as initialAuthKey to the binding. The browser declarations
need their own authorization and filters. Do not put private snapshots into a
shared application cache. See the shared auth guide.
Use other SolidStart server functions/actions for external effects and direct
db.transaction work. Router query invalidation affects router-loaded snapshots;
the database subscription independently observes committed writes. Neither
invalidates the other’s cache automatically.
Choose a Node-compatible deployment target and include the native executables. The persistent subscription service remains separate from the request-time app; see deployment and packaging.