Documentation
Query the database
Client

Client queries

@ponder/client and @ponder/react are TypeScript client packages for querying a Ponder app with end-to-end type safety and live updates.

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 schema from "ponder:schema";
import { Hono } from "hono";
import { client, graphql } from "ponder";
 
const app = new Hono();
 
app.use("/graphql", graphql({ db, schema }));
app.use(client({ db }));
 
export default app;

@ponder/client

The @ponder/client package works in any JavaScript environment, including the browser, server-side scripts, and both client and server code from web frameworks like Next.js. If you're using a React framework, use the @ponder/react package instead.

Guide

Installation

Install the @ponder/client package in your client project.

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 });

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) => {
    // ...
  }
);

client.getStatus()

Get the indexing progress of each chain.

import { createClient } from "@ponder/client";
import * as schema from "../../ponder/ponder.schema";
 
const client = createClient("https://...", { schema });
 
const status = await client.getStatus();

Query from React

The @ponder/react package includes React hooks for subscribing to live updates from your database. It uses @ponder/client and Tanstack Query under the hood.

components/Deposits.tsx
import { usePonderQuery } from "@ponder/react";
 
const { data, isError, isPending } = usePonderQuery({
  queryFn: (db) => db.select().from(schema.depositEvent).limit(10),
});

usePonderQuery uses TanStack Query for async state management. queryFn is overriden to include a database object, similar to the client.db method, and queryKey is automatically generated. All other options are preserved.

Guide

Installation

Install @ponder/react and peer dependencies in your React project.

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

Create client

Create a client object using the URL of your Ponder server. Import your schema into the same file using a relative import from your ponder.schema.ts file.

lib/ponder.ts
import { createClient } from "@ponder/client";
import * as schema from "../../ponder/ponder.schema";
 
const client = createClient("http://localhost:42069", { schema });
 
export { client, 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(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>
}

Example projects

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