> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://docs.ampdb.io/api/mcp` to find what you need.

# TypeScript SDK

The TypeScript SDK, `@edgeandnode/amp`, lets Node.js programs use Amp: run SQL queries, follow their results as new
blocks arrive, and manage datasets and jobs. It works with any Amp: one you [run yourself](/get-started/quickstart), or
a [hosted one](/get-started/choose-how-to-run-amp#connect-to-a-hosted-amp).

It's built on [Effect](https://effect.website), a TypeScript library for programs that can fail, run concurrently, or
need resources like network connections. You don't need to know Effect to use the SDK: [Effect
Basics](/sdks/typescript/effect) covers the parts you need, and how to call the SDK from plain `async` code.

## Prerequisites

* **Node.js 24 or newer.** The examples are TypeScript files that `node` runs directly, like `node query.ts`.
* **A running Amp, with a dataset to query.** The examples use the `acme/eth_mainnet` dataset that the
  [Quickstart](/get-started/quickstart) creates, on solo mode's ports: queries on 1602, and the Admin API on 1610.

## Install the SDK

The SDK isn't published to npm yet. Each commit to its repository, `edgeandnode/amp-typescript`, publishes a build of it
to [pkg.pr.new](https://pkg.pr.new), which you install by URL. These docs are written for the build of commit `1b8cf83`.
Install it with its peer dependencies, the packages it needs you to install alongside it: Effect, and the Connect and
Protocol Buffers libraries it sends queries with. The examples are ES modules, so run `npm pkg set type=module` first
(after `npm init -y`, if there's no `package.json` yet):

:::code-group
```bash [npm]
npm install --allow-remote=all \
  https://pkg.pr.new/edgeandnode/amp-typescript/@edgeandnode/amp@1b8cf83 \
  effect @bufbuild/protobuf @connectrpc/connect @connectrpc/connect-node
```

```bash [pnpm]
pnpm add https://pkg.pr.new/edgeandnode/amp-typescript/@edgeandnode/amp@1b8cf83 \
  effect @bufbuild/protobuf @connectrpc/connect @connectrpc/connect-node
```
:::

npm 12 refuses to install packages from URLs unless you pass `--allow-remote=all`. Older versions of npm don't need it.

| Package                    | Version                     | What it's for                                     |
| -------------------------- | --------------------------- | ------------------------------------------------- |
| `@edgeandnode/amp`         | `0.0.52`, at commit 1b8cf83 | The SDK                                           |
| `effect`                   | `^4.0.0`                    | Effect, which the SDK's functions return and take |
| `@bufbuild/protobuf`       | `^2.16.0`                   | Protocol Buffers, the format of Amp's query API   |
| `@connectrpc/connect`      | `^2.2.0`                    | Connect, the RPC library queries go through       |
| `@connectrpc/connect-node` | `^2.2.0`                    | Connect's Node.js transport, for gRPC             |

:::warning
`npm install @edgeandnode/amp`, without the URL, installs version `0.0.55`, an older SDK with a different API, built on
Effect 3. These docs don't apply to it.
:::

## Run your first query

Save this as `query.ts` in your project. It gets the five most recent blocks of the Quickstart's dataset:

```ts [query.ts]
import { ArrowFlight } from "@edgeandnode/amp"
import { layerTransportGrpc } from "@edgeandnode/amp/arrow-flight/node"
import { Effect, Layer } from "effect"

// What the program does: get the ArrowFlight service, then run a query with it
const program = Effect.gen(function* () {
  const flight = yield* ArrowFlight.ArrowFlight
  const batches = yield* flight.query(
    `SELECT block_num, timestamp, gas_used FROM "acme/eth_mainnet".blocks ORDER BY block_num DESC LIMIT 5`,
  )
  console.table(batches.flatMap((batch) => batch.data))
})

// How to build the services it needs: ArrowFlight, over gRPC, from the query server on port 1602
const AmpLayer = ArrowFlight.layer.pipe(Layer.provide(layerTransportGrpc({ baseUrl: "http://localhost:1602" })))

await Effect.runPromise(program.pipe(Effect.provide(AmpLayer)))
```

Then run it:

:::terminal
```bash
node query.ts
```

```
┌─────────┬────────────┬────────────────────────────┬────────────┐
│ (index) │ block_num  │ timestamp                  │ gas_used   │
├─────────┼────────────┼────────────────────────────┼────────────┤
│ 0       │ '24000099' │ '2025-12-13T00:25:59.000Z' │ '10751338' │
│ 1       │ '24000098' │ '2025-12-13T00:25:47.000Z' │ '36233422' │
│ 2       │ '24000097' │ '2025-12-13T00:25:35.000Z' │ '52877862' │
│ 3       │ '24000096' │ '2025-12-13T00:25:23.000Z' │ '15967071' │
│ 4       │ '24000095' │ '2025-12-13T00:25:11.000Z' │ '21252932' │
└─────────┴────────────┴────────────────────────────┴────────────┘
```
:::

Results arrive in batches, each an array of rows. Large integers, like `block_num`, come as strings unless you ask for
numbers or `bigint`s: [Querying](/sdks/typescript/querying) explains the options, and how to give rows a type.

The program has two parts, a pattern every example follows. The first says what to do, as an Effect: get a service,
`ArrowFlight`, and call it. The second, a layer, says how to build the services the first part uses. `Effect.provide`
joins them, and `Effect.runPromise` runs the result. [Effect Basics](/sdks/typescript/effect) explains each of these.

## What's in the SDK

The SDK is a set of services, each with a page:

| Service                                                         | What it does                                                                   | Import from                                |
| --------------------------------------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------ |
| [`ArrowFlight`](/sdks/typescript/querying)                      | Runs SQL queries on the query server, over Arrow Flight                        | `@edgeandnode/amp` (`ArrowFlight`)         |
| [`ProtocolStream`](/sdks/typescript/streaming)                  | Streams a query's results as blocks arrive, and detects reorgs                 | `@edgeandnode/amp` (`ProtocolStream`)      |
| [`TransactionalStream`](/sdks/typescript/transactional-streams) | Streams with exactly-once processing, crash recovery, and undo events          | `@edgeandnode/amp` (`TransactionalStream`) |
| [`CdcStream`](/sdks/typescript/cdc-streams)                     | Streams as inserts and deletes, to copy results to another system              | `@edgeandnode/amp` (`CdcStream`)           |
| [`AdminApi`](/sdks/typescript/admin-api)                        | Manages datasets, jobs, workers, and providers, through the Admin API          | `@edgeandnode/amp/admin/service`           |
| [`Auth`](/sdks/typescript/authentication)                       | Signs in, which [isn't available yet](/sdks/typescript/authentication#sign-in) | `@edgeandnode/amp` (`Auth`)                |

[Registry API](/sdks/typescript/registry-api) describes a dataset registry's API, though a client built from it has no
service to talk to right now.
[Domain Types](/sdks/typescript/domain) has the types they share, like block ranges and dataset names, and
[Errors](/sdks/typescript/errors) the errors they fail with.

### Entry points

The package's root, `@edgeandnode/amp`, exports each module as a namespace: `ArrowFlight`, `ProtocolStream`,
`TransactionalStream`, `CdcStream`, `AdminApi`, `RegistryApi`, `Auth`, `AuthErrors`, and `Domain`. You can also import
a module by its path, which some exports need:

| Path                                    | Exports                                                                                  |
| --------------------------------------- | ---------------------------------------------------------------------------------------- |
| `@edgeandnode/amp/arrow-flight`         | The same as the root's `ArrowFlight`                                                     |
| `@edgeandnode/amp/arrow-flight/node`    | [`layerTransportGrpc`](/sdks/typescript/connecting#layertransportgrpc), only here        |
| `@edgeandnode/amp/protocol-stream`      | The same as the root's `ProtocolStream`                                                  |
| `@edgeandnode/amp/transactional-stream` | The same as the root's `TransactionalStream`                                             |
| `@edgeandnode/amp/cdc-stream`           | The same as the root's `CdcStream`                                                       |
| `@edgeandnode/amp/core`                 | The same as the root's `Domain`                                                          |
| `@edgeandnode/amp/admin/api`            | The same as the root's `AdminApi`: the Admin API's definition                            |
| `@edgeandnode/amp/admin/service`        | The [`AdminApi` service](/sdks/typescript/admin-api), only here                          |
| `@edgeandnode/amp/admin/domain`         | The Admin API's [request and response types](/sdks/typescript/admin-api#reference)       |
| `@edgeandnode/amp/admin/error`          | The Admin API's [errors](/sdks/typescript/error-codes#admin-api)                         |
| `@edgeandnode/amp/registry/api`         | The same as the root's `RegistryApi`                                                     |
| `@edgeandnode/amp/registry/domain`      | The Registry API's [request and response types](/sdks/typescript/registry-api#reference) |
| `@edgeandnode/amp/registry/error`       | The Registry API's [errors](/sdks/typescript/error-codes#registry-api)                   |
| `@edgeandnode/amp/auth/service`         | The same as the root's `Auth`                                                            |
| `@edgeandnode/amp/auth/error`           | The same as the root's `AuthErrors`                                                      |

Each module of the stream services also has a path for each of its files, like
`@edgeandnode/amp/protocol-stream/messages`, which exports a part of the same module. Two more exist that you don't
need: `@edgeandnode/amp/protobuf/Flight_pb` and `@edgeandnode/amp/protobuf/FlightSql_pb` are Arrow Flight's
Protocol Buffers definitions, which the SDK uses to talk to the query server.

## Known issues

The SDK has issues you should know about before you rely on it. Each page says how to work around them:

* **Streams fail after 30 seconds.** During a stream, Amp sends an empty message every 30 seconds, to keep the
  connection open. The SDK can't read it, and the stream fails with
  `RangeError: Offset is outside the bounds of the DataView`.
  [Streaming](/sdks/typescript/streaming#keep-a-stream-running) shows how to resume.
* **Rows are lost after a reorg.** When a reorg replaces blocks, Amp sends the new blocks' rows in the same message as
  the reorg. The SDK reports the reorg but drops the rows. See [Reorgs](/sdks/typescript/streaming#reorgs).
* **A reorg while a stream is stopped goes unnoticed.** When you resume a stream, the SDK doesn't check whether the
  blocks it resumes after have changed. See [Resume a stream](/sdks/typescript/streaming#resume-a-stream).
* **Some string columns can't be read.** A query whose results have the `Utf8View` type, which `CAST(… AS VARCHAR)`
  produces, fails with a `ParseSchemaError`. Cast with `arrow_cast(…, 'Utf8')` instead.
* **`stream: false` streams.** [`ArrowFlight.streamQuery`](/sdks/typescript/querying#streamquery) streams a query when
  its `stream` option is set to anything, even `false`. Leave it out instead.
