TypeScript client
Use @durable-streams/client when you want direct read and write access to Durable Streams from TypeScript.
It gives you:
stream()for fetch-like readsDurableStreamfor create, append, read, close, and deleteIdempotentProducerfor exactly-once writes with batching and retries
Key features
- Exactly-once writes with
IdempotentProducer - Automatic batching and pipelining for high-throughput producers
- Streaming reads with promise helpers,
ReadableStreams, and subscribers - Offset-based resumability and configurable live modes
- Works with JSON, text, and raw byte streams
Install
bash
npm install @durable-streams/clientRead-only API
The stream() function is the fetch-like API for consuming streams:
typescript
import { stream } from "@durable-streams/client"
const res = await stream<{ message: string }>({
url: "https://streams.example.com/my-account/chat/room-1",
offset: savedOffset,
live: true,
})
const items = await res.json()
console.log(items)Use stream() when your app only needs to consume a stream.
StreamResponse helpers
StreamResponse supports multiple consumption patterns:
typescript
const bytes = await res.body()
const items = await res.json()
const text = await res.text()
const byteStream = res.bodyStream()
const jsonStream = res.jsonStream()
const textStream = res.textStream()
const unsubscribe = res.subscribeJson(async (batch) => {
await processBatch(batch.items)
})Save the returned offset from subscriber batches if you want to resume from the same place later.
Exactly-once writes
For reliable, high-throughput writes with exactly-once semantics, use IdempotentProducer:
typescript
import { DurableStream, IdempotentProducer } from "@durable-streams/client"
const stream = await DurableStream.create({
url: "https://streams.example.com/events",
contentType: "application/json",
})
const producer = new IdempotentProducer(stream, "event-processor-1", {
autoClaim: true,
onError: (err) => console.error("Batch failed:", err),
})
for (const event of events) {
producer.append(event)
}
await producer.flush()
await producer.close()This is the recommended write path when you need safe retries and duplicate prevention.
Read/write API
Use DurableStream when you want a persistent handle for create, append, and read operations:
typescript
import { DurableStream } from "@durable-streams/client"
const handle = await DurableStream.create({
url: "https://streams.example.com/my-account/chat/room-1",
contentType: "application/json",
ttlSeconds: 3600,
})
await handle.append(JSON.stringify({ type: "message", text: "Hello" }))
const res = await handle.stream<{ type: string; text: string }>()
res.subscribeJson(async (batch) => {
for (const item of batch.items) {
console.log(item.text)
}
})Live modes
trueuses the default live behavior for the stream typefalsereads catch-up data only"sse"forces Server-Sent Events"long-poll"forces long-polling
When to use it
- Use the TypeScript client when you are building directly on the protocol.
- Use JSON mode when your stream payloads are structured messages.
- Use Vercel AI SDK or TanStack AI when you want higher-level AI integrations.
More
- TypeScript client README
- Client libraries for the other official language clients