Documentation
Query the database
Client

Client queries

The @ponder/client package is a TypeScript client for querying a Ponder app with end-to-end type safety.

Enable on the server

Client queries are available starting from version 0.9.0. Read the migration guide for more details.

Register the client middleware on your Ponder server to enable client queries.

src/api/index.ts
import { db } from "ponder:api";
import { Hono } from "hono";
import { client } from "ponder";
 
const app = new Hono();
 
app.use(client({ db }));
 
export default app;

Query from React

The @ponder/react package includes React hooks for subscribing to live updates from your database.

đź’ˇ

For an example of how to use @ponder/client with @tanstack/react-query for an automatically updating React hook, see the Next.js example.

Installation

Install @ponder/react and @tanstack/react-query in your React project.

shell
pnpm add @ponder/react @tanstack/react-query

Create client

Create a client using the URL of your Ponder server. Import your Ponder schema into the same file.

lib/ponder.ts
import { createClient } from "@ponder/client";
import * as schema from "../../ponder/ponder.schema";
 
export const client = createClient("http://localhost:42069", { schema });

Wrap app in provider

Wrap your app with the PonderProvider React Context Provider and include the client object you just created.

app.tsx
import { PonderProvider } from '@ponder/react'
import { client } from './lib/ponder'
 
function App() {
  return (
    <PonderProvider client={client}>
      {/** ... */}
    </PonderProvider>
  )
}

Setup TanStack Query

Inside the PonderProvider, wrap your app in a TanStack Query React Context Provider. Read more about setting up TanStack Query.

app.tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { PonderProvider } from '@ponder/react'
import { client } from './lib/ponder'
 
const queryClient = new QueryClient()
 
function App() {
  return (
    <PonderProvider client={client}>
      <QueryClientProvider client={queryClient}>
        {/** ... */}
      </QueryClientProvider>
    </PonderProvider>
  )
}

Use the hook

components/Deposits.tsx
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(desc(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>
}

Query from Node.js

Install @ponder/client

Install the @ponder/client package in your client project. This package works in any JavaScript environment, including the browser, server-side scripts, and both client and server code from web frameworks like Next.js.

shell
pnpm add @ponder/client

Setup client

Create a client using the URL of your Ponder server. Import your Ponder schema into the same file.

import { createClient } from "@ponder/client";
import * as schema from "../../ponder/ponder.schema";
 
const client = createClient("http://localhost:42069", { schema });
 
export { client, schema };

Run a query

Use the client.db method to execute a SELECT statement using Drizzle. The query builder is fully type-safe to provide static query validation and inferred result types.

import { createClient } from "@ponder/client";
import * as schema from "../../ponder/ponder.schema";
 
const client = createClient("https://...", { schema });
 
const result = await client.db.select().from(schema.account).limit(10);

API reference

client.db

This method uses a readonly database role, with strict query limits. It provides a Drizzle query builder, similar to API functions.

import { createClient } from "@ponder/client";
import * as schema from "../../ponder/ponder.schema";
 
const client = createClient("https://...", { schema });
 
const result = await client.db.select().from(schema.account).limit(10);

client.live

Subscribe to live updates.

When you initiate a live query, the client opens a persistent HTTP connection with the server using Server-Sent Events (SSE). Then, the server notifies the client whenever a new block gets indexed. If a query result is no longer valid, the client will immediately refetch it to receive the latest result. This approach achieves low-latency updates with minimal network traffic.

To avoid browser quotas, each client instance uses at most one SSE connection at a time.

import { createClient } from "@ponder/client";
import * as schema from "../../ponder/ponder.schema";
 
const client = createClient("https://...", { schema });
 
const { unsubscribe } = client.live(
  (db) => db.select().from(schema.account),
  (result) => {
    // ...
  },
  (error) => {
    // ...
  }
);

Status

The status table contains information about the indexing status of each chain.

import { createClient, status } from "@ponder/client";
 
const client = createClient("https://...");
 
const mainnetStatus = client.db
  .select()
  .from(status)
  .where(eq(status.chainId, 1));

Example projects

These example apps demonstrate how to use @ponder/client.