{"id":48244748,"url":"https://github.com/tinybirdco/tinybird-sdk-typescript","last_synced_at":"2026-04-04T20:29:31.182Z","repository":{"id":335932209,"uuid":"1146636129","full_name":"tinybirdco/tinybird-sdk-typescript","owner":"tinybirdco","description":null,"archived":false,"fork":false,"pushed_at":"2026-03-31T10:46:25.000Z","size":1197,"stargazers_count":26,"open_issues_count":6,"forks_count":4,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-03-31T12:05:21.091Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tinybirdco.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-31T12:30:03.000Z","updated_at":"2026-03-31T10:46:29.000Z","dependencies_parsed_at":null,"dependency_job_id":"0a81cd17-e148-486c-9ddf-ab2e754ba092","html_url":"https://github.com/tinybirdco/tinybird-sdk-typescript","commit_stats":null,"previous_names":["rmorehig/tinybird-sdk-typescript"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/tinybirdco/tinybird-sdk-typescript","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tinybirdco%2Ftinybird-sdk-typescript","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tinybirdco%2Ftinybird-sdk-typescript/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tinybirdco%2Ftinybird-sdk-typescript/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tinybirdco%2Ftinybird-sdk-typescript/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tinybirdco","download_url":"https://codeload.github.com/tinybirdco/tinybird-sdk-typescript/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tinybirdco%2Ftinybird-sdk-typescript/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31413218,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T20:09:54.854Z","status":"ssl_error","status_checked_at":"2026-04-04T20:09:44.350Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-04-04T20:29:30.439Z","updated_at":"2026-04-04T20:29:31.162Z","avatar_url":"https://github.com/tinybirdco.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @tinybirdco/sdk\n\n\u003e **Note:** This package is experimental. APIs may change between versions.\n\nA TypeScript SDK for defining Tinybird resources with full type inference. Define your datasources, pipes, and queries in TypeScript and sync them directly to Tinybird.\n\n## Installation\n\n```bash\nnpm install @tinybirdco/sdk\n```\n\n## Requirements\n\nTypeScript `\u003e=4.9` is supported for consumers.\n\nOfficially supported runtime:\n- Node.js 20 LTS or later non-EOL versions\n\nNot officially supported (untested in this repository at this time):\n- Deno `\u003e=1.28.0`\n- Bun `\u003e=1.0.0`\n- Cloudflare Workers\n- Vercel Edge Runtime\n- Jest `\u003e=28` (including `\"node\"` environment)\n- Nitro `\u003e=2.6.0`\n\nWeb browsers are not supported. This SDK is designed for server-side usage and using it directly in the browser may expose Tinybird API credentials.\n\nIf you need support for other runtimes, please open or upvote an issue on GitHub.\n\n## Quick Start\n\n### 1. Initialize your project\n\n```bash\nnpx tinybird init\n```\n\nThis creates:\n- `tinybird.config.json` - Configuration file\n- `src/tinybird/datasources.ts` - Define your datasources\n- `src/tinybird/pipes.ts` - Define your pipes/endpoints\n- `src/tinybird/client.ts` - Your typed Tinybird client\n\n### 2. Configure your token\n\nCreate a `.env.local` file:\n\n```env\nTINYBIRD_TOKEN=p.your_token_here\n```\n\n### 3. Define your datasources\n\n```typescript\n// src/tinybird/datasources.ts\nimport { defineDatasource, t, engine, type InferRow } from \"@tinybirdco/sdk\";\n\nexport const pageViews = defineDatasource(\"page_views\", {\n  description: \"Page view tracking data\",\n  schema: {\n    timestamp: t.dateTime(),\n    pathname: t.string(),\n    session_id: t.string(),\n    country: t.string().lowCardinality().nullable(),\n  },\n  engine: engine.mergeTree({\n    sortingKey: [\"pathname\", \"timestamp\"],\n  }),\n});\n\n// Export row type for ingestion\nexport type PageViewsRow = InferRow\u003ctypeof pageViews\u003e;\n```\n\n### 4. Define your endpoints\n\n```typescript\n// src/tinybird/pipes.ts\nimport { defineEndpoint, node, t, p, type InferParams, type InferOutputRow } from \"@tinybirdco/sdk\";\n\nexport const topPages = defineEndpoint(\"top_pages\", {\n  description: \"Get the most visited pages\",\n  params: {\n    start_date: p.dateTime(),\n    end_date: p.dateTime(),\n    limit: p.int32().optional(10),\n  },\n  nodes: [\n    node({\n      name: \"aggregated\",\n      sql: `\n        SELECT pathname, count() AS views\n        FROM page_views\n        WHERE timestamp \u003e= {{DateTime(start_date)}}\n          AND timestamp \u003c= {{DateTime(end_date)}}\n        GROUP BY pathname\n        ORDER BY views DESC\n        LIMIT {{Int32(limit, 10)}}\n      `,\n    }),\n  ],\n  output: {\n    pathname: t.string(),\n    views: t.uint64(),\n  },\n});\n\n// Export endpoint types\nexport type TopPagesParams = InferParams\u003ctypeof topPages\u003e;\nexport type TopPagesOutput = InferOutputRow\u003ctypeof topPages\u003e;\n```\n\n### 5. Create your client\n\n```typescript\n// src/tinybird/client.ts\nimport { Tinybird } from \"@tinybirdco/sdk\";\nimport { pageViews, type PageViewsRow } from \"./datasources\";\nimport { topPages, type TopPagesParams, type TopPagesOutput } from \"./pipes\";\n\nexport const tinybird = new Tinybird({\n  datasources: { pageViews },\n  pipes: { topPages },\n});\n\n// Optional: customize fetch for framework-specific cache or runtime behavior\nexport const tinybirdNoStore = new Tinybird({\n  datasources: { pageViews },\n  pipes: { topPages },\n  fetch: (url, init) =\u003e fetch(url, { ...init, cache: \"no-store\" }),\n});\n\n// Re-export types for convenience\nexport type { PageViewsRow, TopPagesParams, TopPagesOutput };\nexport { pageViews, topPages };\n```\n\n### 6. Add path alias (for Next.js/TypeScript projects)\n\nAdd to your `tsconfig.json`:\n\n```json\n{\n  \"compilerOptions\": {\n    \"paths\": {\n      \"@tinybird/client\": [\"./src/tinybird/client.ts\"]\n    }\n  }\n}\n```\n\n### 7. Start development\n\n```bash\nnpx tinybird dev\n```\n\nThis watches your schema files and automatically syncs changes to Tinybird.\n\n### 8. Use the typed client\n\n```typescript\nimport { tinybird, type PageViewsRow } from \"@tinybird/client\";\n\n// Type-safe data ingestion\nawait tinybird.pageViews.ingest({\n  timestamp: \"2024-01-15 10:30:00\",\n  pathname: \"/home\",\n  session_id: \"abc123\",\n  country: \"US\",\n});\n\n// Type-safe queries with autocomplete\nconst result = await tinybird.topPages.query({\n  start_date: \"2024-01-01 00:00:00\",\n  end_date: \"2024-01-31 23:59:59\",\n  limit: 5,\n});\n\n// result.data is fully typed: { pathname: string, views: bigint }[]\n```\n\n### 9. Manage datasource rows\n\n```typescript\nimport { tinybird } from \"@tinybird/client\";\n\n// Datasource accessors support: ingest, append, replace, delete, truncate\n\n// Ingest one row as JSON\nawait tinybird.pageViews.ingest({\n  timestamp: \"2024-01-15 10:30:00\",\n  pathname: \"/pricing\",\n  session_id: \"session_123\",\n  country: \"US\",\n});\n\n// Import rows from a remote file\nawait tinybird.pageViews.append({\n  url: \"https://example.com/page_views.csv\",\n});\n\n// Replace all rows from a remote file\nawait tinybird.pageViews.replace({\n  url: \"https://example.com/page_views_full_snapshot.csv\",\n});\n\n// Delete matching rows\nawait tinybird.pageViews.delete({\n  deleteCondition: \"country = 'XX'\",\n});\n\n// Preview matching rows without deleting\nawait tinybird.pageViews.delete({\n  deleteCondition: \"country = 'XX'\",\n  dryRun: true,\n});\n\n// Remove all rows from the datasource\nawait tinybird.pageViews.truncate();\n```\n\n## Public Tinybird API (Optional)\n\nIf you want a low-level API wrapper that is decoupled from the typed client layer,\nyou can use `createTinybirdApi()` directly with just `baseUrl` and `token`:\n\n```typescript\nimport { createTinybirdApi } from \"@tinybirdco/sdk\";\n\nconst api = createTinybirdApi({\n  baseUrl: \"https://api.tinybird.co\",\n  token: process.env.TINYBIRD_TOKEN!,\n});\n\n// Query endpoint pipe (with optional type parameters)\ninterface TopPagesRow { pathname: string; visits: number }\ninterface TopPagesParams { start_date: string; end_date: string; limit?: number }\n\nconst topPages = await api.query\u003cTopPagesRow, TopPagesParams\u003e(\"top_pages\", {\n  start_date: \"2024-01-01\",\n  end_date: \"2024-01-31\",\n  limit: 5,\n});\n\n// Ingest one row into datasource (with optional type parameter)\ninterface EventRow { timestamp: string; event_name: string; pathname: string }\n\nawait api.ingest\u003cEventRow\u003e(\"events\", {\n  timestamp: \"2024-01-15 10:30:00\",\n  event_name: \"page_view\",\n  pathname: \"/home\",\n});\n\n// Ingest retry behavior (disabled by default):\n// - 429 retries use Retry-After / X-RateLimit-Reset headers.\n// - 503 retries use SDK default exponential backoff.\nawait api.ingest\u003cEventRow\u003e(\n  \"events\",\n  {\n    timestamp: \"2024-01-15 10:31:00\",\n    event_name: \"button_click\",\n    pathname: \"/pricing\",\n  },\n  {\n    maxRetries: 3,\n  }\n);\n\n// Import rows from URL/file\nawait api.appendDatasource(\"events\", {\n  url: \"https://example.com/events.csv\",\n});\n\n// Delete rows matching a SQL condition\nawait api.deleteDatasource(\"events\", {\n  deleteCondition: \"event_name = 'test'\",\n});\n\n// Delete dry run (validate and return count only)\nawait api.deleteDatasource(\"events\", {\n  deleteCondition: \"event_name = 'test'\",\n  dryRun: true,\n});\n\n// Truncate datasource\nawait api.truncateDatasource(\"events\");\n\n// Execute raw SQL (with optional type parameter)\ninterface CountResult { total: number }\n\nconst sqlResult = await api.sql\u003cCountResult\u003e(\"SELECT count() AS total FROM events\");\n\n// Optional per-request token override\nawait api.request(\"/v1/workspace\", {\n  token: process.env.TINYBIRD_BRANCH_TOKEN,\n});\n```\n\nThis Tinybird API is standalone and can be used without `createClient()` or `new Tinybird()`.\nIt is intended for cases where you want a simple public API that remains\ndecoupled from the higher-level typed client APIs.\n\n## JWT Token Creation\n\nCreate short-lived JWT tokens for secure, scoped access to your Tinybird resources. This is useful for:\n- Frontend applications that need to call Tinybird APIs directly from browsers\n- Multi-tenant applications requiring row-level security\n- Time-limited access with automatic expiration\n\n```typescript\nimport { createClient } from \"@tinybirdco/sdk\";\n\nconst client = createClient({\n  baseUrl: \"https://api.tinybird.co\",\n  token: process.env.TINYBIRD_ADMIN_TOKEN!, // Requires ADMIN scope\n});\n\n// Create a JWT token with scoped access\nconst { token } = await client.tokens.createJWT({\n  name: \"user_123_session\",\n  expiresAt: new Date(Date.now() + 60 * 60 * 1000), // 1 hour\n  scopes: [\n    {\n      type: \"PIPES:READ\",\n      resource: \"user_dashboard\",\n      fixed_params: { user_id: 123 }, // Row-level security\n    },\n  ],\n  limits: { rps: 10 }, // Optional rate limiting\n});\n\n// Use the JWT token for client-side queries\nconst userClient = createClient({\n  baseUrl: \"https://api.tinybird.co\",\n  token, // The JWT\n});\n```\n\n### Scope Types\n\n| Scope | Description |\n|-------|-------------|\n| `PIPES:READ` | Read access to a specific pipe endpoint |\n| `DATASOURCES:READ` | Read access to a datasource (with optional `filter`) |\n| `DATASOURCES:APPEND` | Append access to a datasource |\n\n### Scope Options\n\n- **`fixed_params`**: For pipes, embed parameters that cannot be overridden by the caller\n- **`filter`**: For datasources, append a SQL WHERE clause (e.g., `\"org_id = 'acme'\"`)\n\n## CLI Commands\n\n### `npx tinybird init`\n\nInitialize a new Tinybird TypeScript project. The setup flow can also install Tinybird agent skills and Tinybird SQL syntax highlighting for Cursor/VS Code (when available). If you have existing `.datasource` and `.pipe` files in your repository, the CLI will detect them and ask if you want to include them in your configuration.\n\n```bash\nnpx tinybird init\nnpx tinybird init --force       # Overwrite existing files\nnpx tinybird init --skip-login  # Skip browser login flow\n```\n\nThis enables incremental migration for existing Tinybird projects - you can keep your `.datasource` and `.pipe` files alongside TypeScript definitions.\n\n### `tinybird migrate`\n\nMigrate local Tinybird datafiles (`.datasource`, `.pipe`, `.connection`) into a single TypeScript definitions file.\n\n```bash\ntinybird migrate \"tinybird/**/*.datasource\" \"tinybird/**/*.pipe\" \"tinybird/**/*.connection\"\ntinybird migrate tinybird/legacy --out ./tinybird.migration.ts\ntinybird migrate tinybird --dry-run\n```\n\nBehavior:\n- Processes files, directories, and glob patterns.\n- Continues through all matches and reports migratable resources plus per-file errors.\n- In strict mode (default), exits with non-zero status if any errors are found.\n- Infers parameter defaults from scalar SQL placeholders (for example `{{String(env, 'prod')}}`) while treating `{{Array(ids, 'String')}}` second arguments as element types, not defaults.\n- Writes one output file by default: `./tinybird.migration.ts`.\n\n### `tinybird dev`\n\nWatch for changes and sync with Tinybird automatically. Only works on feature branches (not main).\n\n```bash\ntinybird dev\ntinybird dev --local   # Sync with local Tinybird container\ntinybird dev --branch  # Explicitly use Tinybird cloud with branches\n```\n\n**Note:** `dev` mode is blocked on the main branch to prevent accidental production deployments. Use `tinybird deploy` for production, or switch to a feature branch.\n\n### `tinybird build`\n\nBuild and push resources to a Tinybird branch (not main).\n\n```bash\ntinybird build\ntinybird build --dry-run  # Preview without pushing\ntinybird build --local    # Build to local Tinybird container\ntinybird build --branch   # Explicitly use Tinybird cloud with branches\n```\n\n**Note:** `build` is blocked on the main branch. Use `tinybird deploy` for production deployments.\n\n### `tinybird deploy`\n\nDeploy resources to the main Tinybird workspace (production). This is the only way to deploy to main.\n\n```bash\ntinybird deploy\ntinybird deploy --dry-run  # Preview without pushing\ntinybird deploy --check    # Validate with the API without applying changes\ntinybird deploy --allow-destructive-operations  # Allow deletes in main deploy\n```\n\nUse `--allow-destructive-operations` only when your deploy intentionally removes\nexisting datasources, pipes, or connections from the main workspace.\n\n### `tinybird pull`\n\nDownload all cloud resources as native Tinybird datafiles (`.datasource`, `.pipe`, `.connection`).\n\n```bash\ntinybird pull\ntinybird pull --output-dir ./tinybird-datafiles\ntinybird pull --force  # Overwrite existing files\n```\n\n### `tinybird login`\n\nAuthenticate with Tinybird via browser. Use this to set up credentials for an existing project without reinitializing.\n\n```bash\ntinybird login\n```\n\nThis is useful when:\n- You cloned an existing project that has a `tinybird.config.json` but no credentials\n- Your token has expired or needs to be refreshed\n- You're switching to a different Tinybird workspace\n\nThe command saves your token to `.env.local` and updates the `baseUrl` in your config file if you select a different region.\n\n### `tinybird branch`\n\nManage Tinybird branches.\n\n```bash\ntinybird branch list      # List all branches\ntinybird branch status    # Show current branch status\ntinybird branch delete \u003cname\u003e  # Delete a branch\n```\n\n### `tinybird info`\n\nDisplay information about the current project and workspace.\n\n```bash\ntinybird info         # Show workspace, local, and project info\ntinybird info --json  # Output as JSON\n```\n\nShows:\n- **Workspace**: Cloud workspace details (name, ID, user, API host, dashboard URL, token)\n- **Local**: Local Tinybird container info (when `devMode` is `local`)\n- **Branch**: Current branch details (when `devMode` is `branch` and on a feature branch)\n- **Project**: Configuration and git information\n\n## Configuration\n\nCreate a `tinybird.config.json` (or `tinybird.config.mjs` / `tinybird.config.cjs` for dynamic logic) in your project root:\n\n```json\n{\n  \"include\": [\n    \"src/tinybird/datasources.ts\",\n    \"src/tinybird/pipes.ts\",\n    \"src/tinybird/legacy.datasource\",\n    \"src/tinybird/legacy.pipe\"\n  ],\n  \"token\": \"${TINYBIRD_TOKEN}\",\n  \"baseUrl\": \"https://api.tinybird.co\",\n  \"devMode\": \"branch\"\n}\n```\n\nYou can mix TypeScript files with raw `.datasource` and `.pipe` files for incremental migration.\n`include` also supports glob patterns like `src/tinybird/**/*.ts` and `src/tinybird/**/*.datasource`.\n\n### Config File Formats\n\nThe SDK supports multiple config file formats (in priority order):\n\n| File | Description |\n|------|-------------|\n| `tinybird.config.mjs` | ESM JavaScript config with dynamic logic |\n| `tinybird.config.cjs` | CommonJS JavaScript config with dynamic logic |\n| `tinybird.config.json` | Standard JSON config (default for new projects) |\n| `tinybird.json` | Legacy JSON config (backward compatible) |\n\nFor JavaScript configs, export a default config object or function:\n\n```javascript\n// tinybird.config.mjs\n/** @type {import(\"@tinybirdco/sdk\").TinybirdConfig} */\nexport default {\n  include: [\"src/lib/tinybird.ts\"],\n  token: process.env.TINYBIRD_TOKEN,\n  baseUrl: \"https://api.tinybird.co\",\n  devMode: \"branch\",\n};\n```\n\n### Configuration Options\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `include` | `string[]` | *required* | Array of file paths or glob patterns for TypeScript files and raw `.datasource`/`.pipe` files |\n| `token` | `string` | *required* | API token. Supports `${ENV_VAR}` interpolation for environment variables |\n| `baseUrl` | `string` | `\"https://api.tinybird.co\"` | Tinybird API URL. Use `\"https://api.us-east.tinybird.co\"` for US region |\n| `devMode` | `\"branch\"` \\| `\"local\"` | `\"branch\"` | Development mode. `\"branch\"` uses Tinybird cloud with branches, `\"local\"` uses local Docker container |\n\n### Local Development Mode\n\nUse a local Tinybird container for development without affecting your cloud workspace:\n\n1. Start the local container:\n   ```bash\n   docker run -d -p 7181:7181 --name tinybird-local tinybirdco/tinybird-local:latest\n   ```\n\n2. Configure your project:\n   ```json\n   {\n     \"devMode\": \"local\"\n   }\n   ```\n\n   Or use the CLI flag:\n   ```bash\n   npx tinybird dev --local\n   ```\n\nIn local mode:\n- Tokens are automatically obtained from the local container\n- A workspace is created per git branch\n- No cloud authentication required\n\n## Defining Resources\n\n### Connections\n\n```typescript\nimport { defineKafkaConnection, defineS3Connection, defineGCSConnection, secret } from \"@tinybirdco/sdk\";\n\nexport const eventsKafka = defineKafkaConnection(\"events_kafka\", {\n  bootstrapServers: \"kafka.example.com:9092\",\n  securityProtocol: \"SASL_SSL\",\n  saslMechanism: \"PLAIN\",\n  key: secret(\"KAFKA_KEY\"),\n  secret: secret(\"KAFKA_SECRET\"),\n});\n\nexport const landingS3 = defineS3Connection(\"landing_s3\", {\n  region: \"us-east-1\",\n  arn: \"arn:aws:iam::123456789012:role/tinybird-s3-access\",\n});\n\nexport const landingGCS = defineGCSConnection(\"landing_gcs\", {\n  serviceAccountCredentialsJson: secret(\"GCS_SERVICE_ACCOUNT_CREDENTIALS_JSON\"),\n});\n```\n\nUse connections from datasources:\n\n```typescript\nimport { defineDatasource, t, engine } from \"@tinybirdco/sdk\";\nimport { eventsKafka, landingS3, landingGCS } from \"./connections\";\n\nexport const kafkaEvents = defineDatasource(\"kafka_events\", {\n  schema: {\n    timestamp: t.dateTime(),\n    payload: t.string(),\n  },\n  engine: engine.mergeTree({ sortingKey: [\"timestamp\"] }),\n  kafka: {\n    connection: eventsKafka,\n    topic: \"events\",\n    groupId: \"events-consumer\",\n    autoOffsetReset: \"earliest\",\n  },\n});\n\nexport const s3Landing = defineDatasource(\"s3_landing\", {\n  schema: {\n    timestamp: t.dateTime(),\n    session_id: t.string(),\n  },\n  engine: engine.mergeTree({ sortingKey: [\"timestamp\"] }),\n  s3: {\n    connection: landingS3,\n    bucketUri: \"s3://my-bucket/events/*.csv\",\n    schedule: \"@auto\",\n  },\n});\n\nexport const gcsLanding = defineDatasource(\"gcs_landing\", {\n  schema: {\n    timestamp: t.dateTime(),\n    session_id: t.string(),\n  },\n  engine: engine.mergeTree({ sortingKey: [\"timestamp\"] }),\n  gcs: {\n    connection: landingGCS,\n    bucketUri: \"gs://my-gcs-bucket/events/*.csv\",\n    schedule: \"@auto\",\n  },\n});\n```\n\n### Datasources\n\n```typescript\nimport { defineDatasource, t, engine, type InferRow } from \"@tinybirdco/sdk\";\n\nexport const events = defineDatasource(\"events\", {\n  description: \"Event tracking data\",\n  schema: {\n    timestamp: t.dateTime(),\n    event_name: t.string().lowCardinality(),\n    user_id: t.string().nullable(),\n    properties: t.string(), // JSON as string\n  },\n  engine: engine.mergeTree({\n    sortingKey: [\"event_name\", \"timestamp\"],\n    partitionKey: \"toYYYYMM(timestamp)\",\n    ttl: \"timestamp + INTERVAL 90 DAY\",\n  }),\n});\n\nexport type EventsRow = InferRow\u003ctypeof events\u003e;\n```\n\n### Endpoints (API pipes)\n\n```typescript\nimport { defineEndpoint, node, t, p, type InferParams, type InferOutputRow } from \"@tinybirdco/sdk\";\n\nexport const topEvents = defineEndpoint(\"top_events\", {\n  description: \"Get the most frequent events\",\n  params: {\n    start_date: p.dateTime(),\n    end_date: p.dateTime(),\n    limit: p.int32().optional(10),\n  },\n  nodes: [\n    node({\n      name: \"aggregated\",\n      sql: `\n        SELECT event_name, count() AS event_count\n        FROM events\n        WHERE timestamp \u003e= {{DateTime(start_date)}}\n          AND timestamp \u003c= {{DateTime(end_date)}}\n        GROUP BY event_name\n        ORDER BY event_count DESC\n        LIMIT {{Int32(limit, 10)}}\n      `,\n    }),\n  ],\n  output: {\n    event_name: t.string(),\n    event_count: t.uint64(),\n  },\n});\n\nexport type TopEventsParams = InferParams\u003ctypeof topEvents\u003e;\nexport type TopEventsOutput = InferOutputRow\u003ctypeof topEvents\u003e;\n```\n\n### Internal Pipes (not exposed as API)\n\n```typescript\nimport { definePipe, node } from \"@tinybirdco/sdk\";\n\nexport const filteredEvents = definePipe(\"filtered_events\", {\n  description: \"Filter events by date range\",\n  params: {\n    start_date: p.dateTime(),\n    end_date: p.dateTime(),\n  },\n  nodes: [\n    node({\n      name: \"filtered\",\n      sql: `\n        SELECT * FROM events\n        WHERE timestamp \u003e= {{DateTime(start_date)}}\n          AND timestamp \u003c= {{DateTime(end_date)}}\n      `,\n    }),\n  ],\n});\n```\n\n### Materialized Views\n\n```typescript\nimport { defineDatasource, defineMaterializedView, t, engine } from \"@tinybirdco/sdk\";\n\n// Target datasource for the materialized view\nexport const dailyStats = defineDatasource(\"daily_stats\", {\n  description: \"Daily aggregated statistics\",\n  schema: {\n    date: t.date(),\n    pathname: t.string(),\n    views: t.simpleAggregateFunction(\"sum\", t.uint64()),\n    unique_sessions: t.aggregateFunction(\"uniq\", t.string()),\n  },\n  engine: engine.aggregatingMergeTree({\n    sortingKey: [\"date\", \"pathname\"],\n  }),\n});\n\n// Materialized view that populates the datasource\nexport const dailyStatsMv = defineMaterializedView(\"daily_stats_mv\", {\n  description: \"Materialize daily page view aggregations\",\n  datasource: dailyStats,\n  nodes: [\n    node({\n      name: \"aggregate\",\n      sql: `\n        SELECT\n          toDate(timestamp) AS date,\n          pathname,\n          count() AS views,\n          uniqState(session_id) AS unique_sessions\n        FROM page_views\n        GROUP BY date, pathname\n      `,\n    }),\n  ],\n});\n```\n\n### Copy Pipes\n\n```typescript\nimport { defineCopyPipe, node } from \"@tinybirdco/sdk\";\n\n// Scheduled copy pipe\nexport const dailySnapshot = defineCopyPipe(\"daily_snapshot\", {\n  description: \"Daily snapshot of statistics\",\n  datasource: snapshotDatasource,\n  schedule: \"0 0 * * *\", // Run daily at midnight\n  mode: \"append\",\n  nodes: [\n    node({\n      name: \"snapshot\",\n      sql: `\n        SELECT today() AS snapshot_date, pathname, count() AS views\n        FROM page_views\n        WHERE toDate(timestamp) = today() - 1\n        GROUP BY pathname\n      `,\n    }),\n  ],\n});\n\n// On-demand copy pipe\nexport const manualReport = defineCopyPipe(\"manual_report\", {\n  description: \"On-demand report generation\",\n  datasource: reportDatasource,\n  schedule: \"@on-demand\",\n  mode: \"replace\",\n  nodes: [\n    node({\n      name: \"report\",\n      sql: `SELECT * FROM events WHERE timestamp \u003e= now() - interval 7 day`,\n    }),\n  ],\n});\n```\n\n### Sink Pipes\n\nUse sink pipes to publish query results to external systems. The SDK supports only Kafka and S3 sinks.\n\n```typescript\nimport { defineSinkPipe, node } from \"@tinybirdco/sdk\";\nimport { eventsKafka, landingS3 } from \"./connections\";\n\n// Kafka sink\nexport const kafkaEventsSink = defineSinkPipe(\"kafka_events_sink\", {\n  sink: {\n    connection: eventsKafka,\n    topic: \"events_export\",\n    schedule: \"@on-demand\",\n  },\n  nodes: [\n    node({\n      name: \"publish\",\n      sql: `\n        SELECT timestamp, payload\n        FROM kafka_events\n      `,\n    }),\n  ],\n});\n\n// S3 sink\nexport const s3EventsSink = defineSinkPipe(\"s3_events_sink\", {\n  sink: {\n    connection: landingS3,\n    bucketUri: \"s3://my-bucket/exports/\",\n    fileTemplate: \"events_{date}\",\n    format: \"csv\",\n    schedule: \"@once\",\n    strategy: \"create_new\",\n    compression: \"gzip\",\n  },\n  nodes: [\n    node({\n      name: \"export\",\n      sql: `\n        SELECT timestamp, session_id\n        FROM s3_landing\n      `,\n    }),\n  ],\n});\n```\n\n### Static Tokens\n\nDefine reusable tokens for resource access control:\n\n```typescript\nimport { defineToken, defineDatasource, defineEndpoint, t, node } from \"@tinybirdco/sdk\";\n\n// Define a token once\nconst appToken = defineToken(\"app_read\");\nconst ingestToken = defineToken(\"ingest_token\");\n\n// Use in datasources with READ or APPEND scope\nexport const events = defineDatasource(\"events\", {\n  schema: {\n    timestamp: t.dateTime(),\n    event_name: t.string(),\n  },\n  tokens: [\n    { token: appToken, scope: \"READ\" },\n    { token: ingestToken, scope: \"APPEND\" },\n  ],\n});\n\n// Use in endpoints with READ scope\nexport const topEvents = defineEndpoint(\"top_events\", {\n  nodes: [node({ name: \"endpoint\", sql: \"SELECT * FROM events LIMIT 10\" })],\n  output: { timestamp: t.dateTime(), event_name: t.string() },\n  tokens: [{ token: appToken, scope: \"READ\" }],\n});\n```\n\nTypeScript provides autocomplete for the correct scopes:\n- **Datasources**: `READ` (query access) or `APPEND` (ingest access)\n- **Pipes**: `READ` only\n\n## Type Validators\n\nUse `t.*` to define column types with full TypeScript inference:\n\n```typescript\nimport { t } from \"@tinybirdco/sdk\";\n\nconst schema = {\n  // Strings\n  name: t.string(),\n  id: t.uuid(),\n  code: t.fixedString(3),\n\n  // Numbers\n  count: t.int32(),\n  amount: t.float64(),\n  big_number: t.uint64(),\n  price: t.decimal(10, 2),\n\n  // Date/Time\n  created_at: t.dateTime(),\n  updated_at: t.dateTime64(3),\n  birth_date: t.date(),\n\n  // Boolean\n  is_active: t.bool(),\n\n  // Complex types\n  tags: t.array(t.string()),\n  metadata: t.map(t.string(), t.string()),\n\n  // Aggregate functions (for materialized views)\n  total: t.simpleAggregateFunction(\"sum\", t.uint64()),\n  unique_users: t.aggregateFunction(\"uniq\", t.string()),\n\n  // Modifiers\n  optional_field: t.string().nullable(),\n  category: t.string().lowCardinality(),\n  status: t.string().default(\"pending\"),\n  event_id: t.uuid().defaultExpr(\"generateUUIDv4()\"),\n};\n```\n\n## Parameter Validators\n\nUse `p.*` to define pipe query parameters:\n\n```typescript\nimport { p } from \"@tinybirdco/sdk\";\n\nconst params = {\n  // Required parameters\n  start_date: p.dateTime(),\n  user_id: p.string(),\n\n  // Optional with defaults\n  limit: p.int32().optional(10),\n  offset: p.int32().optional(0),\n\n  // With descriptions (for documentation)\n  status: p.string().optional(\"active\").describe(\"Filter by status\"),\n};\n```\n\n## Engine Configurations\n\n```typescript\nimport { engine } from \"@tinybirdco/sdk\";\n\n// MergeTree (default)\nengine.mergeTree({\n  sortingKey: [\"user_id\", \"timestamp\"],\n  partitionKey: \"toYYYYMM(timestamp)\",\n  ttl: \"timestamp + INTERVAL 90 DAY\",\n});\n\n// ReplacingMergeTree (for upserts)\nengine.replacingMergeTree({\n  sortingKey: [\"id\"],\n  version: \"updated_at\",\n});\n\n// SummingMergeTree (for pre-aggregation)\nengine.summingMergeTree({\n  sortingKey: [\"date\", \"category\"],\n  columns: [\"count\", \"total\"],\n});\n\n// AggregatingMergeTree (for complex aggregates)\nengine.aggregatingMergeTree({\n  sortingKey: [\"date\"],\n});\n```\n\n## Next.js Integration\n\nFor Next.js projects, add these scripts to your `package.json`:\n\n```json\n{\n  \"scripts\": {\n    \"dev\": \"concurrently -n next,tinybird \\\"next dev\\\" \\\"tinybird dev\\\"\",\n    \"tinybird:build\": \"tinybird build\"\n  },\n  \"devDependencies\": {\n    \"concurrently\": \"^9.0.0\"\n  }\n}\n```\n\nThe CLI automatically loads `.env.local` and `.env` files, so no additional setup is needed.\n\nAdd the path alias to `tsconfig.json`:\n\n```json\n{\n  \"compilerOptions\": {\n    \"paths\": {\n      \"@/*\": [\"./src/*\"],\n      \"@tinybird/client\": [\"./src/tinybird/client.ts\"]\n    }\n  }\n}\n```\n\nNow `npm run dev` starts both Next.js and Tinybird sync together.\n\n## Type Inference\n\nThe SDK provides full type inference for your schemas:\n\n```typescript\nimport { type InferRow, type InferParams, type InferOutputRow } from \"@tinybirdco/sdk\";\nimport { pageViews, topPages } from \"./tinybird/datasources\";\n\n// Infer the row type for a datasource\ntype PageViewRow = InferRow\u003ctypeof pageViews\u003e;\n// { timestamp: string, pathname: string, session_id: string, country: string | null }\n\n// Infer the parameters for a pipe\ntype TopPagesParams = InferParams\u003ctypeof topPages\u003e;\n// { start_date: string, end_date: string, limit?: number }\n\n// Infer the output type for a pipe\ntype TopPagesOutput = InferOutputRow\u003ctypeof topPages\u003e;\n// { pathname: string, views: bigint }\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftinybirdco%2Ftinybird-sdk-typescript","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftinybirdco%2Ftinybird-sdk-typescript","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftinybirdco%2Ftinybird-sdk-typescript/lists"}