SolidJS is a modern JavaScript framework designed to build responsive and high-performing user interfaces (UI). It prioritizes a simple and predictable development experience, making it a great choice for developers of all skill levels.
The SurrealDB SDK for JavaScript can also be used in your SolidJS applications to interact with your SurrealDB Instance.
In this guide, we will walk you through setting up and querying your first project with the SurrealDB SDK for SolidJS.
This guide assumes the following:
First, install the SurrealDB SDK using your favorite package manager:
bun install surrealdb
npm install --save surrealdb
yarn add surrealdb
pnpm install surrealdb
ImportantThe SurrealDB SDK for JavaScript is also available in the JSR registry as
@surrealdb/surrealdb
.
After installing the SDK, import it into your project. We recommend initializing the SDK in a Context Provider in order to expose it throughout your application. This makes the client accessible anywhere in your component tree, simplifies cleanup, and provides built-in state management for connection success or failure.
In addition to surrealdb
, we recommend using @tanstack/solid-query for managing manage the asynchronous connection state to the SurrealDB database.
Specifically, createMutation
to create a managed mutation that handles the connection lifecycle. It provides built-in state management for the async operation, automatically tracking loading states (isError, error), and provides utilities like reset to handle errors and retry operations if needed.
You can install the package using your favorite package manager:
bun install @tanstack/solid-query
npm install --save @tanstack/solid-query
yarn add @tanstack/solid-query
pnpm install @tanstack/solid-query
The best way to manage the connection state is to create a context provider that will handle the connection and provide the client to the rest of the application. In SolidJS, we can use the createContext
function to create a context and the createStore
function to manage the state.
First, create a new file, e.g., SurrealProvider.tsx
, which will contain the context provider and hooks for accessing the SurrealDB client. You can copy the following code, which takes care of managing the connection.
Feel free to make any changes to fit your project structure and coding style.
NoteWe do recommend adding this in a
lib/providers
directory, but you can place it wherever you see fit.
src/lib/providers/SurrealProvider.tsximport { useContext, createContext, JSX, createSignal, createEffect, onCleanup, Accessor, onMount } from "solid-js"; import Surreal from "surrealdb"; import { createMutation } from "@tanstack/solid-query"; import { createStore } from "solid-js/store"; interface SurrealProviderProps { children: JSX.Element; /** The database endpoint URL */ endpoint: string; /** Optional existing Surreal client */ client?: Surreal; /* Optional connection parameters */ params?: Parameters<Surreal["connect"]>[1]; /** Auto connect on component mount, defaults to true */ autoConnect?: boolean; } interface SurrealProviderState { /** The Surreal instance */ client: Accessor<Surreal>; /** Whether the connection is pending */ isConnecting: Accessor<boolean>; /** Whether the connection was successfully established */ isSuccess: Accessor<boolean>; /** Whether the connection rejected in an error */ isError: Accessor<boolean>; /** The connection error, if present */ error: Accessor<unknown|null>; /** Connect to the Surreal instance */ connect: () => Promise<void>; /** Close the Surreal instance */ close: () => Promise<true>; } // Store interface to track the Surreal instance and connection status interface SurrealProviderStore { instance: Surreal; status: "connecting" | "connected" | "disconnected"; } const SurrealContext = createContext<SurrealProviderState>(); export function SurrealProvider(props: SurrealProviderProps) { // Initialize store with either provided client or new instance const [store, setStore] = createStore<SurrealProviderStore>({ instance: props.client ?? new Surreal(), status: "disconnected" }); // Use TanStack Query's mutation hook to manage the async connection state const { mutateAsync, isError, error, reset } = createMutation(() => ({ mutationFn: async () => { setStore("status", "connecting"); await store.instance.connect(props.endpoint, props.params); } })); // Effect to handle auto-connection and cleanup createEffect(() => { // Connect automatically if autoConnect is true if(props.autoConnect) { mutateAsync(); } // Cleanup function to reset mutation state and close connection onCleanup(() => { reset(); store.instance.close(); }); }); // Subscribe to connection events when component mounts onMount(() => { // Update store status when connection is established store.instance.emitter.subscribe("connected", () => { setStore("status", "connected"); }); // Update store status when connection is lost store.instance.emitter.subscribe("disconnected", () => { setStore("status", "disconnected"); }); }); // Create the value object that will be provided through context const providerValue: SurrealProviderState = { client: () => store.instance, close: () => store.instance.close(), connect: mutateAsync, error: () => error, isConnecting: () => store.status === "connecting", isError: () => isError, isSuccess: () => store.status === "connected" }; return ( <SurrealContext.Provider value={providerValue}> {props.children} </SurrealContext.Provider> ); } // Custom hook to access the SurrealDB context export function useSurreal(): SurrealProviderState { const context = useContext(SurrealContext); // Ensure the hook is used within a provider if(!context) { throw new Error("useSurreal must be used within a SurrealProvider"); } return context; }
After creating the SurrealProvider, you’ll need to wrap your application’s root component with it. Here’s an example of how to set up your App.tsx
component with the necessary providers. We’re using both the QueryClientProvider
for managing async state and our custom SurrealProvider
for database connectivity, along with routing and authentication setup:
src/App.tsximport type { Component } from 'solid-js'; import { Router, Route } from "@solidjs/router"; import { SurrealProvider } from './lib/providers/SurrealProvider'; import { QueryClientProvider } from '@tanstack/solid-query'; import { tanstackClient } from './lib/query-client'; import { AuthProvider } from './lib/providers/auth'; import { AppLayout } from './components/layout/app'; const App: Component = () => { return ( <QueryClientProvider client={tanstackClient}> <SurrealProvider endpoint={import.meta.env.VITE_DB_HOST} autoConnect params={{ namespace: "surrealdb", database: "pollwebapp", }} > <AuthProvider> <Router> <Route path="/" component={AppLayout}> ... your routes here </Route> </Router> </AuthProvider> </SurrealProvider> </QueryClientProvider> ); }; export default App;
useSurreal
Hook in Your ComponentsNow you can use the useSurreal
hook in your components to access the SurrealDB client. For example, you can create an AuthProvider
to handle authentication and user management.
src/lib/providers/AuthProvider.tsximport { Accessor, createContext, ParentProps, useContext } from "solid-js"; import { createStore } from "solid-js/store"; import { useSurreal } from "../lib/providers/SurrealProvider"; interface AuthProviderState { user: Accessor<UserRecord | undefined>; login: (email: string, password: string) => Promise<void>; register: (data: Omit<UserRecord, "id">) => Promise<void>; logout: () => Promise<void>; } interface AuthProviderStore { user: UserRecord | undefined; status: "idle" | "signing-in" | "signing-up" | "signing-out" | "signed-in" | "signed-up"; } const AuthContext = createContext<AuthProviderState>(); export function AuthProvider(props: ParentProps) { const { client, close, connect } = useSurreal(); const [store, setStore] = createStore<AuthProviderStore>({ user: undefined, status: "idle" }); const login = async (email: string, password: string) => { const db = client(); setStore("status", "signing-in"); await db.signin({ access: "user", namespace: "surrealdb", database: "pollwebapp", variables: { email: email, pass: password } }); setStore("status", "signed-up"); }; const register = async (data: Omit<UserRecord, "id">) => { const db = client(); setStore("status", "signing-up"); await db.signup({ access: "user", namespace: "surrealdb", database: "pollwebapp", variables: data }); setStore("status", "signed-up"); }; const logout = async () => { setStore("status", "signing-out"); await close(); await connect(); setStore("status", "idle"); }; const providerValue: AuthProviderState = { user: () => store.user, login, register, logout }; return ( <AuthContext.Provider value={providerValue}> {props.children} </AuthContext.Provider> ); } export function useAuth(): AuthProviderState { const ctx = useContext(AuthContext); if(!ctx) { throw new Error("useAuth must be used within an AuthProvider"); } return ctx; }
Now that you have SurrealDB integrated into your SolidJS project, you’re ready to start building! To learn more about interacting with your database: