Skip to content

Repository files navigation

πŸš€ tRPC Playground Plus

npm version License: MIT Downloads

Enhanced interactive playground for tRPC with tabs, request management, and much more.

✨ Features

  • πŸ“‹ Intuitive and modern user interface
  • πŸ“‘ Tab system to organize your queries/mutations
  • πŸ’Ύ Export and import queries to share with your team
  • πŸ”„ Default data loading for new users (Tabs, Headers, etc.)
  • πŸ”§ HTTP headers customization (global & per-tab)
  • 🧬 Variables support with type validation (global & per-tab)
  • 🌱 Environment variables injection (read-only, provided by the server)
  • πŸ•‘ Request history β€” replay, view, and diff (input & output) past calls side-by-side
  • ✨ Smart autocomplete & inline linting based on your tRPC schema β€” deep support for nested objects, arrays, discriminated unions, and schema constraints (min/max, patterns, formats…)
  • ⌨️ Configurable keyboard shortcuts (run & search)
  • 🎨 Light & dark themes
  • βš™οΈ Customizable settings (font size, timeout, split, history size, shortcuts)
  • πŸͺ„ Built-in code formatter
  • 🏒 Monorepo-friendly (isolate data per project)
  • πŸ”Œ Adapters for Fastify, Express, Koa and Next.js (App Router)

πŸ› οΈ Coming Soon

  • 🌈 Support for more frameworks (Hono…)
  • πŸ“‘ Subscriptions support (WebSocket)
  • ...and much more!

Feel free to suggest ideas or contribute on GitHub !

πŸ“¦ Installation

It's a dev tool, so install it as a dev dependency:

# npm
npm install --save-dev trpc-playground-plus

# yarn
yarn add --dev trpc-playground-plus

# pnpm
pnpm add --save-dev trpc-playground-plus

πŸš€ Quick Start

With Fastify
import { createFastifyAdapter } from 'trpc-playground-plus/adapters/fastify';
import { fastify } from 'fastify';
import { appRouter } from './router';

const app = fastify();

// Playground configuration
await createFastifyAdapter({
  app,
  trpcEndpoint: 'http://localhost:3000/api/trpc',
  router: appRouter,
  playgroundEndpoint: '/playground'
});

// Start server
await app.listen({ port: 3000 });
console.log('πŸš€ Server available at http://localhost:3000');
console.log('πŸš€ Playground available at http://localhost:3000/playground');
With Express
import { createExpressAdapter } from 'trpc-playground-plus/adapters/express';
import express from 'express';
import { appRouter } from './router';

const app = express();

// Playground configuration
createExpressAdapter({
  app,
  trpcEndpoint: 'http://localhost:3000/api/trpc',
  router: appRouter,
  playgroundEndpoint: '/playground'
});

// Start server
app.listen(3000, () => {
  console.log('πŸš€ Server available at http://localhost:3000');
  console.log('πŸš€ Playground available at http://localhost:3000/playground');
});

Works with Express 4 and 5. Static assets are served with the built-in express.static, so there is nothing else to install.

With Koa
import { createKoaAdapter } from 'trpc-playground-plus/adapters/koa';
import Koa from 'koa';
import { appRouter } from './router';

const app = new Koa();

// Playground configuration
createKoaAdapter({
  app,
  trpcEndpoint: 'http://localhost:3000/api/trpc',
  router: appRouter,
  playgroundEndpoint: '/playground'
});

// Start server
app.listen(3000, () => {
  console.log('πŸš€ Server available at http://localhost:3000');
  console.log('πŸš€ Playground available at http://localhost:3000/playground');
});

Works with Koa 2 and 3. The adapter is a single middleware that matches its own routes and serves the playground assets itself, so neither @koa/router nor koa-static is required.

Koa runs middleware in registration order, so register the adapter before any catch-all middleware β€” otherwise the playground requests never reach it.

