{
  "schemaVersion": "1.0",
  "item": {
    "slug": "api-dev",
    "name": "API Development",
    "source": "tencent",
    "type": "skill",
    "category": "开发工具",
    "sourceUrl": "https://clawhub.ai/gitgoodordietrying/api-dev",
    "canonicalUrl": "https://clawhub.ai/gitgoodordietrying/api-dev",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadMode": "redirect",
    "downloadUrl": "/downloads/api-dev",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=api-dev",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "installMethod": "Manual import",
    "extraction": "Extract archive",
    "prerequisites": [
      "OpenClaw"
    ],
    "packageFormat": "ZIP package",
    "includedAssets": [
      "SKILL.md"
    ],
    "primaryDoc": "SKILL.md",
    "quickSetup": [
      "Download the package from Yavira.",
      "Extract the archive and review SKILL.md first.",
      "Import or place the package into your OpenClaw setup."
    ],
    "agentAssist": {
      "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
      "steps": [
        "Download the package from Yavira.",
        "Extract it into a folder your agent can access.",
        "Paste one of the prompts below and point your agent at the extracted folder."
      ],
      "prompts": [
        {
          "label": "New install",
          "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete."
        },
        {
          "label": "Upgrade existing",
          "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run."
        }
      ]
    },
    "sourceHealth": {
      "source": "tencent",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-04-23T16:43:11.935Z",
      "expiresAt": "2026-04-30T16:43:11.935Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=4claw-imageboard",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=4claw-imageboard",
        "contentDisposition": "attachment; filename=\"4claw-imageboard-1.0.1.zip\"",
        "redirectLocation": null,
        "bodySnippet": null
      },
      "scope": "source",
      "summary": "Source download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this source.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/api-dev"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    },
    "downloadPageUrl": "https://openagent3.xyz/downloads/api-dev",
    "agentPageUrl": "https://openagent3.xyz/skills/api-dev/agent",
    "manifestUrl": "https://openagent3.xyz/skills/api-dev/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/api-dev/agent.md"
  },
  "agentAssist": {
    "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
    "steps": [
      "Download the package from Yavira.",
      "Extract it into a folder your agent can access.",
      "Paste one of the prompts below and point your agent at the extracted folder."
    ],
    "prompts": [
      {
        "label": "New install",
        "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete."
      },
      {
        "label": "Upgrade existing",
        "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run."
      }
    ]
  },
  "documentation": {
    "source": "clawhub",
    "primaryDoc": "SKILL.md",
    "sections": [
      {
        "title": "API Development",
        "body": "Build, test, document, and debug HTTP APIs from the command line. Covers the full API lifecycle: scaffolding endpoints, testing with curl, generating OpenAPI docs, mocking services, and debugging."
      },
      {
        "title": "When to Use",
        "body": "Scaffolding new REST or GraphQL endpoints\nTesting APIs with curl or scripts\nGenerating or validating OpenAPI/Swagger specs\nMocking external APIs for development\nDebugging HTTP request/response issues\nLoad testing endpoints"
      },
      {
        "title": "GET requests",
        "body": "# Basic GET\ncurl -s https://api.example.com/users | jq .\n\n# With headers\ncurl -s -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Accept: application/json\" \\\n  https://api.example.com/users | jq .\n\n# With query params\ncurl -s \"https://api.example.com/users?page=2&limit=10\" | jq .\n\n# Show response headers too\ncurl -si https://api.example.com/users"
      },
      {
        "title": "POST/PUT/PATCH/DELETE",
        "body": "# POST JSON\ncurl -s -X POST https://api.example.com/users \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -d '{\"name\": \"Alice\", \"email\": \"alice@example.com\"}' | jq .\n\n# PUT (full replace)\ncurl -s -X PUT https://api.example.com/users/123 \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"name\": \"Alice Updated\", \"email\": \"alice@example.com\"}' | jq .\n\n# PATCH (partial update)\ncurl -s -X PATCH https://api.example.com/users/123 \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"name\": \"Alice V2\"}' | jq .\n\n# DELETE\ncurl -s -X DELETE https://api.example.com/users/123\n\n# POST form data\ncurl -s -X POST https://api.example.com/upload \\\n  -F \"file=@document.pdf\" \\\n  -F \"description=My document\""
      },
      {
        "title": "Debug requests",
        "body": "# Verbose output (see full request/response)\ncurl -v https://api.example.com/health 2>&1\n\n# Show only response headers\ncurl -sI https://api.example.com/health\n\n# Show timing breakdown\ncurl -s -o /dev/null -w \"DNS: %{time_namelookup}s\\nConnect: %{time_connect}s\\nTLS: %{time_appconnect}s\\nFirst byte: %{time_starttransfer}s\\nTotal: %{time_total}s\\n\" https://api.example.com/health\n\n# Follow redirects\ncurl -sL https://api.example.com/old-endpoint\n\n# Save response to file\ncurl -s -o response.json https://api.example.com/data"
      },
      {
        "title": "Bash test runner",
        "body": "#!/bin/bash\n# api-test.sh - Simple API test runner\nBASE_URL=\"${1:-http://localhost:3000}\"\nPASS=0\nFAIL=0\n\nassert_status() {\n  local method=\"$1\" url=\"$2\" expected=\"$3\" body=\"$4\"\n  local args=(-s -o /dev/null -w \"%{http_code}\" -X \"$method\")\n  if [ -n \"$body\" ]; then\n    args+=(-H \"Content-Type: application/json\" -d \"$body\")\n  fi\n  local status\n  status=$(curl \"${args[@]}\" \"$BASE_URL$url\")\n  if [ \"$status\" = \"$expected\" ]; then\n    echo \"PASS: $method $url -> $status\"\n    ((PASS++))\n  else\n    echo \"FAIL: $method $url -> $status (expected $expected)\"\n    ((FAIL++))\n  fi\n}\n\nassert_json() {\n  local url=\"$1\" jq_expr=\"$2\" expected=\"$3\"\n  local actual\n  actual=$(curl -s \"$BASE_URL$url\" | jq -r \"$jq_expr\")\n  if [ \"$actual\" = \"$expected\" ]; then\n    echo \"PASS: GET $url | jq '$jq_expr' = $expected\"\n    ((PASS++))\n  else\n    echo \"FAIL: GET $url | jq '$jq_expr' = $actual (expected $expected)\"\n    ((FAIL++))\n  fi\n}\n\n# Health check\nassert_status GET /health 200\n\n# CRUD tests\nassert_status POST /api/users 201 '{\"name\":\"Test\",\"email\":\"test@test.com\"}'\nassert_status GET /api/users 200\nassert_json /api/users '.[-1].name' 'Test'\nassert_status DELETE /api/users/1 204\n\n# Auth tests\nassert_status GET /api/admin 401\nassert_status GET /api/admin 403  # with wrong role\n\necho \"\"\necho \"Results: $PASS passed, $FAIL failed\"\n[ \"$FAIL\" -eq 0 ] && exit 0 || exit 1"
      },
      {
        "title": "Python test runner",
        "body": "#!/usr/bin/env python3\n\"\"\"api_test.py - API integration test suite.\"\"\"\nimport json, sys, urllib.request, urllib.error\n\nBASE = sys.argv[1] if len(sys.argv) > 1 else \"http://localhost:3000\"\nPASS = FAIL = 0\n\ndef request(method, path, body=None, headers=None):\n    \"\"\"Make an HTTP request, return (status, body_dict, headers).\"\"\"\n    url = f\"{BASE}{path}\"\n    data = json.dumps(body).encode() if body else None\n    hdrs = {\"Content-Type\": \"application/json\", \"Accept\": \"application/json\"}\n    if headers:\n        hdrs.update(headers)\n    req = urllib.request.Request(url, data=data, headers=hdrs, method=method)\n    try:\n        resp = urllib.request.urlopen(req)\n        body = json.loads(resp.read().decode()) if resp.read() else None\n    except urllib.error.HTTPError as e:\n        return e.code, None, dict(e.headers)\n    return resp.status, body, dict(resp.headers)\n\ndef test(name, fn):\n    \"\"\"Run a test function, track pass/fail.\"\"\"\n    global PASS, FAIL\n    try:\n        fn()\n        print(f\"  PASS: {name}\")\n        PASS += 1\n    except AssertionError as e:\n        print(f\"  FAIL: {name} - {e}\")\n        FAIL += 1\n\ndef assert_eq(actual, expected, msg=\"\"):\n    assert actual == expected, f\"got {actual}, expected {expected}. {msg}\"\n\n# --- Tests ---\nprint(f\"Testing {BASE}\\n\")\n\ntest(\"GET /health returns 200\", lambda: (\n    assert_eq(request(\"GET\", \"/health\")[0], 200)\n))\n\ntest(\"POST /api/users creates user\", lambda: (\n    assert_eq(request(\"POST\", \"/api/users\", {\"name\": \"Test\", \"email\": \"t@t.com\"})[0], 201)\n))\n\ntest(\"GET /api/users returns array\", lambda: (\n    assert_eq(type(request(\"GET\", \"/api/users\")[1]), list)\n))\n\ntest(\"GET /api/notfound returns 404\", lambda: (\n    assert_eq(request(\"GET\", \"/api/notfound\")[0], 404)\n))\n\nprint(f\"\\nResults: {PASS} passed, {FAIL} failed\")\nsys.exit(0 if FAIL == 0 else 1)"
      },
      {
        "title": "Generate from existing endpoints",
        "body": "# Scaffold an OpenAPI 3.0 spec from curl responses\n# Run this, then fill in the details\ncat > openapi.yaml << 'EOF'\nopenapi: \"3.0.3\"\ninfo:\n  title: My API\n  version: \"1.0.0\"\n  description: API description here\nservers:\n  - url: http://localhost:3000\n    description: Local development\npaths:\n  /health:\n    get:\n      summary: Health check\n      responses:\n        \"200\":\n          description: Service is healthy\n          content:\n            application/json:\n              schema:\n                type: object\n                properties:\n                  status:\n                    type: string\n                    example: ok\n  /api/users:\n    get:\n      summary: List users\n      parameters:\n        - name: page\n          in: query\n          schema:\n            type: integer\n            default: 1\n        - name: limit\n          in: query\n          schema:\n            type: integer\n            default: 20\n      responses:\n        \"200\":\n          description: List of users\n          content:\n            application/json:\n              schema:\n                type: array\n                items:\n                  $ref: \"#/components/schemas/User\"\n    post:\n      summary: Create user\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              $ref: \"#/components/schemas/CreateUser\"\n      responses:\n        \"201\":\n          description: User created\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/User\"\n        \"400\":\n          description: Validation error\n  /api/users/{id}:\n    get:\n      summary: Get user by ID\n      parameters:\n        - name: id\n          in: path\n          required: true\n          schema:\n            type: string\n      responses:\n        \"200\":\n          description: User details\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/User\"\n        \"404\":\n          description: Not found\ncomponents:\n  schemas:\n    User:\n      type: object\n      properties:\n        id:\n          type: string\n        name:\n          type: string\n        email:\n          type: string\n          format: email\n        createdAt:\n          type: string\n          format: date-time\n    CreateUser:\n      type: object\n      required:\n        - name\n        - email\n      properties:\n        name:\n          type: string\n        email:\n          type: string\n          format: email\n  securitySchemes:\n    bearerAuth:\n      type: http\n      scheme: bearer\n      bearerFormat: JWT\nEOF"
      },
      {
        "title": "Validate OpenAPI spec",
        "body": "# Using npx (no install needed)\nnpx @redocly/cli lint openapi.yaml\n\n# Quick check: is the YAML valid?\npython3 -c \"import yaml; yaml.safe_load(open('openapi.yaml'))\" && echo \"Valid YAML\""
      },
      {
        "title": "Quick mock with Python",
        "body": "#!/usr/bin/env python3\n\"\"\"mock_server.py - Lightweight API mock from OpenAPI-like config.\"\"\"\nimport json, http.server, re, sys\n\nPORT = int(sys.argv[1]) if len(sys.argv) > 1 else 8080\n\n# Define mock routes: (method, path_pattern) -> response\nROUTES = {\n    (\"GET\", \"/health\"): {\"status\": 200, \"body\": {\"status\": \"ok\"}},\n    (\"GET\", \"/api/users\"): {\"status\": 200, \"body\": [\n        {\"id\": \"1\", \"name\": \"Alice\", \"email\": \"alice@example.com\"},\n        {\"id\": \"2\", \"name\": \"Bob\", \"email\": \"bob@example.com\"},\n    ]},\n    (\"POST\", \"/api/users\"): {\"status\": 201, \"body\": {\"id\": \"3\", \"name\": \"Created\"}},\n    (\"GET\", r\"/api/users/\\w+\"): {\"status\": 200, \"body\": {\"id\": \"1\", \"name\": \"Alice\"}},\n    (\"DELETE\", r\"/api/users/\\w+\"): {\"status\": 204, \"body\": None},\n}\n\nclass MockHandler(http.server.BaseHTTPRequestHandler):\n    def _handle(self):\n        for (method, pattern), response in ROUTES.items():\n            if self.command == method and re.fullmatch(pattern, self.path.split('?')[0]):\n                self.send_response(response[\"status\"])\n                if response[\"body\"] is not None:\n                    self.send_header(\"Content-Type\", \"application/json\")\n                    self.end_headers()\n                    self.wfile.write(json.dumps(response[\"body\"]).encode())\n                else:\n                    self.end_headers()\n                return\n        self.send_response(404)\n        self.send_header(\"Content-Type\", \"application/json\")\n        self.end_headers()\n        self.wfile.write(json.dumps({\"error\": \"Not found\"}).encode())\n\n    do_GET = do_POST = do_PUT = do_PATCH = do_DELETE = _handle\n\n    def log_message(self, fmt, *args):\n        print(f\"{self.command} {self.path} -> {args[1] if len(args) > 1 else '?'}\")\n\nprint(f\"Mock server on http://localhost:{PORT}\")\nhttp.server.HTTPServer((\"\", PORT), MockHandler).serve_forever()\n\nRun: python3 mock_server.py 8080"
      },
      {
        "title": "Minimal REST API",
        "body": "// server.js - Minimal Express REST API\nconst express = require('express');\nconst app = express();\napp.use(express.json());\n\n// In-memory store\nconst items = new Map();\nlet nextId = 1;\n\n// CRUD endpoints\napp.get('/api/items', (req, res) => {\n  const { page = 1, limit = 20 } = req.query;\n  const all = [...items.values()];\n  const start = (page - 1) * limit;\n  res.json({ items: all.slice(start, start + +limit), total: all.length });\n});\n\napp.get('/api/items/:id', (req, res) => {\n  const item = items.get(req.params.id);\n  if (!item) return res.status(404).json({ error: 'Not found' });\n  res.json(item);\n});\n\napp.post('/api/items', (req, res) => {\n  const { name, description } = req.body;\n  if (!name) return res.status(400).json({ error: 'name required' });\n  const id = String(nextId++);\n  const item = { id, name, description: description || '', createdAt: new Date().toISOString() };\n  items.set(id, item);\n  res.status(201).json(item);\n});\n\napp.put('/api/items/:id', (req, res) => {\n  if (!items.has(req.params.id)) return res.status(404).json({ error: 'Not found' });\n  const item = { ...req.body, id: req.params.id, updatedAt: new Date().toISOString() };\n  items.set(req.params.id, item);\n  res.json(item);\n});\n\napp.delete('/api/items/:id', (req, res) => {\n  if (!items.has(req.params.id)) return res.status(404).json({ error: 'Not found' });\n  items.delete(req.params.id);\n  res.status(204).end();\n});\n\n// Error handler\napp.use((err, req, res, next) => {\n  console.error(err.stack);\n  res.status(500).json({ error: 'Internal server error' });\n});\n\nconst PORT = process.env.PORT || 3000;\napp.listen(PORT, () => console.log(`API running on http://localhost:${PORT}`));"
      },
      {
        "title": "Setup",
        "body": "mkdir my-api && cd my-api\nnpm init -y\nnpm install express\nnode server.js"
      },
      {
        "title": "Check if port is in use",
        "body": "# Linux/macOS\nlsof -i :3000\n# or\nss -tlnp | grep 3000\n\n# Kill process on port\nkill $(lsof -t -i :3000)"
      },
      {
        "title": "Test CORS",
        "body": "# Preflight request\ncurl -s -X OPTIONS https://api.example.com/users \\\n  -H \"Origin: http://localhost:3000\" \\\n  -H \"Access-Control-Request-Method: POST\" \\\n  -H \"Access-Control-Request-Headers: Content-Type\" \\\n  -I"
      },
      {
        "title": "Watch for response time regressions",
        "body": "# Quick benchmark (10 requests)\nfor i in $(seq 1 10); do\n  curl -s -o /dev/null -w \"%{time_total}\\n\" http://localhost:3000/api/users\ndone | awk '{sum+=$1; if($1>max)max=$1} END {printf \"Avg: %.3fs, Max: %.3fs\\n\", sum/NR, max}'"
      },
      {
        "title": "Inspect JWT tokens",
        "body": "# Decode JWT payload (no verification)\necho \"$TOKEN\" | cut -d. -f2 | base64 -d 2>/dev/null | jq ."
      },
      {
        "title": "Tips",
        "body": "Use jq for JSON response processing: curl -s url | jq '.items[] | {id, name}'\nSet Content-Type header on every request with a body - missing it causes silent 400s\nUse -w '\\n' with curl to ensure output ends with a newline\nFor large response bodies, pipe to jq -C . | less -R for colored paging\nTest error paths: invalid JSON, missing fields, wrong types, unauthorized, not found\nFor WebSocket testing: npx wscat -c ws://localhost:3000/ws"
      }
    ],
    "body": "API Development\n\nBuild, test, document, and debug HTTP APIs from the command line. Covers the full API lifecycle: scaffolding endpoints, testing with curl, generating OpenAPI docs, mocking services, and debugging.\n\nWhen to Use\nScaffolding new REST or GraphQL endpoints\nTesting APIs with curl or scripts\nGenerating or validating OpenAPI/Swagger specs\nMocking external APIs for development\nDebugging HTTP request/response issues\nLoad testing endpoints\nTesting APIs with curl\nGET requests\n# Basic GET\ncurl -s https://api.example.com/users | jq .\n\n# With headers\ncurl -s -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Accept: application/json\" \\\n  https://api.example.com/users | jq .\n\n# With query params\ncurl -s \"https://api.example.com/users?page=2&limit=10\" | jq .\n\n# Show response headers too\ncurl -si https://api.example.com/users\n\nPOST/PUT/PATCH/DELETE\n# POST JSON\ncurl -s -X POST https://api.example.com/users \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -d '{\"name\": \"Alice\", \"email\": \"alice@example.com\"}' | jq .\n\n# PUT (full replace)\ncurl -s -X PUT https://api.example.com/users/123 \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"name\": \"Alice Updated\", \"email\": \"alice@example.com\"}' | jq .\n\n# PATCH (partial update)\ncurl -s -X PATCH https://api.example.com/users/123 \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"name\": \"Alice V2\"}' | jq .\n\n# DELETE\ncurl -s -X DELETE https://api.example.com/users/123\n\n# POST form data\ncurl -s -X POST https://api.example.com/upload \\\n  -F \"file=@document.pdf\" \\\n  -F \"description=My document\"\n\nDebug requests\n# Verbose output (see full request/response)\ncurl -v https://api.example.com/health 2>&1\n\n# Show only response headers\ncurl -sI https://api.example.com/health\n\n# Show timing breakdown\ncurl -s -o /dev/null -w \"DNS: %{time_namelookup}s\\nConnect: %{time_connect}s\\nTLS: %{time_appconnect}s\\nFirst byte: %{time_starttransfer}s\\nTotal: %{time_total}s\\n\" https://api.example.com/health\n\n# Follow redirects\ncurl -sL https://api.example.com/old-endpoint\n\n# Save response to file\ncurl -s -o response.json https://api.example.com/data\n\nAPI Test Scripts\nBash test runner\n#!/bin/bash\n# api-test.sh - Simple API test runner\nBASE_URL=\"${1:-http://localhost:3000}\"\nPASS=0\nFAIL=0\n\nassert_status() {\n  local method=\"$1\" url=\"$2\" expected=\"$3\" body=\"$4\"\n  local args=(-s -o /dev/null -w \"%{http_code}\" -X \"$method\")\n  if [ -n \"$body\" ]; then\n    args+=(-H \"Content-Type: application/json\" -d \"$body\")\n  fi\n  local status\n  status=$(curl \"${args[@]}\" \"$BASE_URL$url\")\n  if [ \"$status\" = \"$expected\" ]; then\n    echo \"PASS: $method $url -> $status\"\n    ((PASS++))\n  else\n    echo \"FAIL: $method $url -> $status (expected $expected)\"\n    ((FAIL++))\n  fi\n}\n\nassert_json() {\n  local url=\"$1\" jq_expr=\"$2\" expected=\"$3\"\n  local actual\n  actual=$(curl -s \"$BASE_URL$url\" | jq -r \"$jq_expr\")\n  if [ \"$actual\" = \"$expected\" ]; then\n    echo \"PASS: GET $url | jq '$jq_expr' = $expected\"\n    ((PASS++))\n  else\n    echo \"FAIL: GET $url | jq '$jq_expr' = $actual (expected $expected)\"\n    ((FAIL++))\n  fi\n}\n\n# Health check\nassert_status GET /health 200\n\n# CRUD tests\nassert_status POST /api/users 201 '{\"name\":\"Test\",\"email\":\"test@test.com\"}'\nassert_status GET /api/users 200\nassert_json /api/users '.[-1].name' 'Test'\nassert_status DELETE /api/users/1 204\n\n# Auth tests\nassert_status GET /api/admin 401\nassert_status GET /api/admin 403  # with wrong role\n\necho \"\"\necho \"Results: $PASS passed, $FAIL failed\"\n[ \"$FAIL\" -eq 0 ] && exit 0 || exit 1\n\nPython test runner\n#!/usr/bin/env python3\n\"\"\"api_test.py - API integration test suite.\"\"\"\nimport json, sys, urllib.request, urllib.error\n\nBASE = sys.argv[1] if len(sys.argv) > 1 else \"http://localhost:3000\"\nPASS = FAIL = 0\n\ndef request(method, path, body=None, headers=None):\n    \"\"\"Make an HTTP request, return (status, body_dict, headers).\"\"\"\n    url = f\"{BASE}{path}\"\n    data = json.dumps(body).encode() if body else None\n    hdrs = {\"Content-Type\": \"application/json\", \"Accept\": \"application/json\"}\n    if headers:\n        hdrs.update(headers)\n    req = urllib.request.Request(url, data=data, headers=hdrs, method=method)\n    try:\n        resp = urllib.request.urlopen(req)\n        body = json.loads(resp.read().decode()) if resp.read() else None\n    except urllib.error.HTTPError as e:\n        return e.code, None, dict(e.headers)\n    return resp.status, body, dict(resp.headers)\n\ndef test(name, fn):\n    \"\"\"Run a test function, track pass/fail.\"\"\"\n    global PASS, FAIL\n    try:\n        fn()\n        print(f\"  PASS: {name}\")\n        PASS += 1\n    except AssertionError as e:\n        print(f\"  FAIL: {name} - {e}\")\n        FAIL += 1\n\ndef assert_eq(actual, expected, msg=\"\"):\n    assert actual == expected, f\"got {actual}, expected {expected}. {msg}\"\n\n# --- Tests ---\nprint(f\"Testing {BASE}\\n\")\n\ntest(\"GET /health returns 200\", lambda: (\n    assert_eq(request(\"GET\", \"/health\")[0], 200)\n))\n\ntest(\"POST /api/users creates user\", lambda: (\n    assert_eq(request(\"POST\", \"/api/users\", {\"name\": \"Test\", \"email\": \"t@t.com\"})[0], 201)\n))\n\ntest(\"GET /api/users returns array\", lambda: (\n    assert_eq(type(request(\"GET\", \"/api/users\")[1]), list)\n))\n\ntest(\"GET /api/notfound returns 404\", lambda: (\n    assert_eq(request(\"GET\", \"/api/notfound\")[0], 404)\n))\n\nprint(f\"\\nResults: {PASS} passed, {FAIL} failed\")\nsys.exit(0 if FAIL == 0 else 1)\n\nOpenAPI Spec Generation\nGenerate from existing endpoints\n# Scaffold an OpenAPI 3.0 spec from curl responses\n# Run this, then fill in the details\ncat > openapi.yaml << 'EOF'\nopenapi: \"3.0.3\"\ninfo:\n  title: My API\n  version: \"1.0.0\"\n  description: API description here\nservers:\n  - url: http://localhost:3000\n    description: Local development\npaths:\n  /health:\n    get:\n      summary: Health check\n      responses:\n        \"200\":\n          description: Service is healthy\n          content:\n            application/json:\n              schema:\n                type: object\n                properties:\n                  status:\n                    type: string\n                    example: ok\n  /api/users:\n    get:\n      summary: List users\n      parameters:\n        - name: page\n          in: query\n          schema:\n            type: integer\n            default: 1\n        - name: limit\n          in: query\n          schema:\n            type: integer\n            default: 20\n      responses:\n        \"200\":\n          description: List of users\n          content:\n            application/json:\n              schema:\n                type: array\n                items:\n                  $ref: \"#/components/schemas/User\"\n    post:\n      summary: Create user\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              $ref: \"#/components/schemas/CreateUser\"\n      responses:\n        \"201\":\n          description: User created\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/User\"\n        \"400\":\n          description: Validation error\n  /api/users/{id}:\n    get:\n      summary: Get user by ID\n      parameters:\n        - name: id\n          in: path\n          required: true\n          schema:\n            type: string\n      responses:\n        \"200\":\n          description: User details\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/User\"\n        \"404\":\n          description: Not found\ncomponents:\n  schemas:\n    User:\n      type: object\n      properties:\n        id:\n          type: string\n        name:\n          type: string\n        email:\n          type: string\n          format: email\n        createdAt:\n          type: string\n          format: date-time\n    CreateUser:\n      type: object\n      required:\n        - name\n        - email\n      properties:\n        name:\n          type: string\n        email:\n          type: string\n          format: email\n  securitySchemes:\n    bearerAuth:\n      type: http\n      scheme: bearer\n      bearerFormat: JWT\nEOF\n\nValidate OpenAPI spec\n# Using npx (no install needed)\nnpx @redocly/cli lint openapi.yaml\n\n# Quick check: is the YAML valid?\npython3 -c \"import yaml; yaml.safe_load(open('openapi.yaml'))\" && echo \"Valid YAML\"\n\nMock Server\nQuick mock with Python\n#!/usr/bin/env python3\n\"\"\"mock_server.py - Lightweight API mock from OpenAPI-like config.\"\"\"\nimport json, http.server, re, sys\n\nPORT = int(sys.argv[1]) if len(sys.argv) > 1 else 8080\n\n# Define mock routes: (method, path_pattern) -> response\nROUTES = {\n    (\"GET\", \"/health\"): {\"status\": 200, \"body\": {\"status\": \"ok\"}},\n    (\"GET\", \"/api/users\"): {\"status\": 200, \"body\": [\n        {\"id\": \"1\", \"name\": \"Alice\", \"email\": \"alice@example.com\"},\n        {\"id\": \"2\", \"name\": \"Bob\", \"email\": \"bob@example.com\"},\n    ]},\n    (\"POST\", \"/api/users\"): {\"status\": 201, \"body\": {\"id\": \"3\", \"name\": \"Created\"}},\n    (\"GET\", r\"/api/users/\\w+\"): {\"status\": 200, \"body\": {\"id\": \"1\", \"name\": \"Alice\"}},\n    (\"DELETE\", r\"/api/users/\\w+\"): {\"status\": 204, \"body\": None},\n}\n\nclass MockHandler(http.server.BaseHTTPRequestHandler):\n    def _handle(self):\n        for (method, pattern), response in ROUTES.items():\n            if self.command == method and re.fullmatch(pattern, self.path.split('?')[0]):\n                self.send_response(response[\"status\"])\n                if response[\"body\"] is not None:\n                    self.send_header(\"Content-Type\", \"application/json\")\n                    self.end_headers()\n                    self.wfile.write(json.dumps(response[\"body\"]).encode())\n                else:\n                    self.end_headers()\n                return\n        self.send_response(404)\n        self.send_header(\"Content-Type\", \"application/json\")\n        self.end_headers()\n        self.wfile.write(json.dumps({\"error\": \"Not found\"}).encode())\n\n    do_GET = do_POST = do_PUT = do_PATCH = do_DELETE = _handle\n\n    def log_message(self, fmt, *args):\n        print(f\"{self.command} {self.path} -> {args[1] if len(args) > 1 else '?'}\")\n\nprint(f\"Mock server on http://localhost:{PORT}\")\nhttp.server.HTTPServer((\"\", PORT), MockHandler).serve_forever()\n\n\nRun: python3 mock_server.py 8080\n\nNode.js Express Scaffolding\nMinimal REST API\n// server.js - Minimal Express REST API\nconst express = require('express');\nconst app = express();\napp.use(express.json());\n\n// In-memory store\nconst items = new Map();\nlet nextId = 1;\n\n// CRUD endpoints\napp.get('/api/items', (req, res) => {\n  const { page = 1, limit = 20 } = req.query;\n  const all = [...items.values()];\n  const start = (page - 1) * limit;\n  res.json({ items: all.slice(start, start + +limit), total: all.length });\n});\n\napp.get('/api/items/:id', (req, res) => {\n  const item = items.get(req.params.id);\n  if (!item) return res.status(404).json({ error: 'Not found' });\n  res.json(item);\n});\n\napp.post('/api/items', (req, res) => {\n  const { name, description } = req.body;\n  if (!name) return res.status(400).json({ error: 'name required' });\n  const id = String(nextId++);\n  const item = { id, name, description: description || '', createdAt: new Date().toISOString() };\n  items.set(id, item);\n  res.status(201).json(item);\n});\n\napp.put('/api/items/:id', (req, res) => {\n  if (!items.has(req.params.id)) return res.status(404).json({ error: 'Not found' });\n  const item = { ...req.body, id: req.params.id, updatedAt: new Date().toISOString() };\n  items.set(req.params.id, item);\n  res.json(item);\n});\n\napp.delete('/api/items/:id', (req, res) => {\n  if (!items.has(req.params.id)) return res.status(404).json({ error: 'Not found' });\n  items.delete(req.params.id);\n  res.status(204).end();\n});\n\n// Error handler\napp.use((err, req, res, next) => {\n  console.error(err.stack);\n  res.status(500).json({ error: 'Internal server error' });\n});\n\nconst PORT = process.env.PORT || 3000;\napp.listen(PORT, () => console.log(`API running on http://localhost:${PORT}`));\n\nSetup\nmkdir my-api && cd my-api\nnpm init -y\nnpm install express\nnode server.js\n\nDebugging Patterns\nCheck if port is in use\n# Linux/macOS\nlsof -i :3000\n# or\nss -tlnp | grep 3000\n\n# Kill process on port\nkill $(lsof -t -i :3000)\n\nTest CORS\n# Preflight request\ncurl -s -X OPTIONS https://api.example.com/users \\\n  -H \"Origin: http://localhost:3000\" \\\n  -H \"Access-Control-Request-Method: POST\" \\\n  -H \"Access-Control-Request-Headers: Content-Type\" \\\n  -I\n\nWatch for response time regressions\n# Quick benchmark (10 requests)\nfor i in $(seq 1 10); do\n  curl -s -o /dev/null -w \"%{time_total}\\n\" http://localhost:3000/api/users\ndone | awk '{sum+=$1; if($1>max)max=$1} END {printf \"Avg: %.3fs, Max: %.3fs\\n\", sum/NR, max}'\n\nInspect JWT tokens\n# Decode JWT payload (no verification)\necho \"$TOKEN\" | cut -d. -f2 | base64 -d 2>/dev/null | jq .\n\nTips\nUse jq for JSON response processing: curl -s url | jq '.items[] | {id, name}'\nSet Content-Type header on every request with a body - missing it causes silent 400s\nUse -w '\\n' with curl to ensure output ends with a newline\nFor large response bodies, pipe to jq -C . | less -R for colored paging\nTest error paths: invalid JSON, missing fields, wrong types, unauthorized, not found\nFor WebSocket testing: npx wscat -c ws://localhost:3000/ws"
  },
  "trust": {
    "sourceLabel": "tencent",
    "provenanceUrl": "https://clawhub.ai/gitgoodordietrying/api-dev",
    "publisherUrl": "https://clawhub.ai/gitgoodordietrying/api-dev",
    "owner": "gitgoodordietrying",
    "version": "1.0.0",
    "license": null,
    "verificationStatus": "Indexed source record"
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/api-dev",
    "downloadUrl": "https://openagent3.xyz/downloads/api-dev",
    "agentUrl": "https://openagent3.xyz/skills/api-dev/agent",
    "manifestUrl": "https://openagent3.xyz/skills/api-dev/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/api-dev/agent.md"
  }
}