Add to existing product
Adding multiplayer with Room Service involves two steps:
- (10 minutes) Create an Auth Webhook: a POST-request-accepting HTTP endpoint on your server.
- (5 minutes) Install the browser SDK and start building!
Creating an Auth Webhook
Room Service uses your auth setup. To do that, we ask you to create an endpoint that accepts POST requests on your server.
By default, this endpoint receives a POST body from the browser SDK containing resources the user would like to access. To authorize the user, we'll forward the message along with our API key and a user id to Room Service via the /provision
endpoint.
The following is an example in Node.js/Express, but you can build your route in any language:
1const express = require("express");2const fetch = require("node-fetch");3const cors = require("cors");4const app = express();5const port = 8080;6app.use(express.json());7app.use(cors());89// Replace this with your authorization scheme.10function isLoggedIn(req) {11 return true; // for the moment, we'll just let everyone in12}1314const API_KEY = "___APIKEY___";1516app.post("/my-roomservice", async (req, res) => {17 if (!isLoggedIn(req)) {18 return res.send(401);19 }2021 // In practice, this should be whatever user id YOU use.22 const user = Math.random().toString(36).substr(2, 9);23 const body = req.body;24 const r = await fetch("https://super.roomservice.dev/provision", {25 method: "POST",26 headers: {27 Authorization: `Bearer: ${API_KEY}`,28 "Content-Type": "application/json",29 },3031 body: JSON.stringify({32 user: user,33 resources: body.resources,34 }),35 });3637 return res.json(await r.json());38});3940app.listen(port, () => {41 console.log(`Example app listening at http://localhost:${port}`);42});43
Setting up the browser SDK
Room Service comes with a ~4kb browser library written in TypeScript. Install it with:
1yarn add @roomservice/browser23// OR45npm install --save @roomservice/browser
To start, import the SDK and create a new client that points to your Auth Webhook.
1import { RoomService } from '@roomservice/browser';23const service = new RoomService({4 auth: '/my-roomservice',5});
Creating your first room
The key concept of Room Service is the "room". Rooms are like shared folders that hold all your maps, lists, and presence keys inside. If you update something within a room, everyone who's connected to the room will receive the update.
You might create a room for a team, a document, a page, or anything else you'd like.
1const room = await service.room('nasa');
Working with lists
Room Service comes with several data structures that make updates locally-first, so your app is as fast as possible. Each data structure manages conflicts between users automatically, but this is most obvious in lists.
To create a list, grab one from the room:
1let list = room.list("todos")
Lists (and maps) are immutable, so every change returns a new copy of the list:
1list = list.push("water plants")2list = list.insertAfter(1, "feed cats")3list = list.set(1, "feed dogs")
Anytime you make a change to something in Room Service, we'll automatically tell everyone else connected to the room. To get updates, we just have to subscribe to them.
1room.subscribe(list, (nextList) => {2 // nextList is a new list with the changes applied!3})
To get data out of the list, we can either convert the whole thing (fast) or get an item directly (faster):
1let todos = list.toArray() // ["water plants", "feed dogs"]2let first = list.get(0) // "water plants"
And when you need to, you can delete anything from the list:
1list = list.delete(0)
Conflicts are managed automatically
Say Alice deletes the 2nd item in a list, at the same time Bob updates the 3rd item. What happens?
1// Initial list looks like2// ["cats", "birds", "dogs", "snakes", "fish"]34// Alice5list.delete(2)67// Bob8list.set(3, "horses")910// ...After network communication11// ???
In a normal array, this is undefined behavior. If Alice's network is just a bit faster and she goes first, then Bob is changing "fish"
into "horses"
, which he probably didn't want to do.
In Room Service, this just-works™ the way you'd want it to. Regardless of who's arrives first:
- Bob will change
"snakes"
into"horses"
- Alice will delete
"dogs"
This works because Room Service's maps and lists aren't true objects and arrays. They're distributed data structures (CRDT-likes) meant for building multiplayer, with similar interfaces.
Working with maps
Much like lists, Room Service also comes with optimistically updating maps. To create one, just use the room:
1let map = room.map("document")
Like lists, maps are immutable; their update functions return a copy of themselves.
1map = map.set("title", "Cool doc 123")2map = map.delete("body")
To get updates on maps, just subscribe to them with the room.
1room.subscribe(map, (nextMap) => {2 // nextMap is a new map with the changes applied!3})
To get data out of the map, use get
.
1map.get("title") // "Cool doc 123"