With Next.js (App Router)
// app/playground/[[...slug]]/route.ts
import { createNextHandler } from 'trpc-playground-plus/adapters/next';
import { appRouter } from '@/server/router';

export const { GET } = createNextHandler({
  router: appRouter,
  trpcEndpoint: '/api/trpc',
});

That's it β€” no next.config change is needed: the playground assets are bundled inside the adapter, so it works the same on Vercel, in standalone output and in Docker.

A few notes:

  • The route must be an optional catch-all ([[...slug]]) so the handler also receives /playground/app.js and /playground/config.
  • There is no app option: the file's location is the mount point. Move the folder (or set a basePath) and the playground follows, with no code change.
  • Requires Next 13.4+ with the App Router. Next itself is never imported β€” the handler is a plain Web Request β†’ Response function β€” so there is no peer dependency to install.

πŸ“‹ Loading Default Queries

Method: Configuration via an object or Json file

import { createFastifyAdapter } from 'trpc-playground-plus/adapters/fastify';

const myData = {
  tabs: [
    {
      id: "tab-1",
      title: "Get all users",
      content: "trpc.user.getAll.query()",
      isActive: true
    },
  ],
  headers: [
    {
      key: "Authorization",
      value: "Bearer your-token-here",
      enabled: true
    }
  ],
};

await createFastifyAdapter({
  app: fastify,
  trpcEndpoint: '/api/trpc',
  playgroundEndpoint: '/playground',
  router: appRouter,
  defaultData: myData // <- defaultData is optional but recommended for new user
});

🧩 Configuration Options

Option Type Description Default
app FastifyInstance | Express | Koa Server instance the playground is mounted on (required, except Next.js)
trpcEndpoint string tRPC API Endpoint (required)
router Router tRPC Router (required)
playgroundEndpoint string Playground path (Next.js: derived from the route, override only if needed) /playground
transformer 'superjson' Data transformer used by your tRPC client undefined
defaultData ExportData Default tabs/headers to bootstrap the playground undefined
envVariables Record<string, unknown> Read-only variables injected by the server, usable in queries by their key (like any variable) undefined
projectKey string Prefix for localStorage keys (monorepo isolation) undefined

🏒 Monorepo Support

If you use trpc-playground-plus in multiple projects served on the same domain (typical in a monorepo), the localStorage data would normally collide. Set a unique projectKey per project to isolate them:

// App A
await createAdapter({
  app,
  trpcEndpoint: '/api/trpc',
  router: appRouter,
  projectKey: 'app-a',
});

// App B (different project in same monorepo)
await createAdapter({
  app,
  trpcEndpoint: '/api/trpc',
  router: appRouter,
  projectKey: 'app-b',
});

localStorage keys are then prefixed (e.g. app-a:trpc-playground-tabs), avoiding collisions. The projectKey is also embedded in exported JSON files so imports can warn when data is brought over from a different project.

πŸ”§ Compatibility

Compatible with tRPC v11+ and zod 4.

Adapters are exposed as separate entry points, so only the framework you import is pulled in. The matching framework package is an optional peer dependency β€” install it in your own app:

Adapter Entry point Peer dependencies
Fastify trpc-playground-plus/adapters/fastify fastify ^5, @fastify/static ^8
Express trpc-playground-plus/adapters/express express ^4.18 || ^5
Koa trpc-playground-plus/adapters/koa none (Koa 2 or 3)
Next.js trpc-playground-plus/adapters/next none (Next 13.4+, App Router)

❓ Why this project?

During a project, we encountered limitations with the trpc-playground solution, which is no longer maintained. It started as a proof of concept (POC) to address the specific needs we had β€” but it has since grown well beyond that into a real, actively maintained alternative for exploring and testing tRPC APIs, with a modern UI, smart autocomplete & linting, request history, environment variables, and an extensible multi-adapter architecture.

πŸ“„ License

MIT Β© RΓ©my 'Raesta' Mulet

About

Enhanced interactive playground for tRPC with tabs, request management, and much more.

Resources

Stars

11 stars

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages