Skip to content

Commit e565e67

Browse files
authored
feat(publisher): cloudflare durable object adapter (#1253)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Cloudflare Durable Objects available as a publisher adapter with durable pub/sub and resume/replay support * new Durable-backed publisher for publish/subscribe use and a packaged experimental Durable Object publisher * Configurable customJsonSerializers option added to Redis publishers * **Playground** * ChatRoomV2 demo added * RPC client now includes automatic retry support * **Documentation** * Publisher docs updated with Durable Object examples and serializer guidance <sub>✏️ Tip: You can customize this high-level summary in your review settings.</sub> <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent eccbaa4 commit e565e67

32 files changed

+7303
-2842
lines changed

apps/content/docs/helpers/publisher.md

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -148,11 +148,12 @@ while (true) {
148148

149149
## Available Adapters
150150

151-
| Name | Resume Support | Description |
152-
| ----------------------- | -------------- | ---------------------------------------------------------------- |
153-
| `MemoryPublisher` || A simple in-memory publisher |
154-
| `IORedisPublisher` || Adapter for [ioredis](https://github.com/redis/ioredis) |
155-
| `UpstashRedisPublisher` || Adapter for [Upstash Redis](https://github.com/upstash/redis-js) |
151+
| Name | Resume Support | Description |
152+
| ------------------------ | -------------- | -------------------------------------------------------------------------------------------- |
153+
| `MemoryPublisher` || A simple in-memory publisher |
154+
| `IORedisPublisher` || Adapter for [ioredis](https://github.com/redis/ioredis) |
155+
| `UpstashRedisPublisher` || Adapter for [Upstash Redis](https://github.com/upstash/redis-js) |
156+
| `PublisherDurableObject` || Adapter for [Cloudflare Durable Objects](https://developers.cloudflare.com/durable-objects/) |
156157

157158
::: info
158159
If you'd like to add a new publisher adapter, please open an issue.
@@ -191,6 +192,7 @@ const publisher = new IORedisPublisher<{
191192
subscriber: new Redis(), // For subscribing to events
192193
resumeRetentionSeconds: 60 * 2, // Retain events for 2 minutes to support resume
193194
prefix: 'orpc:publisher:', // avoid conflict with other keys
195+
customJsonSerializers: [] // optional custom serializers
194196
})
195197
```
196198

@@ -215,9 +217,56 @@ const publisher = new UpstashRedisPublisher<{
215217
}>(redis, {
216218
resumeRetentionSeconds: 60 * 2, // Retain events for 2 minutes to support resume
217219
prefix: 'orpc:publisher:', // avoid conflict with other keys
220+
customJsonSerializers: [] // optional custom serializers
218221
})
219222
```
220223

221224
::: info
222225
Resume support is disabled by default in `UpstashRedisPublisher`. Enable it by setting `resumeRetentionSeconds` to an appropriate value.
223226
:::
227+
228+
### Cloudflare Durable Object
229+
230+
```ts
231+
import { DurablePublisher, PublisherDurableObject } from '@orpc/experimental-publisher-durable-object'
232+
233+
export class PublisherDO extends PublisherDurableObject {
234+
constructor(ctx: DurableObjectState, env: Env) {
235+
super(ctx, env, {
236+
resume: {
237+
retentionSeconds: 60 * 2, // Retain events for 2 minutes to support resume
238+
},
239+
})
240+
}
241+
}
242+
243+
export default {
244+
async fetch(request, env) {
245+
const publisher = new DurablePublisher<{
246+
'something-updated': {
247+
id: string
248+
}
249+
}>(env.PUBLISHER_DO, {
250+
prefix: 'publisher1', // avoid conflict with other keys
251+
customJsonSerializers: [] // optional custom serializers
252+
})
253+
},
254+
}
255+
```
256+
257+
::: warning
258+
You must enable the [`enable_request_signal`](https://developers.cloudflare.com/workers/configuration/compatibility-flags/#enable-requestsignal-for-incoming-requests) compatibility flag in your workers to support request abort signals, which are necessary for properly cleaning up subscriptions.
259+
260+
```json
261+
{
262+
"compatibility_flags": [
263+
"enable_request_signal"
264+
]
265+
}
266+
```
267+
268+
:::
269+
270+
::: info
271+
Resume support is disabled by default in `PublisherDurableObject`. Enable it by setting `resume.retentionSeconds` to an appropriate value.
272+
:::

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
},
2828
"devDependencies": {
2929
"@antfu/eslint-config": "^6.2.0",
30+
"@cloudflare/vitest-pool-workers": "^0.10.10",
3031
"@solidjs/testing-library": "^0.8.10",
3132
"@sveltejs/vite-plugin-svelte": "^6.2.1",
3233
"@testing-library/jest-dom": "^6.9.1",
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Hidden folders and files
2+
.*
3+
!.gitignore
4+
!.*.example
5+
6+
# Common generated folders
7+
logs/
8+
node_modules/
9+
out/
10+
dist/
11+
dist-ssr/
12+
build/
13+
coverage/
14+
temp/
15+
16+
# Common generated files
17+
*.log
18+
*.log.*
19+
*.tsbuildinfo
20+
*.vitest-temp.json
21+
vite.config.ts.timestamp-*
22+
vitest.config.ts.timestamp-*
23+
24+
# Common manual ignore files
25+
*.local
26+
*.pem
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<div align="center">
2+
<image align="center" src="https://orpc.unnoq.com/logo.webp" width=280 alt="oRPC logo" />
3+
</div>
4+
5+
<h1></h1>
6+
7+
<div align="center">
8+
<a href="https://codecov.io/gh/unnoq/orpc">
9+
<img alt="codecov" src="https://codecov.io/gh/unnoq/orpc/branch/main/graph/badge.svg">
10+
</a>
11+
<a href="https://www.npmjs.com/package/@orpc/experimental-publisher-durable-object">
12+
<img alt="weekly downloads" src="https://img.shields.io/npm/dw/%40orpc%2Fexperimental-publisher-durable-object?logo=npm" />
13+
</a>
14+
<a href="https://github.com/unnoq/orpc/blob/main/LICENSE">
15+
<img alt="MIT License" src="https://img.shields.io/github/license/unnoq/orpc?logo=open-source-initiative" />
16+
</a>
17+
<a href="https://discord.gg/TXEbwRBvQn">
18+
<img alt="Discord" src="https://img.shields.io/discord/1308966753044398161?color=7389D8&label&logo=discord&logoColor=ffffff" />
19+
</a>
20+
<a href="https://deepwiki.com/unnoq/orpc">
21+
<img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki">
22+
</a>
23+
</div>
24+
25+
<h3 align="center">Typesafe APIs Made Simple 🪄</h3>
26+
27+
**oRPC is a powerful combination of RPC and OpenAPI**, makes it easy to build APIs that are end-to-end type-safe and adhere to OpenAPI standards
28+
29+
---
30+
31+
## Highlights
32+
33+
- **🔗 End-to-End Type Safety**: Ensure type-safe inputs, outputs, and errors from client to server.
34+
- **📘 First-Class OpenAPI**: Built-in support that fully adheres to the OpenAPI standard.
35+
- **📝 Contract-First Development**: Optionally define your API contract before implementation.
36+
- **🔍 First-Class OpenTelemetry**: Seamlessly integrate with OpenTelemetry for observability.
37+
- **⚙️ Framework Integrations**: Seamlessly integrate with TanStack Query (React, Vue, Solid, Svelte, Angular), SWR, Pinia Colada, and more.
38+
- **🚀 Server Actions**: Fully compatible with React Server Actions on Next.js, TanStack Start, and other platforms.
39+
- **🔠 Standard Schema Support**: Works out of the box with Zod, Valibot, ArkType, and other schema validators.
40+
- **🗃️ Native Types**: Supports native types like Date, File, Blob, BigInt, URL, and more.
41+
- **⏱️ Lazy Router**: Enhance cold start times with our lazy routing feature.
42+
- **📡 SSE & Streaming**: Enjoy full type-safe support for SSE and streaming.
43+
- **🌍 Multi-Runtime Support**: Fast and lightweight on Cloudflare, Deno, Bun, Node.js, and beyond.
44+
- **🔌 Extendability**: Easily extend functionality with plugins, middleware, and interceptors.
45+
46+
## Documentation
47+
48+
You can find the full documentation [here](https://orpc.unnoq.com).
49+
50+
## Packages
51+
52+
- [@orpc/contract](https://www.npmjs.com/package/@orpc/contract): Build your API contract.
53+
- [@orpc/server](https://www.npmjs.com/package/@orpc/server): Build your API or implement API contract.
54+
- [@orpc/client](https://www.npmjs.com/package/@orpc/client): Consume your API on the client with type-safety.
55+
- [@orpc/openapi](https://www.npmjs.com/package/@orpc/openapi): Generate OpenAPI specs and handle OpenAPI requests.
56+
- [@orpc/otel](https://www.npmjs.com/package/@orpc/otel): [OpenTelemetry](https://opentelemetry.io/) integration for observability.
57+
- [@orpc/nest](https://www.npmjs.com/package/@orpc/nest): Deeply integrate oRPC with [NestJS](https://nestjs.com/).
58+
- [@orpc/react](https://www.npmjs.com/package/@orpc/react): Utilities for integrating oRPC with React and React Server Actions.
59+
- [@orpc/tanstack-query](https://www.npmjs.com/package/@orpc/tanstack-query): [TanStack Query](https://tanstack.com/query/latest) integration.
60+
- [@orpc/experimental-react-swr](https://www.npmjs.com/package/@orpc/experimental-react-swr): [SWR](https://swr.vercel.app/) integration.
61+
- [@orpc/vue-colada](https://www.npmjs.com/package/@orpc/vue-colada): Integration with [Pinia Colada](https://pinia-colada.esm.dev/).
62+
- [@orpc/hey-api](https://www.npmjs.com/package/@orpc/hey-api): [Hey API](https://heyapi.dev/) integration.
63+
- [@orpc/zod](https://www.npmjs.com/package/@orpc/zod): More schemas that [Zod](https://zod.dev/) doesn't support yet.
64+
- [@orpc/valibot](https://www.npmjs.com/package/@orpc/valibot): OpenAPI spec generation from [Valibot](https://valibot.dev/).
65+
- [@orpc/arktype](https://www.npmjs.com/package/@orpc/arktype): OpenAPI spec generation from [ArkType](https://arktype.io/).
66+
67+
## `@orpc/experimental-publisher-durable-object`
68+
69+
Publisher adapter for [Durable Objects](https://developers.cloudflare.com/durable-objects/)
70+
71+
## Sponsors
72+
73+
<p align="center">
74+
<a href="https://cdn.jsdelivr.net/gh/unnoq/unnoq/sponsors.svg">
75+
<img src='https://cdn.jsdelivr.net/gh/unnoq/unnoq/sponsors.svg'/>
76+
</a>
77+
</p>
78+
79+
## License
80+
81+
Distributed under the MIT License. See [LICENSE](https://github.com/unnoq/orpc/blob/main/LICENSE) for more information.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { defineBuildConfig } from 'unbuild'
2+
3+
export default defineBuildConfig({
4+
externals: [
5+
'cloudflare:workers',
6+
],
7+
})
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"name": "@orpc/experimental-publisher-durable-object",
3+
"type": "module",
4+
"version": "0.0.1",
5+
"license": "MIT",
6+
"homepage": "https://orpc.unnoq.com",
7+
"repository": {
8+
"type": "git",
9+
"url": "git+https://github.com/unnoq/orpc.git",
10+
"directory": "packages/publisher-durable-object"
11+
},
12+
"keywords": [
13+
"unnoq",
14+
"orpc"
15+
],
16+
"publishConfig": {
17+
"exports": {
18+
".": {
19+
"types": "./dist/index.d.mts",
20+
"import": "./dist/index.mjs",
21+
"default": "./dist/index.mjs"
22+
}
23+
}
24+
},
25+
"exports": {
26+
".": "./src/index.ts"
27+
},
28+
"files": [
29+
"dist"
30+
],
31+
"scripts": {
32+
"build": "unbuild",
33+
"build:watch": "pnpm run build --watch",
34+
"type:check": "tsc -b",
35+
"type:check:test": "tsc -p tsconfig.test.json --noEmit"
36+
},
37+
"dependencies": {
38+
"@orpc/client": "workspace:*",
39+
"@orpc/experimental-publisher": "workspace:*",
40+
"@orpc/shared": "workspace:*",
41+
"@orpc/standard-server": "workspace:*"
42+
},
43+
"devDependencies": {
44+
"@cloudflare/workers-types": "^4.20251118.0",
45+
"@orpc/standard-server-peer": "workspace:*",
46+
"@types/node": "^22.15.30"
47+
}
48+
}

0 commit comments

Comments
 (0)