Quickstart: TypeScript SDK

This quickstart walks through creating a Tinybird project using the TypeScript SDK. You define Data Sources and Endpoints in TypeScript, sync them to Tinybird, and use a typed client for ingest and queries.

Looking for other ways to start? See Quickstarts.

If you use a coding agent like Cursor, Claude Code, Amp, or Open Code, install Tinybird agent skills so your agent understands Tinybird project structure and workflows:

npx skills add tinybirdco/tinybird-agent-skills

Before you begin

To get started, you need the following:

  • Node.js 20 LTS or later
  • TypeScript 4.9 or later

SDK Installation

We recommend installing the SDK globally so you can run the tinybird CLI directly.

pnpm add -g @tinybirdco/sdk

You can also add it just to your repository:

pnpm add @tinybirdco/sdk

To follow the quickstart, install it in the repository so that commands work exactly as they are in the examples.

Initialize a Tinybird TypeScript project

Create a new directory and initialize the project:

mkdir tinybird-typescript-quickstart
cd tinybird-typescript-quickstart
git init
pnpm add @tinybirdco/sdk

npx tinybird init

During initialization, the CLI authenticates you against Tinybird Cloud and can optionally add CI/CD workflow files.

This creates:

  • tinybird.config.json configuration file
  • lib/tinybird.ts for Data Sources, Pipes, Connectors, and Client definitions
  • .env.local for authentication against your Tinybird Cloud Workspace

Check your config file

In tinybird.config.json, the default "include" is lib/tinybird.ts to generate resources from that file. The default "devMode" is "branches" to develop in Tinybird Cloud Branches, although you can use "local" too with Tinybird Local.

{
  "include": [
    "lib/tinybird.ts"
  ],
  "token": "${TINYBIRD_TOKEN}",
  "baseUrl": "${TINYBIRD_URL}",
  "devMode": "branches"
}

Check your Tinybird token

Create a .env.local file with your Tinybird token if it doesn't exist yet:

TINYBIRD_TOKEN=p.your_token_here
TINYBIRD_URL=https://api.tinybird.co

The CLI reads .env.local automatically.

Inspect resource definitions

Data sources

Check the Data Source definition in lib/tinybird.ts. The schema and engine stay the same regardless of how you ingest data. The difference is how data arrives: via the Events API, from a Kafka topic, or from an S3 bucket.

import { defineDatasource, engine, t, type InferRow } from "@tinybirdco/sdk";

export const pageViews = defineDatasource("page_views", {
  description: "Page view tracking data",
  schema: {
    timestamp: t.dateTime(),
    session_id: t.string(),
    pathname: t.string(),
    referrer: t.string().nullable(),
  },
  engine: engine.mergeTree({
    sortingKey: ["pathname", "timestamp"],
  }),
});

export type PageViewsRow = InferRow<typeof pageViews>;

API Endpoints

Check the API Endpoint definition in lib/tinybird.ts:

import {
  defineEndpoint,
  node,
  p,
  t,
  type InferParams,
  type InferOutputRow,
} from "@tinybirdco/sdk";

export const topPages = defineEndpoint("top_pages", {
  description: "Get the most visited pages",
  params: {
    start_date: p.dateTime().describe("Start of date range"),
    end_date: p.dateTime().describe("End of date range"),
    limit: p.int32().optional(10).describe("Number of results"),
  },
  nodes: [
    node({
      name: "aggregated",
      sql: `
        SELECT
          pathname,
          count() AS views
        FROM page_views
        WHERE timestamp >= {{DateTime(start_date)}}
          AND timestamp <= {{DateTime(end_date)}}
        GROUP BY pathname
        ORDER BY views DESC
        LIMIT {{Int32(limit, 10)}}
      `,
    }),
  ],
  output: {
    pathname: t.string(),
    views: t.uint64(),
  },
});

export type TopPagesParams = InferParams<typeof topPages>;
export type TopPagesOutput = InferOutputRow<typeof topPages>;

Typed client

Check the client in lib/tinybird.ts:

import { Tinybird } from "@tinybirdco/sdk";

export const tinybird = new Tinybird({
  datasources: { pageViews },
  pipes: { topPages },
});

Build the project

