Room types
A project starts with one kind of room. irtio/room.ts exports one defineRoom, every room in the
project runs it, and any difference between two rooms is a branch inside one handler sharing one
schema.
That runs out when a project wants two genuinely different rooms. A game with a match room and a persistent notifications hub does not want them sharing a schema, a tick rate, or a memory budget: the match room simulates 30 times a second and the hub pushes JSON at nobody in particular.
Room types are the answer. A project can define several rooms, name them, and address them.
The directory
Put each room in irtio/rooms/, one file per type, each with its own default export:
irtio/
rooms/
default.ts the default type
arena.ts the arena type
notifications.ts The filename is the type name. Type names are lowercase, start with a letter or a digit, use only a-z 0-9 - _, and run to 24 characters.
irtio/room.ts still works and still means what it always meant: a project with one room file has
one room type, the default one, and nothing about it changes. If you have both, irtio/room.ts is
the default type and irtio/rooms/ holds the others.
Addressing
The room type goes in the room id, in front of a colon:
arena:x7Qb a room of the arena type
notifications:global the notifications hub
lobby the default type
AB23 the default type, from a shared link A room id with no prefix means the default type. That is why every link, save, log line and room code minted before room types existed keeps working: a plain id was the only kind there was, and it still resolves to the only definition such a project has.
On the client, the room id is the room id. Pass the whole string, with the schema that type exports:
import { arenaSchema } from './irtio/rooms/arena-schema';
const room = await joinRoom(arenaSchema, { room: 'arena:x7Qb' }); Two rules the platform holds to, worth knowing because they are not guesses:
- The colon is the only separator, and it is never a slash. A room id is one opaque string
everywhere below the join: snapshot keys, player storage, alarms,
irtio logs --room. Nothing downstream splits it. - A plain id and
default:are different rooms.lobbyanddefault:lobbyboth run the default type’s code, and they are two rooms with two sets of state, because the room id is the key. Pick one spelling and stay with it.
Each type brings its own schema
Every type has its own defineSchema, so the arena’s collections and the hub’s are unrelated. The
convention is a schema module per type beside the room file:
irtio/
rooms/
arena.ts
arena-schema.ts
notifications.ts
notifications-schema.ts A join is checked against the schema of the type it names. Joining arena:x7Qb with the
notifications schema is refused with a schema mismatch, even though both schemas belong to the same
project, and the error says which type it was checking.
There is no code generator. The schema a client joins with is the module it imports, the way it always has been.
Each type declares its own size
A room type can say how much worker memory it needs:
export default defineRoom(notificationsSchema, {
mode: 'event',
memoryMb: 40,
// ...
}); memoryMb is the room worker’s heap ceiling, a whole number of MB from 32 to 4096. It is a real
limit: a room that goes past it dies as a room crash, and its siblings keep serving.
Declaring it does two things. The obvious one is that a small room stops being sized like a large one. The useful one is that your project’s memory becomes arithmetic instead of a guess: the declared budgets of every type, plus the supervisor’s own footprint, plus a reserve for what lives outside the JavaScript heap, are checked against the VM your size class gives you when the tenant boots. If they do not fit, the tenant logs exactly which types and exactly what the sum was.
The check counts every type, not an expected mix, because nothing can know which of your rooms will be awake at three in the morning. A type that declares nothing is counted at the default the VM’s size derives, which is also what it will actually run under.
Deploys move every type together
A deploy is still one version of one project. All the types in it ship together, and a rollback takes all of them back together. There is no way to deploy or roll back a single type, and that is deliberate: types share a project’s stored state, and a version where the arena had rolled back and the hub had not is a project whose data disagrees with itself.
The practical consequences:
- A deploy that changes three schemas is one version with three schema changes in it, classified as a whole. If any of them is breaking, the deploy is breaking.
- A room that slept across a deploy wakes on the newest version and migrates against its own type’s schema chain.
- A rollback returns every type to the target version, and every room to the state it had there.
See migrations for what a schema change costs and how to write one.