[TERMINAL · SKILLS]
> mounting /skills...
> indexing 295 manifests...
> linking agents: claude · codex · gemini · cursor
> ready.
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░] 0%
Terminal.skills
Use Cases/Build an API Playground

Build an API Playground

Build an interactive API playground where developers can explore endpoints, edit request parameters, see live responses, save and share snippets, and auto-generate code examples in multiple languages.

#redis#caching#database#pub-sub#queues
Works with:claude-codeopenai-codexgemini-clicursor

Skills stack · 6 skills

Avg quality 93/100·All SAFE
>

typescript

v

Not yet scored
View skill
>

redis

v1.0.0

Build applications with Redis — caching, session storage, pub/sub, streams, rate limiting, leaderboards, and queues. Use when tasks involve in-memory data storage, real-time messaging, distributed locking, or performance optimization with caching layers.

93/100 quality
1.81× impact
SAFE
View skill
>

postgresql

v1.0.0

Assists with designing schemas, writing performant queries, managing indexes, and operating PostgreSQL databases. Use when working with JSONB, full-text search, window functions, CTEs, row-level security, replication, or performance tuning. Trigger words: postgresql, postgres, sql, database, jsonb, rls, window functions, cte.

87/100 quality
1.53× impact
SAFE
View skill
>

hono

v1.0.0

You are an expert in Hono, the ultrafast web framework for the edge. You help developers build APIs and web applications that run on Cloudflare Workers, Deno, Bun, Node.js, AWS Lambda, and Vercel Edge — with a tiny footprint (~14KB), middleware ecosystem, JSX support, RPC client, and Web Standards API compatibility that makes code truly portable across runtimes.

93/100 quality
3.00× impact
SAFE
View skill
>

zod

v1.0.0

You are an expert in Zod, the TypeScript-first schema declaration and validation library. You help developers define schemas that validate data at runtime AND infer TypeScript types at compile time — eliminating the need to write types and validators separately. Used for API input validation, form validation, environment variables, config files, and any data boundary.

100/100 quality
1.21× impact
SAFE
View skill
>

nextjs

v1.0.0

Assists with building production-grade React applications using Next.js. Use when working with the App Router, Server Components, Server Actions, Middleware, or deploying to Vercel or self-hosted environments. Trigger words: nextjs, next.js, app router, server components, server actions, react framework, ssr, isr.

93/100 quality
1.16× impact
SAFE
View skill
$

The Problem

Rosa leads developer relations at a 30-person API company. Their docs have curl examples, but developers still spend 30 minutes figuring out authentication, headers, and request bodies. Support tickets are 60% "how do I call this endpoint?" They tried Swagger UI but it's clunky — no saved history, no code generation, no way to share a working request with a teammate. They need an interactive playground where developers can explore the API in the browser, see real responses, and copy working code.

Step 1: Build the Playground Engine

typescript
// src/playground/executor.ts — API playground with sandboxed execution and code generation
import { pool } from "../db";
import { Redis } from "ioredis";
import { z } from "zod";

const redis = new Redis(process.env.REDIS_URL!);

interface APIEndpoint {
  id: string;
  method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
  path: string;
  summary: string;
  description: string;
  parameters: EndpointParam[];
  requestBody: RequestBodySchema | null;
  responses: Record<string, ResponseSchema>;
  auth: "none" | "api_key" | "bearer" | "oauth2";
  rateLimit: { requests: number; window: string };
}

interface EndpointParam {
  name: string;
  in: "path" | "query" | "header";
  type: string;
  required: boolean;
  description: string;
  example: any;
  enum?: string[];
}

interface RequestBodySchema {
  contentType: string;
  schema: Record<string, any>;
  example: any;
}

interface ResponseSchema {
  description: string;
  example: any;
}

interface PlaygroundRequest {
  endpointId: string;
  method: string;
  path: string;
  headers: Record<string, string>;
  queryParams: Record<string, string>;
  body: any;
  apiKey?: string;
}

interface PlaygroundResponse {
  status: number;
  statusText: string;
  headers: Record<string, string>;
  body: any;
  latencyMs: number;
  size: number;
}

interface SavedSnippet {
  id: string;
  name: string;
  request: PlaygroundRequest;
  response: PlaygroundResponse;
  createdBy: string;
  isPublic: boolean;
  shareUrl: string;
  createdAt: string;
}

// Execute playground request (proxied through our server for CORS)
export async function executeRequest(
  request: PlaygroundRequest,
  userId: string
): Promise<PlaygroundResponse> {
  // Rate limit playground usage
  const rateKey = `playground:rate:${userId}`;
  const count = await redis.incr(rateKey);
  await redis.expire(rateKey, 60);

  if (count > 30) {
    return { status: 429, statusText: "Too Many Requests", headers: {}, body: { error: "Playground rate limit: 30 requests/minute" }, latencyMs: 0, size: 0 };
  }

  // Build the actual API URL
  const baseUrl = process.env.API_BASE_URL!;
  let url = `${baseUrl}${request.path}`;

  // Apply query params
  const queryString = new URLSearchParams(request.queryParams).toString();
  if (queryString) url += `?${queryString}`;

  // Build headers
  const headers: Record<string, string> = {
    "Content-Type": "application/json",
    ...request.headers,
  };

  // Apply auth
  if (request.apiKey) {
    headers["Authorization"] = `Bearer ${request.apiKey}`;
  }

  const start = Date.now();
  try {
    const fetchOptions: RequestInit = {
      method: request.method,
      headers,
      signal: AbortSignal.timeout(10000), // 10s timeout
    };

    if (request.body && ["POST", "PUT", "PATCH"].includes(request.method)) {
      fetchOptions.body = JSON.stringify(request.body);
    }

    const res = await fetch(url, fetchOptions);
    const responseBody = await res.json().catch(() => res.text());
    const latencyMs = Date.now() - start;

    const responseHeaders: Record<string, string> = {};
    res.headers.forEach((v, k) => { responseHeaders[k] = v; });

    const response: PlaygroundResponse = {
      status: res.status,
      statusText: res.statusText,
      headers: responseHeaders,
      body: responseBody,
      latencyMs,
      size: JSON.stringify(responseBody).length,
    };

    // Save to history
    await redis.rpush(`playground:history:${userId}`, JSON.stringify({
      request, response, timestamp: new Date().toISOString(),
    }));
    await redis.ltrim(`playground:history:${userId}`, -50, -1);

    return response;
  } catch (err: any) {
    return {
      status: 0,
      statusText: "Error",
      headers: {},
      body: { error: err.message },
      latencyMs: Date.now() - start,
      size: 0,
    };
  }
}

