This generates a schema.gen.ts file with your schema definition and types.
3. Initialize Database
Create a db.ts file to configure and export your database instance:
src/db.ts
import type { SyncedDbConfig } from '@spooky-sync/client-solid';import { schema, SURQL_SCHEMA } from './schema.gen';export const dbConfig: SyncedDbConfig<typeof schema> = { logLevel: 'info', schema, schemaSurql: SURQL_SCHEMA, database: { namespace: 'main', database: 'main', // `spky dev` exposes SurrealDB on 8666 by default. endpoint: 'ws://localhost:8666/rpc', store: 'indexeddb', },};
4. App Entry Point
Initialize the database in your root component before rendering the app:
src/App.tsx
import { Sp00kyProvider } from '@spooky-sync/client-solid';import { dbConfig } from './db';import { MainContent } from './MainContent';export default function App() { // `Sp00kyProvider` constructs a `SyncedDb` from the config and // awaits its `init()` internally. While it's connecting it shows // the `fallback` instead of the children. After that, descendants // can pull the client with `useDb()` and queries with `useQuery()`. return ( <Sp00kyProvider config={dbConfig} fallback={<div>Initializing database...</div>} onError={(err) => console.error('Failed to initialize database:', err)} > <MainContent /> </Sp00kyProvider> );}
5. Querying Data with useQuery
Use the useQuery hook to create reactive queries that automatically update:
src/components/ThreadList.tsx
import { For, Show } from 'solid-js';import { useDb, useQuery } from '@spooky-sync/client-solid';import type { schema } from '../schema.gen';export function ThreadList() { // `useDb` reads the client from the `<Sp00kyProvider>` context. const db = useDb<typeof schema>(); // Build the query. `useQuery` re-renders the component when the // result set changes (locally or from another tab / device). const threadsQuery = useQuery(() => db.query('thread') .related('author') .orderBy('created_at', 'desc') .limit(20) .build() ); return ( <div> <Show when={threadsQuery.isLoading()}> <div>Loading...</div> </Show> <Show when={threadsQuery.error()}> <div>Error: {threadsQuery.error()?.message}</div> </Show> <ul> <For each={threadsQuery.data() || []}> {(thread) => ( <li> <h3>{thread.title}</h3> <p>{thread.content}</p> <small>By {thread.author?.username}</small> </li> )} </For> </ul> </div> );}
6. Authentication with Auth Context
Create an auth context to manage user authentication state:
src/lib/auth.tsx
import { createContext, useContext, createSignal, onCleanup, type JSX } from 'solid-js';import { useDb } from '@spooky-sync/client-solid';import type { schema } from '../schema.gen';interface AuthContextType { userId: () => string | null; isLoading: () => boolean; signIn: (username: string, password: string) => Promise<void>; signUp: (username: string, password: string) => Promise<void>; signOut: () => Promise<void>;}const AuthContext = createContext<AuthContextType>();export function AuthProvider(props: { children: JSX.Element }) { const db = useDb<typeof schema>(); const [userId, setUserId] = createSignal<string | null>(null); const [isInitializing, setIsInitializing] = createSignal(true); // Subscribe to auth state changes, fires synchronously with the // current value on subscribe, then on every signin/signout. const unsubscribe = db.auth.subscribe((uid) => { setUserId(uid); setIsInitializing(false); }); onCleanup(() => unsubscribe()); const signIn = async (username: string, password: string) => { await db.auth.signIn('account', { username, password }); }; const signUp = async (username: string, password: string) => { await db.auth.signUp('account', { username, password }); }; const signOut = async () => { await db.signOut(); }; const value: AuthContextType = { userId, isLoading: () => isInitializing(), signIn, signUp, signOut, }; return ( <AuthContext.Provider value={value}> {props.children} </AuthContext.Provider> );}export function useAuth() { const context = useContext(AuthContext); if (!context) { throw new Error('useAuth must be used within AuthProvider'); } return context;}
7. Creating and Updating Data
Use the database methods to create and update records:
import { RecordId, Uuid } from 'surrealdb';import { useDb } from '@spooky-sync/client-solid';import type { schema } from './schema.gen';function useMutations() { const db = useDb<typeof schema>(); // Create a new thread. `db.create` takes a fully-qualified record // id ("thread:abc…") plus a typed payload. There's no separate // table argument. const createThread = async (title: string, content: string, authorId: string) => { const id = \`thread:\${Uuid.v4().toString().replace(/-/g, '')}\`; await db.create(id, { title, content, active: true, author: new RecordId('user', authorId), }); }; // Update a thread. The third arg is a partial, only listed fields // are merged. const updateThread = async ( threadId: string, updates: { title?: string; content?: string }, ) => { await db.update('thread', threadId, updates); }; // Delete a thread. const deleteThread = async (threadId: string) => { await db.delete('thread', threadId); }; return { createThread, updateThread, deleteThread };}