boardgame.io

Server

Creates a boardgame.io server. This is only required when multiplayer is set to true on the client. It creates a Koa app that keeps track of the game states of the various clients connected to it, and also broadcasts updates to those clients so that all browsers that are connected to the same game are kept in sync in realtime.

The server also hosts a REST API that is used for creating and joining games. This is hosted on the same port, but can be configured to run on a separate port.

Arguments

A config object with the following options:

  1. games (array) (required): a list of game implementations (each should be an object conforming to the Game API).

  2. origins (array) (required): a list of allowed origins for CORS.

    The list can contain strings or regular expressions, matching the origins that are allowed to access the game server. For example, this could be ['https://example.com'] if that’s where your game is running. While developing locally you probably want to allow any page running on localhost to connect. See Usage below for an example.

  1. db (object): the database connector. If not provided, an in-memory implementation is used.

  2. transport (object): the transport implementation. If not provided, socket.io is used.

  3. uuid (function): an optional function that returns a unique identifier, used to create new game IDs and — if generateCredentials is not specified — player credentials. Defaults to nanoid.

  4. generateCredentials (function): an optional function that returns player credentials to store in the game metadata and validate against. If not specified, the uuid function will be used.

  5. authenticateCredentials (function): an optional function that tests if a player’s move is made with the correct credentials when using the default socket.io transport implementation.

  6. apiOrigins (array): a list of allowed origins for requests to the Lobby API. Defaults to the value provided as the origins option (which also applies to the socket transport).

  7. apiBodyLimit (string | number): maximum size for request bodies parsed by the Lobby API (e.g. '5mb' or 5 * 1024 * 1024 bytes). Defaults to '5mb'. Increase this only if your game legitimately needs large setupData. Note that large setupData is generally an anti-pattern — prefer passing lightweight config and generating heavy data inside your game’s setup() function.

Returns

An object that contains:

  1. run (function): A function to run the server. (portOrConfig, callback) => ({ apiServer, appServer })

  2. kill (function): A function to stop the server. ({ apiServer, appServer }) => void

  3. app (object): The Koa app.

  4. db (object): The database implementation.

  5. router (object): The Koa Router for the server API.

Usage

Basic

const { Server, Origins } = require('boardgame.io/server');

const server = Server({
  // Provide the definitions for your game(s).
  games: [game1, game2, ...],

  // Provide the database storage class to use.
  db: new DbConnector(),

  origins: [
    // Allow your game site to connect.
    'https://www.mygame.domain',
    // Allow localhost to connect, except when NODE_ENV is 'production'.
    Origins.LOCALHOST_IN_DEVELOPMENT
  ],
});

server.run(8000);

With callback

server.run(8000, () => console.log("server running..."));

With custom Lobby settings

You can pass lobbyConfig to configure the Lobby API during server startup:

const lobbyConfig = {
  apiPort: 8080,
  apiCallback: () => console.log('Running Lobby API on port 8080...'),
};

server.run({ port: 8000, lobbyConfig });

Options are:

The run config also accepts a host alongside port, which binds both the game server and the Lobby API server to that host. It defaults to all interfaces.

With HTTPS

const { Server } = require('boardgame.io/server');
const fs = require('fs');

const server = Server({
  games: [game1, game2, ...],

  https: {
    cert: fs.readFileSync('/path/to/cert'),
    key: fs.readFileSync('/path/to/key'),
  },
});

server.run(8000);

With custom authentication

generateCredentials is called when a player joins a game with:

authenticateCredentials is called when a player makes a move with:

Below is an example of how you might implement custom authentication with a hypothetical authService library.

The generateCredentials method checks for the Authorization header on incoming requests and tries to use it to decode a token. It returns an ID from the result, storing a public user ID as “credentials” in the game metadata.

The authenticateCredentials method passed to the Server also expects a similar token, which when decoded matches the ID stored in game metadata.

const { Server } = require('boardgame.io/server');

const generateCredentials = async ctx => {
  const authHeader = ctx.request.headers['authorization'];
  const token = await authService.decodeToken(authHeader);
  return token.uid;
}

const authenticateCredentials = async (credentials, playerMetadata) => {
  if (credentials) {
    const token = await authService.decodeToken(credentials);
    if (token.uid === playerMetadata.credentials) return true;
  }
  return false;
}

const server = Server({
  games: [game1, game2, ...],
  generateCredentials,
  authenticateCredentials,
});

server.run(8000);

!> N.B. This approach is not currently compatible with how the React <Lobby> provides credentials.

Extending the server

The boardgame.io server uses Koa and @koa/router. You can customise the Lobby API by accessing the router instance, for example to add routes or to add custom middleware for existing routes. See an example of customising the entire Koa app in the Heroku deployment guide.

Add a custom route

const server = Server({ /* options */ });

server.router.get('/custom-endpoint', (ctx, next) => {
  ctx.body = 'Hello World!';
});

server.run(8000);

Add middleware

const server = Server({ /* options */ });

// Add middleware to the create game route.
server.router.use('/games/:name/create', async (ctx, next) => {
  // Decide number of players etc. based on some other API.
  const { numPlayers, setupData } = await fetchDataFromSomeCustomAPI();
  // Set request body to be used by the create game route.
  ctx.request.body.numPlayers = numPlayers;
  ctx.request.body.setupData = setupData;
  next();
});

server.run(8000);