// Generate code snippet in multiple languages
export function generateCodeSnippet(
  request: PlaygroundRequest,
  language: "curl" | "javascript" | "python" | "go" | "ruby" | "php"
): string {
  const baseUrl = process.env.API_BASE_URL!;
  let url = `${baseUrl}${request.path}`;
  const qs = new URLSearchParams(request.queryParams).toString();
  if (qs) url += `?${qs}`;

  switch (language) {
    case "curl": {
      let cmd = `curl -X ${request.method} '${url}'`;
      for (const [k, v] of Object.entries(request.headers)) {
        cmd += ` \\\n  -H '${k}: ${v}'`;
      }
      if (request.apiKey) cmd += ` \\\n  -H 'Authorization: Bearer ${request.apiKey}'`;
      if (request.body) cmd += ` \\\n  -d '${JSON.stringify(request.body, null, 2)}'`;
      return cmd;
    }

    case "javascript": {
      const opts: any = { method: request.method, headers: { ...request.headers } };
      if (request.apiKey) opts.headers["Authorization"] = `Bearer ${request.apiKey}`;
      if (request.body) opts.body = "JSON.stringify(body)";

      return `const body = ${JSON.stringify(request.body, null, 2)};

const response = await fetch("${url}", {
  method: "${request.method}",
  headers: ${JSON.stringify(opts.headers, null, 4)},${request.body ? '\n  body: JSON.stringify(body),' : ''}
});

const data = await response.json();
console.log(data);`;
    }

    case "python": {
      const headers = { ...request.headers };
      if (request.apiKey) headers["Authorization"] = `Bearer ${request.apiKey}`;

      return `import requests

response = requests.${request.method.toLowerCase()}(
    "${url}",
    headers=${JSON.stringify(headers, null, 4)},${request.body ? `\n    json=${JSON.stringify(request.body, null, 4)},` : ''}
)

print(response.json())`;
    }

    case "go": {
      return `package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "net/http"
    "io"
)

func main() {
    ${request.body ? `body, _ := json.Marshal(${JSON.stringify(request.body)})
    req, _ := http.NewRequest("${request.method}", "${url}", bytes.NewBuffer(body))` : `req, _ := http.NewRequest("${request.method}", "${url}", nil)`}
    ${request.apiKey ? `req.Header.Set("Authorization", "Bearer ${request.apiKey}")` : ''}
    req.Header.Set("Content-Type", "application/json")

    resp, _ := http.DefaultClient.Do(req)
    defer resp.Body.Close()
    data, _ := io.ReadAll(resp.Body)
    fmt.Println(string(data))
}`;
    }

    default:
      return `// Code generation for ${language} not implemented`;
  }
}

// Save and share a snippet
export async function saveSnippet(
  name: string,
  request: PlaygroundRequest,
  response: PlaygroundResponse,
  userId: string,
  isPublic: boolean
): Promise<SavedSnippet> {
  const id = `snip-${Date.now().toString(36)}${Math.random().toString(36).slice(2, 6)}`;

  const snippet: SavedSnippet = {
    id, name, request, response,
    createdBy: userId, isPublic,
    shareUrl: `${process.env.APP_URL}/playground/s/${id}`,
    createdAt: new Date().toISOString(),
  };

  await pool.query(
    `INSERT INTO playground_snippets (id, name, request, response, created_by, is_public, created_at)
     VALUES ($1, $2, $3, $4, $5, $6, NOW())`,
    [id, name, JSON.stringify(request), JSON.stringify(response), userId, isPublic]
  );

  return snippet;
}

// Get request history for a user
export async function getHistory(userId: string): Promise<Array<{
  request: PlaygroundRequest;
  response: PlaygroundResponse;
  timestamp: string;
}>> {
  const items = await redis.lrange(`playground:history:${userId}`, 0, -1);
  return items.map((item) => JSON.parse(item)).reverse();
}

Results

  • Support tickets down 55% — "how do I call this endpoint?" replaced by "click Try It, see it work"; developers are self-serve
  • Time to first API call: 30 min → 3 min — prefilled examples with working auth; developers click "Send" and see the response immediately
  • Code snippets in 4 languages — developer copies working Python/JS/curl/Go code directly; no more translating curl to their language manually
  • Shareable snippets — developer gets a working request, saves it, shares the URL with their team; onboarding new team members takes minutes not hours
  • Request history — developers don't re-enter parameters; last 50 requests saved; iterate faster on complex queries