Deploy boardgame.io multiplayer games to Cloudflare Workers using PartyKit — with zero client-side changes.
bgio-partykit is a library and CLI that lets you run boardgame.io game servers on PartyKit's Durable Objects infrastructure. Your existing Client with SocketIO() transport and LobbyClient work exactly as they do today — no UI or networking code needs to change.
- Zero client-side changes — Use the standard boardgame.io
ClientwithSocketIO()transport. Existing games and frontends work out of the box. - Standard
LobbyClientcompatibility — The full boardgame.io lobby REST API (/games,/games/:name/create,/games/:name/:id/join, etc.) is implemented and served from PartyKit. - Match persistence via PartyKit
room.storage— Every match is a Durable Object with its ownStorageAPI.Asyncbackend. Game state, metadata, and logs survive restarts and are replicated across Cloudflare's edge. - Deploy to Cloudflare Workers via PartyKit — One-command deploy to Cloudflare's global edge network. No server management, no Docker, no scaling headaches.
- Interactive CLI scaffolding —
pnpm create bgio-partykitscaffolds a complete, working project with your game definition, TypeScript configs, and PartyKit manifest in seconds. - TypeScript-first — Full type definitions for the public API, game registry, storage layer, and transport adapters.
The fastest way to get started is with the interactive CLI:
pnpm create bgio-partykit my-game
cd my-game
pnpm dev # starts the local PartyKit dev server on localhost:1999The CLI will ask you for a project name, game name, and number of players. It then:
- Copies bundled templates with your variables substituted.
- Runs
pnpm install. - Runs
git init. - Prints next steps.
Open the generated demo page or point your existing boardgame.io client at http://localhost:1999 and it will just work.
If you already have a boardgame.io game and want to host it on PartyKit:
npm install bgio-partykitCreate a file (e.g., src/server.ts) that exports the default Server class and registers your games:
import Server, { registerGame } from "bgio-partykit";
import MyGame from "./game";
// Register your game(s)
registerGame(MyGame);
// Export the Server as the PartyKit room entrypoint
export default Server;Create a partykit.json at the project root:
{
"name": "my-bgio-game",
"main": "src/server.ts",
"serve": {
"path": "static"
},
"compatibilityDate": "2024-07-01",
"parties": {
"lobby": "src/server.ts",
"match": "src/server.ts",
"bus": "src/server.ts"
}
}All three party kinds (lobby, match, bus) point to the same server file. The runtime routes requests to the appropriate internal handler based on the room name.
Your existing client code requires only the server URL:
import { Client } from "boardgame.io/react";
import { SocketIO } from "boardgame.io/multiplayer";
import { LobbyClient } from "boardgame.io/client";
import MyGame from "./game";
import { socketIOServerUrl, lobbyClientUrl } from "bgio-partykit";
const BASE_URL = "http://localhost:1999"; // or your deployed URL
const App = Client({
game: MyGame,
transport: SocketIO({ server: socketIOServerUrl(BASE_URL) }),
});
const lobby = new LobbyClient({ server: lobbyClientUrl(BASE_URL) });┌─────────────────┐ WebSocket ┌─────────────────────────────┐
│ boardgame.io │ ◄─────────────────►│ PartyKit Durable Object │
│ Client + │ Socket.IO │ (one per match room) │
│ LobbyClient │ │ │
└─────────────────┘ │ ┌───────────────────────┐ │
│ │ vendored party.io │ │
│ │ Socket.IO server │ │
│ └───────────────────────┘ │
│ │ │
│ ┌───────────────────────┐ │
│ │ boardgame.io Master │ │
│ │ (authoritative logic)│ │
│ └───────────────────────┘ │
│ │ │
│ ┌───────────────────────┐ │
│ │ StorageAPI.Async │ │
│ │ backed by │ │
│ │ room.storage │ │
│ └───────────────────────┘ │
└─────────────────────────────┘
- Each match is a PartyKit room — A Cloudflare Durable Object that maintains WebSocket connections, game state, and in-memory queues for that specific match.
- Socket.IO server via vendored
party.io— The project bundles a fork ofparty.iothat implements a Socket.IO server on top of PartyKit's WebSocket infrastructure, enablingSocketIO()transport compatibility. - Authoritative
Masterclass — The standard boardgame.ioMasterprocesses all moves server-side. Game rules, turn order, and state transitions run inside the Durable Object exactly as they would on a Node.js server. StorageAPI.Asyncbacked byroom.storage— Match state, metadata, logs, and initial state are persisted to PartyKit's transactional key-value storage. ThePartyKitStorageclass implements the fullStorageAPIcontract.TransportAPIadapter withplayerViewfiltering —createTransportAPIbuilds a boardgame.io-compatible transport layer that broadcasts updates to all connected sockets, applying per-playerplayerViewfiltering so clients only see the state they are allowed to see.
The PartyKit room entrypoint. Implements Party.Server and handles HTTP requests, WebSocket upgrades, lobby routing, and Socket.IO handshakes.
import Server from "bgio-partykit";
export default Server;import { registerGame, getGame, listGames, getGames } from "bgio-partykit";
import type { RegisteredGame } from "bgio-partykit";
registerGame(myGame);
const names = listGames(); // ["tic-tac-toe", "my-game"]
const game = getGame("my-game"); // RegisteredGame | undefinedimport {
PartyKitStorage,
StorageType,
RemoteStorage,
} from "bgio-partykit";
import type {
FetchOpts,
CreateMatchOpts,
ListMatchesOpts,
} from "bgio-partykit";PartyKitStorage— DirectStorageAPIimplementation backed by aParty.Storageinstance. Used inside the match Durable Object.RemoteStorage— AsyncStorageAPIimplementation that forwards CRUD operations to the match Durable Object via HTTP. Used by the lobby Durable Object.StorageType.SYNC/StorageType.ASYNC— Storage type constants.
import { createTransportAPI } from "bgio-partykit";Builds a boardgame.io TransportAPI for a given socket, game definition, and match ID. Handles per-player state filtering and broadcast/fallback logic for PartyKit's I/O isolation.
import { lobbyClientUrl, socketIOServerUrl } from "bgio-partykit";
const lobby = new LobbyClient({ server: lobbyClientUrl("http://localhost:1999") });
const client = Client({
game: TicTacToe,
transport: SocketIO({ server: socketIOServerUrl("http://localhost:1999") }),
});import { TicTacToe } from "bgio-partykit";
import type { TicTacToeState } from "bgio-partykit";A built-in Tic-Tac-Toe game definition, useful for testing and as a reference implementation.
Deploy your game to Cloudflare Workers with a single command:
pnpm deployBefore deploying, make sure the following environment variables are set:
export CLOUDFLARE_ACCOUNT_ID=your-account-id
export CLOUDFLARE_API_TOKEN=your-api-tokenYou can find these in your Cloudflare dashboard. The deploy script builds the project and pushes it to PartyKit's edge infrastructure, making your game server available globally within seconds.
The repository includes unit tests, local end-to-end tests, and deployed end-to-end tests.
| Command | Description |
|---|---|
pnpm test |
Run unit tests with Vitest |
pnpm dev |
Start local PartyKit dev server on localhost:1999 |
pnpm e2e:local |
Run E2E suite against the local dev server |
pnpm e2e:deployed |
Run E2E suite against the currently deployed instance |
pnpm e2e:lobby-client |
Verify LobbyClient compatibility |
pnpm lint |
ESLint |
pnpm typecheck |
TypeScript --noEmit |
E2E scripts spin up the dev server, create matches via the lobby API, connect Socket.IO clients, play moves, assert state transitions, and clean up. They exercise the full stack: lobby → match DO → storage → transport → Socket.IO broadcast.
bgio-partykit/
├── src/
│ ├── index.ts # Public API exports
│ ├── server.ts # PartyKit Server entrypoint (lobby / match / bus routing)
│ ├── match-room.ts # Per-match Durable Object logic (Master, queues, sockets)
│ ├── lobby.ts # Lobby REST API handlers (create, join, leave, play again)
│ ├── registry.ts # Game registry (registerGame, getGame, listGames)
│ ├── storage.ts # PartyKitStorage (StorageAPI.Async via room.storage)
│ ├── remote-storage.ts # RemoteStorage (HTTP-forwarding StorageAPI for lobby)
│ ├── transport.ts # createTransportAPI with playerView filtering
│ └── games/
│ └── tic-tac-toe.ts # Built-in demo game
├── packages/
│ ├── party.io/ # Vendored Socket.IO server for PartyKit
│ └── create-bgio-partykit/ # Interactive CLI scaffolding package
├── scripts/
│ ├── e2e-local.mjs # Local E2E test runner
│ ├── e2e-deployed.mjs # Deployed E2E test runner
│ └── deploy.mjs # Deploy helper script
├── static/
│ └── index.html # Static demo page served by PartyKit
├── tests/
│ ├── e2e/ # End-to-end test suites
│ ├── foundation/ # Unit tests for core utilities
│ ├── lobby/ # Lobby API unit tests
│ └── runtime/ # Runtime / integration tests
├── partykit.json # PartyKit manifest (parties, compat date, serve)
├── package.json # Package manifest & scripts
└── README.md # You are here
MIT