snapshot of https://github.com/tldraw/tldraw.git (main, 2026-09-23; blobs > 131072B omitted)
Some checks failed
Deploy analytics worker / Deploy analytics worker to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
i18n upload strings / Upload latest strings (push) Failing after 1s
Deploy bemo / Deploy bemo to ${{ (github.ref == 'refs/heads/bemo-production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
Deploy grafana resources / ${{ github.event_name == 'push' && 'Deploy grafana resources' || 'Validate grafana resources (dry run)' }} (push) Failing after 0s
End to end tests (dotcom) / Check for relevant changes (push) Failing after 0s
End to end tests (examples) / Check for relevant changes (push) Failing after 0s
End to end tests (mermaid readability) / Check for relevant changes (push) Failing after 1s
End to end tests (perf) / Check for relevant changes (push) Failing after 0s
Publish editor extensions / Publish editor extensions (push) Failing after 1s
Publish SDK packages / Remove the publish-packages label (push) Has been skipped
Checks / Tests & checks (push) Has been cancelled
Checks / Build all projects (push) Has been cancelled
Deploy templates / Deploy templates (push) Has been cancelled
End to end tests (dotcom) / End to end tests (dotcom) (push) Has been cancelled
End to end tests (mermaid readability) / End to end tests (mermaid readability) (push) Has been cancelled
End to end tests (perf) / End to end tests (perf) (push) Has been cancelled
Deploy .com / Deploy dotcom to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Has been cancelled
End to end tests (examples) / End to end tests (examples) (push) Has been cancelled
Publish SDK packages / Publish SDK packages (push) Has been cancelled
Publish SDK packages / Tell user released version (push) Has been cancelled
Publish SDK packages / Publishes code templates to separate repositories (push) Has been cancelled
Publish SDK packages / Deploys the template demos with the new SDK version (push) Has been cancelled
Some checks failed
Deploy analytics worker / Deploy analytics worker to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
i18n upload strings / Upload latest strings (push) Failing after 1s
Deploy bemo / Deploy bemo to ${{ (github.ref == 'refs/heads/bemo-production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
Deploy grafana resources / ${{ github.event_name == 'push' && 'Deploy grafana resources' || 'Validate grafana resources (dry run)' }} (push) Failing after 0s
End to end tests (dotcom) / Check for relevant changes (push) Failing after 0s
End to end tests (examples) / Check for relevant changes (push) Failing after 0s
End to end tests (mermaid readability) / Check for relevant changes (push) Failing after 1s
End to end tests (perf) / Check for relevant changes (push) Failing after 0s
Publish editor extensions / Publish editor extensions (push) Failing after 1s
Publish SDK packages / Remove the publish-packages label (push) Has been skipped
Checks / Tests & checks (push) Has been cancelled
Checks / Build all projects (push) Has been cancelled
Deploy templates / Deploy templates (push) Has been cancelled
End to end tests (dotcom) / End to end tests (dotcom) (push) Has been cancelled
End to end tests (mermaid readability) / End to end tests (mermaid readability) (push) Has been cancelled
End to end tests (perf) / End to end tests (perf) (push) Has been cancelled
Deploy .com / Deploy dotcom to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Has been cancelled
End to end tests (examples) / End to end tests (examples) (push) Has been cancelled
Publish SDK packages / Publish SDK packages (push) Has been cancelled
Publish SDK packages / Tell user released version (push) Has been cancelled
Publish SDK packages / Publishes code templates to separate repositories (push) Has been cancelled
Publish SDK packages / Deploys the template demos with the new SDK version (push) Has been cancelled
This commit is contained in:
119
templates/sync-cloudflare/README.md
Normal file
119
templates/sync-cloudflare/README.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# tldraw sync server
|
||||
|
||||
This is a production-ready backend for [tldraw sync](https://tldraw.dev/docs/sync).
|
||||
|
||||
- Your client-side tldraw-based app can be served from anywhere you want.
|
||||
- This backend uses [Cloudflare Workers](https://developers.cloudflare.com/workers/), and will need
|
||||
to be deployed to your own Cloudflare account.
|
||||
- Each whiteboard is synced via
|
||||
[WebSockets](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) to a [Cloudflare
|
||||
Durable Object](https://developers.cloudflare.com/durable-objects/), which persists room state in
|
||||
its built-in SQLite storage.
|
||||
- Uploaded images and videos are stored in a [Cloudflare
|
||||
R2](https://developers.cloudflare.com/r2/) bucket.
|
||||
- Although unrelated to tldraw sync, this server also includes a component to fetch link previews
|
||||
for URLs added to the canvas.
|
||||
This is a minimal setup of the same system that powers multiplayer collaboration for hundreds of
|
||||
thousands of rooms & users on www.tldraw.com. Because durable objects effectively create a mini
|
||||
server instance for every single active room, we've never needed to worry about scale. Cloudflare
|
||||
handles the tricky infrastructure work of ensuring there's only ever one instance of each room, and
|
||||
making sure that every user gets connected to that instance. We've found that with this approach,
|
||||
each room is able to handle about 50 simultaneous collaborators.
|
||||
|
||||
[](https://www.tldraw.com/ro/Yb_QHJFP9syPZq1YrV3YR?v=-255,-148,2025,1265&p=page)
|
||||
|
||||
When a user opens a room, they connect via Workers to a durable object. Each durable object is like
|
||||
its own miniature server. There's only ever one for each room, and all the users of that room
|
||||
connect to it. When a user makes a change to the drawing, it's sent via a websocket connection to
|
||||
the durable object for that room. The durable object applies the change to its in-memory copy of the
|
||||
document, and broadcasts the change via websockets to all other connected clients. Room state is
|
||||
persisted automatically to the durable object's built-in SQLite storage, so it survives restarts
|
||||
and hibernation. When the last client leaves the room, the durable object will shut down.
|
||||
|
||||
Static assets like images and videos are too big to be synced via websockets and a durable object.
|
||||
Instead, they're uploaded to workers which store them in an R2 bucket. When they're downloaded,
|
||||
they're cached on cloudflare's edge network to reduce costs and make serving them faster.
|
||||
|
||||
## Development
|
||||
|
||||
To install dependencies, run `yarn`. To start a local development server, run `yarn dev`. This will
|
||||
start a [`vite`](https://vitejs.dev/) dev server running both your application frontend, and the
|
||||
cloudflare workers backend via the [cloudflare vite
|
||||
plugin](https://developers.cloudflare.com/workers/vite-plugin/). The app & server should now be
|
||||
running at http://localhost:5137.
|
||||
|
||||
The backend worker is under [`worker`](./worker/), and is split across several files:
|
||||
|
||||
- **[`worker/worker.ts`](./worker/worker.ts):** the main entrypoint to the worker, defining each
|
||||
route available.
|
||||
- **[`worker/TldrawDurableObject.ts`](./worker/TldrawDurableObject.ts):** the sync durable object.
|
||||
An instance of this is created for every active room. This exposes a
|
||||
[`TLSocketRoom`](https://tldraw.dev/reference/sync-core/TLSocketRoom) over websockets, and
|
||||
persists room state to the durable object's built-in SQLite storage.
|
||||
- **[`worker/assetUploads.ts`](./worker/assetUploads.ts):** uploads, downloads, and caching for
|
||||
static assets like images and videos.
|
||||
- **[`worker/bookmarkUnfurling.ts`](./worker/bookmarkUnfurling.ts):** extract URL metadata for bookmark shapes.
|
||||
|
||||
The frontend client is under [`client`](./client):
|
||||
|
||||
- **[`client/App.tsx`](./client/App.tsx):** the main client `<App />` component. This connects our
|
||||
sync backend to the `<Tldraw />` component, wiring in assets and bookmark previews.
|
||||
- **[`client/multiplayerAssetStore.tsx`](./client/multiplayerAssetStore.tsx):** how does the client
|
||||
upload and retrieve assets like images & videos from the worker?
|
||||
- **[`client/getBookmarkPreview.tsx`](./client/getBookmarkPreview.tsx):** how does the client fetch
|
||||
bookmark previews from the worker?
|
||||
|
||||
## Custom shapes
|
||||
|
||||
To add support for custom shapes, see the [tldraw sync custom shapes docs](https://tldraw.dev/docs/sync#Custom-shapes--bindings).
|
||||
|
||||
## Adding cloudflare to your own repo
|
||||
|
||||
If you already have an app using tldraw and want to use the system in this repo, you can copy and
|
||||
paste the relevant parts to your own app.
|
||||
|
||||
To add the server to your own app, copy the contents of the [`worker`](./worker/) folder and
|
||||
[`./wrangler.toml`](./wrangler.toml) into your app. Add the dependencies from
|
||||
[`package.json`](./package.json). You can run the worker using `wrangler dev` in the same folder as
|
||||
`./wrangler.toml`.
|
||||
|
||||
To point your existing client at the server defined in this repo, copy
|
||||
[`client/multiplayerAssetStore.tsx`](./client/multiplayerAssetStore.tsx) and
|
||||
[`client/getBookmarkPreview.tsx`](./client/getBookmarkPreview.tsx) into your app. Then, adapt the
|
||||
code from [`client/App.tsx`](./client/App.tsx) to your own app. Adapt the `/api/` URLs used in each
|
||||
of these files to point at your new `wrangler dev` server.
|
||||
|
||||
## Deployment
|
||||
|
||||
To deploy this example, you'll need to create a cloudflare account and create an R2 bucket to store
|
||||
uploaded images and videos. Update `bucket_name = 'tldraw-content'` in
|
||||
[`wrangler.toml`](./wrangler.toml) with the name of your new bucket.
|
||||
|
||||
To actually deploy the app, first create a production build using `yarn build`. Then, run `yarn
|
||||
wrangler deploy`. This will deploy the backend worker along with the frontend app to cloudflare.
|
||||
This should give you a workers.dev URL, but you can also [configure a custom
|
||||
domain](https://developers.cloudflare.com/workers/configuration/routing/custom-domains/).
|
||||
|
||||
## License
|
||||
|
||||
This project is provided under the MIT license found [here](https://github.com/tldraw/tldraw-sync-cloudflare/blob/main/LICENSE.md). The tldraw SDK is provided under the [tldraw license](https://github.com/tldraw/tldraw/blob/main/LICENSE.md).
|
||||
|
||||
## Trademarks
|
||||
|
||||
Copyright (c) 2024-present tldraw Inc. The tldraw name and logo are trademarks of tldraw. Please see our [trademark guidelines](https://github.com/tldraw/tldraw/blob/main/TRADEMARKS.md) for info on acceptable usage.
|
||||
|
||||
## Distributions
|
||||
|
||||
You can find tldraw on npm [here](https://www.npmjs.com/package/@tldraw/tldraw?activeTab=versions).
|
||||
|
||||
## Contribution
|
||||
|
||||
Found a bug? Please [submit an issue](https://github.com/tldraw/tldraw/issues/new).
|
||||
|
||||
## Community
|
||||
|
||||
Have questions, comments or feedback? [Join our discord](https://discord.tldraw.com/?utm_source=github&utm_medium=readme&utm_campaign=sociallink). For the latest news and release notes, visit [tldraw.dev](https://tldraw.dev).
|
||||
|
||||
## Contact
|
||||
|
||||
Find us on Twitter/X at [@tldraw](https://twitter.com/tldraw).
|
||||
Reference in New Issue
Block a user