Check out a new git branch:

git add tinybird.config.json && git commit -m "Initial commit" && git checkout -b tinybird-intro

Run a build to create your resources in your Tinybird development environment:

npx tinybird build

If you want your changes to sync automatically while you work, run dev mode instead:

npx tinybird dev

About the development flow

Your default target (local or branch) is set in tinybird.config.json. You can override it with flags on the command line.

tinybird build syncs to a Tinybird branch, either in Cloud or in Tinybird Local.

You can also pass a flag to explicitly use the desired development environment.

npx tinybird build --local # Tinybird Local Branches
# or
npx tinybird build --branch  # Tinybird Cloud Branches

If you haven't installed Tinybird Local yet, follow Install Tinybird Local.

When you're ready to promote changes:

  • Use tinybird build to push to a branch (good for review or CI).
  • To deploy to your main Tinybird Cloud Workspace, we strongly recommend CI/CD, but you can use tinybird deploy directly.

Git integration

You may have noticed that the quickstart example had a git checkout -b tinybird-intro. If Tinybird detects you are in a Git repository, it creates a branch with the git branch name in your Tinybird development environment.

Ingest data and query

Ingest

Use the typed client to send events via the Events API:

import { tinybird } from "./lib/tinybird";

await tinybird.ingest.pageViews({
  timestamp: new Date(),
  pathname: "/home",
  session_id: "abc123",
  referrer: "https://google.com",
});

If you configured a Kafka or S3 connector, data is ingested automatically after you deploy. You don't need to call ingest manually.

Query

Query the endpoint from your application code:

const result = await tinybird.query.topPages({
  start_date: new Date("2026-01-01"),
  end_date: new Date(),
  limit: 5,
});

result.data.forEach((row) => {
  console.log(row.pathname, row.views);
});

Iterate in a branch

Add a materialized view in another branch, test the app against that branch, and deploy to Tinybird Cloud when you're finished.

git checkout -b materialization

Edit lib/tinybird.ts to add a new Data Source, a new materialized view, and update the Endpoint to read from the materialized view.

import {
  defineDatasource,
  defineEndpoint,
  defineMaterializedView,
  engine,
  node,
  p,
  t,
} from "@tinybirdco/sdk";

export const pageViews = defineDatasource("page_views", {
  description: "Page view tracking data",
  schema: {
    timestamp: t.dateTime(),
    session_id: t.string(),
    pathname: t.string(),
    referrer: t.string().nullable(),
  },
  engine: engine.mergeTree({
    sortingKey: ["pathname", "timestamp"],
  }),
});

export const dailyPageViews = defineDatasource("daily_page_views_ds", {
  description: "Daily page view counts populated by a materialized view",
  schema: {
    date: t.date(),
    pathname: t.string(),
    views: t.simpleAggregateFunction("sum", t.uint64()),
  },
  engine: engine.aggregatingMergeTree({
    sortingKey: ["date", "pathname"],
  }),
});

export const dailyPageViewsMv = defineMaterializedView("daily_page_views_mv", {
  datasource: dailyPageViews,
  nodes: [
    node({
      name: "aggregate",
      sql: `
        SELECT
          toDate(timestamp) AS date,
          pathname,
          count() AS views
        FROM page_views
        GROUP BY date, pathname
      `,
    }),
  ],
});

export const topPages = defineEndpoint("top_pages", {
  description: "Get the most visited pages from MV-backed datasource",
  params: {
    start_date: p.dateTime(),
    end_date: p.dateTime(),
    limit: p.int32().optional(10),
  },
  nodes: [
    node({
      name: "aggregated",
      sql: `
        SELECT
          pathname,
          sum(views) AS views
        FROM daily_page_views_ds
        WHERE date >= toDate({{DateTime(start_date)}})
          AND date <= toDate({{DateTime(end_date)}})
        GROUP BY pathname
        ORDER BY views DESC
        LIMIT {{Int32(limit, 10)}}
      `,
    }),
  ],
  output: {
    pathname: t.string(),
    views: t.uint64(),
  },
});

Build against the branch:

npx tinybird build

Once validated, you can deploy to Tinybird Cloud with npx tinybird deploy if you haven't set up a CI/CD script.

Next steps