SQL client
Ponder natively supports SQL queries over HTTP using the @ponder/client
package.
The SQL client is a more powerful alternative to GraphQL that offers zero-codegen type inference, live queries, and the flexibility of SQL directly in your client code.
Guide
Register client
middleware
Use the client
Hono middleware to enable SQL client queries.
import { db } from "ponder:api";
import schema from "ponder:schema";
import { Hono } from "hono";
import { client } from "ponder";
const app = new Hono();
app.use("/sql/*", client({ db, schema }));
export default app;
Install @ponder/client
Install the @ponder/client
package in your client project.
pnpm add @ponder/client
Connect to the server
Use createClient
to connect to the server at the path where you registered the middleware.
Import your schema from ponder.schema.ts
into the same file. Read more about schema portability.
import { createClient } from "@ponder/client";
import * as schema from "../../ponder/ponder.schema";
const client = createClient("http://localhost:42069/sql", { schema });
export { client, schema };
Run a query
Use client.db.select()
to build a custom SQL query using Drizzle and execute it over HTTP.
import { desc } from "@ponder/client";
import { client, schema } from "../lib/ponder";
const result = await client.db
.select()
.from(schema.account)
.orderBy(desc(schema.account.balance))
.limit(10);
Guide (React)
The @ponder/react
package provides React hooks for subscribing to live updates from your database, powered by @ponder/client
. This package wraps TanStack Query, a popular library for managing async state in React.
Set up @ponder/client
First, follow the steps above to set up a SQL client instance using @ponder/client
.
Install dependencies
Install @ponder/react
and peer dependencies in your client project.
pnpm add @ponder/react @ponder/client @tanstack/react-query
Set up PonderProvider
Wrap your app with PonderProvider
and include the @ponder/client
instance. This makes the client instance available to all child components.
import { PonderProvider } from "@ponder/react";
import { client } from "../lib/ponder";
function App() {
return (
<PonderProvider client={client}>
{/** ... */}
</PonderProvider>
);
}
Set up TanStack Query
Inside the PonderProvider
, wrap your app with a TanStack Query Provider. If you're already using TanStack Query, you can skip this step. Read more about setting up TanStack Query.
import { PonderProvider } from "@ponder/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { client } from "../lib/ponder";
const queryClient = new QueryClient();
function App() {
return (
<PonderProvider client={client}>
<QueryClientProvider client={queryClient}>
{/** ... */}
</QueryClientProvider>
</PonderProvider>
);
}
Run a query
Use the usePonderQuery
hook in a client component to initiate a live query. The hook returns an ordinary TanStack Query result object.
Whenever the query result changes, the component will re-render with the new data.
import { usePonderQuery } from "@ponder/react";
import { schema } from "../lib/ponder";
export function Deposits() {
const { data, isError, isPending } = usePonderQuery({
queryFn: (db) =>
db.select()
.from(schema.depositEvent)
.orderBy(schema.depositEvent.timestamp)
.limit(10),
});
if (isPending) return <div>Loading deposits</div>;
if (isError) return <div>Error fetching deposits</div>;
return <div>Deposits: {data}</div>;
}
Frequently asked questions
Use schema from a different repo
The @ponder/client
package needs the onchainTable
objects exported by ponder.schema.ts
to properly compile the SQL queries client-side. If the client project is in a different repo from the Ponder project, it's not possible to import the schema file directly.
At the moment, the easiest workaround is to copy the entire ponder.schema.ts
file into the client project and install ponder
to get access to the onchainTable
function.
Security
Here are the measures taken by the client
middleware to prevent malicious queries & denial-of-service attacks. These measures aim to achieve a similar level of risk as the GraphQL API.
-
Read-only: Each query statement runs in a
READ ONLY
transaction using autocommit. -
Query validator: Each query is parsed using
libpg_query
and must pass the following checks.- The query AST root must be a
SELECT
statement. Queries containing multiple statements are rejected. - The query must only contain allowed AST node types and built-in SQL functions. For example,
SELECT
,WHERE
, andmax()
are allowed, butDELETE
,SET
, andpg_advisory_lock()
are not. Read more. - The query must not reference objects in schemas other than the current schema. Read more.
- The query AST root must be a
-
Resource limits: The database session uses the following resource limit settings.
SET work_mem = '512MB'; SET statement_timeout = '500ms'; SET lock_timeout = '500ms';
More examples
- Basic usage (
@ponder/client
only) - Usage with Next.js (
@ponder/client
and@ponder/react
)