API endpoints
Ponder supports custom API endpoints with direct access to the database and other useful resources.
Custom API endpoints offer more flexibility than GraphQL or SQL client queries, making it possible to serve web requests with complex SQL queries, data from external sources, authentication, and more.
Guide
Open src/api/index.ts
Ponder's API server uses Hono, a fast and lightweight HTTP router. The src/api/index.ts
file must default export a Hono instance.
import { Hono } from "hono";
const app = new Hono();
export default app;
Register a route handler
To customize the server, register routes and middleware on the Hono instance before exporting it.
import { Hono } from "hono";
const app = new Hono();
app.get("/hello", (c) => {
return c.text("Hello, world!");
});
export default app;
Test the endpoint
To test the endpoint, start the development server and visit the route in your browser.
Hello, world!
Hono supports all HTTP methods, custom middleware, cookies, JSX, and more. Visit the Hono documentation for more details.
Resources
The ponder:api
virtual module includes useful resources for custom API endpoints.
Database queries
The db
object exported from ponder:api
is a ready-to-use Drizzle database instance. This is the same database instance that powers GraphQL and SQL client queries.
Use the db
object to run read-only SQL queries within custom API endpoints.
import { db } from "ponder:api";
import { accounts } from "ponder:schema";
import { Hono } from "hono";
import { eq } from "ponder";
const app = new Hono();
app.get("/account/:address", async (c) => {
const address = c.req.param("address");
const account = await db
.select()
.from(accounts)
.where(eq(accounts.address, address));
return c.json(account);
});
export default app;
RPC requests
The publicClients
object exported from ponder:api
is a collection of Viem Public Client objects — one for each chain defined in ponder.config.ts
, using the same transports.
Use these clients to make RPC requests within custom API endpoints. Note that the publicClients
object is keyed by chain ID, not chain name.
import { publicClients } from "ponder:api";
import { Hono } from "hono";
const app = new Hono();
app.get("/balance/:address", async (c) => {
const address = c.req.param("address");
const balance = await publicClients[8453].getBalance({ address });
return c.json({ address, balance });
});
export default app;
More examples
- Basic usage - An app with custom endpoints serving ERC-20 data.
- Usage with offchain data - An app that includes data from offchain sources.