1"use strict";(globalThis.webpackChunkpublic_docs=globalThis.webpackChunkpublic_docs||[]).push([[5527],{28453(e,n,i){i.d(n,{R:()=>r,x:()=>o});var s=i(96540);const l={},t=s.createContext(l);function r(e){const n=s.useContext(t);return s.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function o(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(l):e.components||l:r(e.components),s.createElement(t.Provider,{value:n},e.children)}},47226(e,n,i){i.r(n),i.d(n,{assets:()=>c,contentTitle:()=>a,default:()=>u,frontMatter:()=>o,metadata:()=>s,toc:()=>d});const s=JSON.parse('{"id":"tooling/cbt-api","title":"CBT API","description":"<ToolHeroBanner","source":"@site/docs/tooling/cbt-api.md","sourceDirName":"tooling","slug":"/tooling/cbt-api","permalink":"/docs/tooling/cbt-api","draft":false,"unlisted":false,"tags":[],"version":"current","sidebarPosition":20,"frontMatter":{"sidebar_position":20,"title":"CBT API","hide_title":true},"sidebar":"docs","previous":{"title":"CBT","permalink":"/docs/tooling/cbt"},"next":{"title":"ClickHouse Proto Gen","permalink":"/docs/tooling/clickhouse-proto-gen"}}');var l=i(74848),t=i(28453),r=i(59951);const o={sidebar_position:20,title:"CBT API",hide_title:!0},a=void 0,c={},d=[{value:"Overview",id:"overview",level:2},{value:"Key Features",id:"key-features",level:2},{value:"Automatic API Generation",id:"automatic-api-generation",level:3},{value:"Rich Query Capabilities",id:"rich-query-capabilities",level:3},{value:"Developer-Friendly",id:"developer-friendly",level:3},{value:"How It Works",id:"how-it-works",level:2},{value:"Generation Pipeline",id:"generation-pipeline",level:3},{value:"Request Flow",id:"request-flow",level:3},{value:"API Examples",id:"api-examples",level:2},{value:"Basic Queries",id:"basic-queries",level:3},{value:"Advanced Filtering",id:"advanced-filtering",level:3},{value:"Pagination",id:"pagination",level:3},{value:"Sorting",id:"sorting",level:3},{value:"Configuration",id:"configuration",level:2},{value:"Table Discovery",id:"table-discovery",level:3},{value:"API Exposure",id:"api-exposure",level:3},{value:"Use Cases",id:"use-cases",level:2},{value:"Public Data Access",id:"public-data-access",level:3},{value:"Internal Dashboards",id:"internal-dashboards",level:3},{value:"Multi-Network Deployment",id:"multi-network-deployment",level:3},{value:"Integration with ethPandaOps Stack",id:"integration-with-ethpandaops-stack",level:2},{value:"API Endpoints",id:"api-endpoints",level:2},{value:"Generic & Reusable",id:"generic--reusable",level:2},{value:"Resources",id:"resources",level:2},{value:"Related Tools",id:"related-tools",level:2},{value:"Community",id:"community",level:2}];function h(e){const n={a:"a",code:"code",h2:"h2",h3:"h3",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,t.R)(),...e.components};return(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(r.A,{title:"CBT API",description:"Automatic REST API generator for ClickHouse databases with OpenAPI specifications, making it easy to expose your data via modern APIs.",imagePath:"/img/tools/cbt-api.jpg"}),"\n",(0,l.jsxs)(n.p,{children:["CBT API is a generic REST API generator for ClickHouse databases managed with ",(0,l.jsx)(n.a,{href:"/docs/tooling/cbt/",children:"CBT (ClickHouse Build Tool)"}),". It automatically generates OpenAPI specifications and fully functional REST APIs from your ClickHouse table schemas."]}),"\n",(0,l.jsx)(n.h2,{id:"overview",children:"Overview"}),"\n",(0,l.jsx)(n.p,{children:"CBT API eliminates the manual work of building REST APIs for ClickHouse data by automatically:"}),"\n",(0,l.jsxs)(n.ul,{children:["\n",(0,l.jsxs)(n.li,{children:[(0,l.jsx)(n.strong,{children:"Discovering Tables"}),": Queries ClickHouse to find tables matching configured prefixes"]}),"\n",(0,l.jsxs)(n.li,{children:[(0,l.jsx)(n.strong,{children:"Generating Protobuf Definitions"}),": Creates type-safe Protocol Buffer schemas from table structures"]}),"\n",(0,l.jsxs)(n.li,{children:[(0,l.jsx)(n.strong,{children:"Creating OpenAPI Specs"}),": Generates complete OpenAPI 3.0 specifications with all endpoints"]}),"\n",(0,l.jsxs)(n.li,{children:[(0,l.jsx)(n.strong,{children:"Building Server Implementation"}),": Produces a fully functional Go REST API server with query builders"]}),"\n"]}),"\n",(0,l.jsx)(n.h2,{id:"key-features",children:"Key Features"}),"\n",(0,l.jsx)(n.h3,{id:"automatic-api-generation",children:"Automatic API Generation"}),"\n",(0,l.jsx)(n.p,{children:"Point CBT API at your ClickHouse database and get a complete REST API:"}),"\n",(0,l.jsxs)(n.ul,{children:["\n",(0,l.jsx)(n.li,{children:"No manual endpoint configuration required"}),"\n",(0,l.jsx)(n.li,{children:"Type-safe query builders generated from schemas"}),"\n",(0,l.jsx)(n.li,{children:"Pagination, filtering, and sorting built-in"}),"\n",(0,l.jsx)(n.li,{children:"OpenAPI specification auto-generated"}),"\n"]}),"\n",(0,l.jsx)(n.h3,{id:"rich-query-capabilities",children:"Rich Query Capabilities"}),"\n",(0,l.jsx)(n.p,{children:"Every table gets comprehensive filtering options:"}),"\n",(0,l.jsxs)(n.ul,{children:["\n",(0,l.jsxs)(n.li,{children:[(0,l.jsx)(n.strong,{children:"Scalar operators"}),": ",(0,l.jsx)(n.code,{children:"eq"}),", ",(0,l.jsx)(n.code,{children:"ne"}),", ",(0,l.jsx)(n.code,{children:"lt"}),", ",(0,l.jsx)(n.code,{children:"lte"}),", ",(0,l.jsx)(n.code,{children:"gt"}),", ",(0,l.jsx)(n.code,{children:"gte"}),", ",(0,l.jsx)(n.code,{children:"in_values"}),", ",(0,l.jsx)(n.code,{children:"not_in_values"})]}),"\n",(0,l.jsxs)(n.li,{children:[(0,l.jsx)(n.strong,{children:"String operators"}),": ",(0,l.jsx)(n.code,{children:"contains"}),", ",(0,l.jsx)(n.code,{children:"starts_with"}),", ",(0,l.jsx)(n.code,{children:"ends_with"}),", ",(0,l.jsx)(n.code,{children:"like"}),", ",(0,l.jsx)(n.code,{children:"not_like"})]}),"\n",(0,l.jsxs)(n.li,{children:[(0,l.jsx)(n.strong,{children:"Nullable operators"}),": ",(0,l.jsx)(n.code,{children:"is_null"}),", ",(0,l.jsx)(n.code,{children:"is_not_null"})]}),"\n",(0,l.jsxs)(n.li,{children:[(0,l.jsx)(n.strong,{children:"Map operators"}),": ",(0,l.jsx)(n.code,{children:"has_key"}),", ",(0,l.jsx)(n.code,{children:"not_has_key"}),", ",(0,l.jsx)(n.code,{children:"has_any_key"}),", ",(0,l.jsx)(n.code,{children:"has_all_keys"})]}),"\n"]}),"\n",(0,l.jsx)(n.h3,{id:"developer-friendly",children:"Developer-Friendly"}),"\n",(0,l.jsxs)(n.ul,{children:["\n",(0,l.jsx)(n.li,{children:"REST-friendly URL parameters with underscore notation"}),"\n",(0,l.jsx)(n.li,{children:"Swagger UI included for interactive API exploration"}),"\n",(0,l.jsx)(n.li,{children:"Prometheus metrics for observability"}),"\n",(0,l.jsx)(n.li,{children:"Health check endpoints for monitoring"}),"\n",(0,l.jsx)(n.li,{children:"Optional OpenTelemetry tracing support"}),"\n"]}),"\n",(0,l.jsx)(n.h2,{id:"how-it-works",children:"How It Works"}),"\n",(0,l.jsx)(n.h3,{id:"generation-pipeline",children:"Generation Pipeline"}),"\n",(0,l.jsxs)(n.ol,{children:["\n",(0,l.jsxs)(n.li,{children:[(0,l.jsx)(n.strong,{children:"Table Discovery"}),": Queries ClickHouse ",(0,l.jsx)(n.code,{children:"system.tables"})," to find tables matching configured prefixes"]}),"\n",(0,l.jsxs)(n.li,{children:[(0,l.jsx)(n.strong,{children:"Proto Generation"}),": Uses ",(0,l.jsx)(n.a,{href:"/docs/tooling/clickhouse-proto-gen/",children:"clickhouse-proto-gen"})," to create Protocol Buffer definitions"]}),"\n",(0,l.jsxs)(n.li,{children:[(0,l.jsx)(n.strong,{children:"OpenAPI Generation"}),": Creates OpenAPI spec from proto annotations with flattened filter parameters"]}),"\n",(0,l.jsxs)(n.li,{children:[(0,l.jsx)(n.strong,{children:"Server Generation"}),": Generates complete server implementation with query builders"]}),"\n"]}),"\n",(0,l.jsx)(n.h3,{id:"request-flow",children:"Request Flow"}),"\n",(0,l.jsx)(n.pre,{children:(0,l.jsx)(n.code,{children:"HTTP Request\n \u2193\nRouter (validates params)\n \u2193\nHandler (maps HTTP \u2192 Proto)\n \u2193\nQuery Builder (generates SQL)\n \u2193\nClickHouse Execution\n \u2193\nResult Scanning\n \u2193\nJSON Response\n"})}),"\n",(0,l.jsx)(n.h2,{id:"api-examples",children:"API Examples"}
1),"\n",(0,l.jsx)(n.h3,{id:"basic-queries",children:"Basic Queries"}),"\n",(0,l.jsx)(n.pre,{children:(0,l.jsx)(n.code,{className:"language-bash",children:"# List blocks\nGET /api/v1/fct_block\n\n# Filter by slot\nGET /api/v1/fct_block?slot_eq=1000\n\n# Range query\nGET /api/v1/fct_block?slot_gte=1000&slot_lte=2000\n\n# String search\nGET /api/v1/fct_block?meta_client_name_contains=lighthouse\n"})}),"\n",(0,l.jsx)(n.h3,{id:"advanced-filtering",children:"Advanced Filtering"}),"\n",(0,l.jsx)(n.pre,{children:(0,l.jsx)(n.code,{className:"language-bash",children:"# List filters (comma-separated)\nGET /api/v1/fct_block?meta_client_name_in_values=lighthouse,prysm,teku\n\n# Multiple conditions (AND logic)\nGET /api/v1/fct_block?slot_gte=1000&slot_lte=2000&meta_client_name_eq=lighthouse\n\n# Null checks\nGET /api/v1/fct_block?optional_field_is_not_null=true\n"})}),"\n",(0,l.jsx)(n.h3,{id:"pagination",children:"Pagination"}),"\n",(0,l.jsx)(n.pre,{children:(0,l.jsx)(n.code,{className:"language-bash",children:"# Custom page size\nGET /api/v1/fct_block?page_size=100\n\n# Next page\nGET /api/v1/fct_block?page_size=100&page_token=offset_100\n\n# Default: 100 items per page, max: 10000\n"})}),"\n",(0,l.jsx)(n.h3,{id:"sorting",children:"Sorting"}),"\n",(0,l.jsx)(n.pre,{children:(0,l.jsx)(n.code,{className:"language-bash",children:"# Sort by field (ascending)\nGET /api/v1/fct_block?order_by=slot\n\n# Sort descending\nGET /api/v1/fct_block?order_by=slot&order_desc=true\n"})}),"\n",(0,l.jsx)(n.h2,{id:"configuration",children:"Configuration"}),"\n",(0,l.jsx)(n.h3,{id:"table-discovery",children:"Table Discovery"}),"\n",(0,l.jsx)(n.p,{children:"Control which tables are exposed via the API:"}),"\n",(0,l.jsx)(n.pre,{children:(0,l.jsx)(n.code,{className:"language-yaml",children:'clickhouse:\n dsn: "https://user:password@host:443"\n database: "mainnet"\n\n # Table discovery for proto generation\n discovery:\n prefixes:\n - fct # Discover fact tables\n - dim # Discover dimension tables\n exclude:\n - "*_test" # Exclude test tables\n - "*_tmp" # Exclude temporary tables\n'})}),"\n",(0,l.jsx)(n.h3,{id:"api-exposure",children:"API Exposure"}),"\n",(0,l.jsx)(n.p,{children:"Fine-tune which tables get REST endpoints:"}),"\n",(0,l.jsx)(n.pre,{children:(0,l.jsx)(n.code,{className:"language-yaml",children:'api:\n enable: true\n base_path: "/api/v1"\n # Only tables with these prefixes will be exposed\n expose_prefixes:\n - fct # Only expose fact tables via API\n'})}),"\n",(0,l.jsx)(n.h2,{id:"use-cases",children:"Use Cases"}),"\n",(0,l.jsx)(n.h3,{id:"public-data-access",children:"Public Data Access"}),"\n",(0,l.jsx)(n.p,{children:"Expose ClickHouse data to the community:"}),"\n",(0,l.jsxs)(n.ul,{children:["\n",(0,l.jsx)(n.li,{children:"Provide REST APIs for your Ethereum datasets"}),"\n",(0,l.jsx)(n.li,{children:"Enable developers to query data without ClickHouse access"}),"\n",(0,l.jsx)(n.li,{children:"Automatic pagination and filtering for large datasets"}),"\n"]}),"\n",(0,l.jsx)(n.h3,{id:"internal-dashboards",children:"Internal Dashboards"}),"\n",(0,l.jsx)(n.p,{children:"Power internal analytics tools:"}),"\n",(0,l.jsxs)(n.ul,{children:["\n",(0,l.jsx)(n.li,{children:"Generate APIs for Grafana or custom dashboards"}),"\n",(0,l.jsx)(n.li,{children:"Type-safe data access with Protocol Buffers"}),"\n",(0,l.jsx)(n.li,{children:"Consistent query interface across teams"}),"\n"]}),"\n",(0,l.jsx)(n.h3,{id:"multi-network-deployment",children:"Multi-Network Deployment"}),"\n",(0,l.jsx)(n.p,{children:"Serve data from multiple Ethereum networks:"}),"\n",(0,l.jsxs)(n.ul,{children:["\n",(0,l.jsx)(n.li,{children:"Generate APIs for mainnet, testnets, and devnets"}),"\n",(0,l.jsx)(n.li,{children:"Consistent API structure across all networks"}),"\n",(0,l.jsxs)(n.li,{children:["Use with ",(0,l.jsx)(n.a,{href:"https://github.com/ethpandaops/lab-backend",children:"lab-backend"})," for routing"]}),"\n"]}),"\n",(0,l.jsx)(n.h2,{id:"integration-with-ethpandaops-stack",children:"Integration with ethPandaOps Stack"}),"\n",(0,l.jsx)(n.p,{children:"CBT API is used throughout the ethPandaOps infrastru
1cture:"}),"\n",(0,l.jsxs)(n.ul,{children:["\n",(0,l.jsxs)(n.li,{children:[(0,l.jsx)(n.strong,{children:"The Lab"}),": Powers the REST APIs serving transformed data from ",(0,l.jsx)(n.a,{href:"/docs/tooling/cbt/",children:"CBT"})]}),"\n",(0,l.jsxs)(n.li,{children:[(0,l.jsx)(n.strong,{children:"Public Datasets"}),": Exposes Xatu data to the community via REST APIs"]}),"\n",(0,l.jsxs)(n.li,{children:[(0,l.jsx)(n.strong,{children:"Multi-Network"}),": Generates separate APIs for each Ethereum network (mainnet, sepolia, holesky, etc.)"]}),"\n"]}),"\n",(0,l.jsx)(n.h2,{id:"api-endpoints",children:"API Endpoints"}),"\n",(0,l.jsx)(n.p,{children:"All generated APIs include:"}),"\n",(0,l.jsxs)(n.ul,{children:["\n",(0,l.jsxs)(n.li,{children:[(0,l.jsx)(n.strong,{children:"API endpoints"}),": ",(0,l.jsx)(n.code,{children:"/api/v1/*"})," (all discovered tables)"]}),"\n",(0,l.jsxs)(n.li,{children:[(0,l.jsx)(n.strong,{children:"Swagger UI"}),": ",(0,l.jsx)(n.code,{children:"/docs/"})," (interactive API explorer)"]}),"\n",(0,l.jsxs)(n.li,{children:[(0,l.jsx)(n.strong,{children:"OpenAPI spec"}),": ",(0,l.jsx)(n.code,{children:"/openapi.yaml"})," (machine-readable specification)"]}),"\n",(0,l.jsxs)(n.li,{children:[(0,l.jsx)(n.strong,{children:"Health check"}),": ",(0,l.jsx)(n.code,{children:"/health"})," (liveness probe)"]}),"\n",(0,l.jsxs)(n.li,{children:[(0,l.jsx)(n.strong,{children:"Metrics"}),": ",(0,l.jsx)(n.code,{children:"/metrics"})," (Prometheus format)"]}),"\n"]}),"\n",(0,l.jsx)(n.h2,{id:"generic--reusable",children:"Generic & Reusable"}),"\n",(0,l.jsxs)(n.p,{children:["CBT API is ",(0,l.jsx)(n.strong,{children:"completely generic"})," and works with any ClickHouse database. Simply point it at your ClickHouse instance, configure table prefixes, and generate a complete REST API with type-safe query capabilities."]}),"\n",(0,l.jsx)(n.h2,{id:"resources",children:"Resources"}),"\n",(0,l.jsxs)(n.ul,{children:["\n",(0,l.jsx)(n.li,{children:(0,l.jsx)(n.a,{href:"https://github.com/ethpandaops/cbt-api",children:"GitHub Repository"})}),"\n",(0,l.jsx)(n.li,{children:(0,l.jsx)(n.a,{href:"https://github.com/ethpandaops/cbt-api#readme",children:"Full Documentation"})}),"\n",(0,l.jsx)(n.li,{children:(0,l.jsx)(n.a,{href:"https://github.com/ethpandaops/cbt-api#configuration",children:"Configuration Guide"})}),"\n"]}),"\n",(0,l.jsx)(n.h2,{id:"related-tools",children:"Related Tools"}),"\n",(0,l.jsxs)(n.ul,{children:["\n",(0,l.jsxs)(n.li,{children:[(0,l.jsx)(n.strong,{children:(0,l.jsx)(n.a,{href:"/docs/tooling/cbt/",children:"CBT"})}),": Transform data in ClickHouse before exposing via API"]}),"\n",(0,l.jsxs)(n.li,{children:[(0,l.jsx)(n.strong,{children:(0,l.jsx)(n.a,{href:"/docs/tooling/clickhouse-proto-gen/",children:"ClickHouse Proto Gen"})}),": Underlying tool for schema generation (used internally)"]}),"\n",(0,l.jsxs)(n.li,{children:[(0,l.jsx)(n.strong,{children:(0,l.jsx)(n.a,{href:"/docs/tooling/lab/",children:"The Lab"})}),": Visualize data served by CBT API"]}),"\n",(0,l.jsxs)(n.li,{children:[(0,l.jsx)(n.strong,{children:(0,l.jsx)(n.a,{href:"/docs/tooling/xatu/",children:"Xatu"})}),": Collect data that can be served via CBT API"]}),"\n"]}),"\n",(0,l.jsx)(n.h2,{id:"community",children:"Community"}),"\n",(0,l.jsx)(n.p,{children:"Need help or want to contribute?"}),"\n",(0,l.jsxs)(n.ul,{children:["\n",(0,l.jsxs)(n.li,{children:["Report issues on ",(0,l.jsx)(n.a,{href:"https://github.com/ethpandaops/cbt-api/issues",children:"GitHub"})]}),"\n",(0,l.jsxs)(n.li,{children:["Join us on the ",(0,l.jsx)(n.a,{href:"https://discord.com/invite/qGpsxSA",children:"Ethereum R&D Discord"})]}),"\n",(0,l.jsxs)(n.li,{children:["Check out related tools in the ",(0,l.jsx)(n.a,{href:"/docs/tooling/overview/",children:"ethPandaOps ecosystem"})]}),"\n"]})]})}function u(e={}){const{wrapper:n}={...(0,t.R)(),...e.components};return n?(0,l.jsx)(n,{...e,children:(0,l.jsx)(h,{...e})}):h(e)}},59951(e,n,i){i.d(n,{A:()=>c});i(96540);const s="heroBanner_p5AC",l="overlay_Ob9f",t="content_XlV_",r="title_v07c",o="description_nfNL";var a=i(74848);function c({title:e,description:n,imagePath:i}){return(0,a.jsxs)("div",{className:s,style:{backgroundImage:`url(${i})`},children:[(0,a.jsx)("div",{className:l}),(0,a.jsxs)("div",{className:t,children:[(0,a.jsx)("h1",{className:r,children:e}),(0,a.jsx)("p",{className:o,children:n})]})]})}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.