For node.js

Setup

The node.js setup follows a very similar approach to setting up a js project for web. The differences between the node.js setup and browser setup are:

  1. The node.js SDK is meant to be used with an application principal i.e. it is supposed to be used with an access key/access secret pair. It does not support user-based authorization for performing API calls.

  2. The node.js SDK does not support any messaging pipelines and is only designed to provide a service layer to mitter.io since it is designed to be used for projects implementing an application backend.

  3. Do note that with a websocket polyfill, you can still use @mitter-io/web in your node.js project, but this is currently not supported. In future versions we will be documenting the usage of the @mitter-io/web package within a node.js application.

To install the node.js SDK, add the following dependencies in your application

yarn add @mitter-io/node @mitter-io/models

or with npm

npm install @mitter-io/node @mitter-io/models

To initialize a mitter object, use the following:

import { Mitter } from '@mitter-io/node'

const mitter = Mitter.forNode({
    accessKey: {
        accessKey: 'your-access-key',
        accessSecret: 'your-accesss-ecret'
    },
    applicationId: 'fb70ff76-ea33-4bb0-bd59-90853f103202', /* your application id */
    mitterApiBaseUrl: '<mitter-api-base-url>' /* look for values below */
}, {
    mitterInstanceReady: () => { /* your code here */ }
})

Do note that the access key object is not the direct JSON you get when you press on Copy to clipboard on the mitter.io panel. That follows a structure with an additional level of structuring and is used for file-based credential loading by other SDKs (for example, Java)

For the <mitter-api-url>, use the following value:

  • If you're using the cloud hosted solution, you can omit the mitterApiBaseUrl key in the config or explicitly set it to https://api.mitter.io

  • If you're running it as a docker container set it to http://localhost:<port> where the port is port forwarded by docker for 11902. To find out which port it is, run the following command docker port $(docker ps --filter expose=11901-11903/tcp --format "")

Once you have the mitter.io object, most of the operations are similar to using the web package. For instance, a common use-case is to get tokens for a user in your application.

Implementing a Token Server

An example project for this is located on our public gitlab repository

In our getting started sections we hard-coded our user authentication tokens directly in our code. It goes without saying that it is not a great practice for production applications. To implement a token service, what we first need is a repository of our users and optionally some mapping of their credentials. For this example, we'll use a simple JS dictionary:

const Users = {
  'user-0001': {
    name: 'John Doe',
    credentials: {
      username: 'john',
      password: 'password'
    }
  },

  'user-0002': {
    name: 'BoJack Horseman',
    credentials: {
      username: 'bojack',
      password: 'passphrase'
    }
  }
}

What we also need the backend to do is create the user in mitter.io if it doesn't exist. To make this resilient, we'll model this around get-or-create semantics. What our service will have is:

  1. Have a /login endpoint that accepts a request with parameters {username: '', password: ''} and authenticates it against the store.

  2. If a user is authenticated, it creates a user in mitter.io. If the request to create a user fails due to the user already existing, we will ignore the error. Any other error will be reported in our request as a 500.

  3. We will then fetch a user token for the user and return this to the front-end along with the mitter.io user id we got from the previous step.

index.js
const userAuthClient = mitter.clients().userAuth()
const userClient = mitter.clients().users()

router.post('/login', async function(req, res, next) {
  const { username, password } = req.body;

  const userFound = Object.keys(Users).find((userId) => {              // [1]
    const { username: targetUsername, password: targetPassword } =
        Users[userId].credentials;

    return username === targetUsername && password === targetPassword;
  })

  if (userFound) {
    const createUser = userClient.createUser({                         // [2]
      userId: userFound,
      userLocators: [],
      systemUser: false,
      screenName: {
        screenName: Users[userFound].name
      }
    })
    .catch((e) => {
      if (!(e.response.status === 409 &&
            e.response.data.errorCode === 'duplicate_entity')) {        // [3]
        throw e
      }
    })

    createUser.then(() => userAuthClient.getUserToken(userFound))        // [4]
        .then(token => {
          loginSuccessfulResponse(res, userFound, token.userToken.signedToken)
        })
        .catch(e => {
          console.error('Error executing request, setting 500', e)
          res.sendStatus(500)
        })
  } else {
    res.sendStatus(401);
    return Promise.resolve();
  }
});

An explanation of the code above:

  1. We first iterate over our user mapping to check if a user with the given credentials exists. In a production application, this would connect to your database, ldap or any other user storage you might be using.

  2. If the user is found, then we create a user with the same id in mitter.io. We don't have to specify a user id, or use the same user id at all, but usually having the same user id makes things simple. It is recommended that you map your domain ids directly as mitter.io ids or map them with some prefix. If a user is not found, we simply return a 401 saying that the user was not authenticated.

  3. The create user request might fail in case the user was already created. Whenever any operation fails due to a unique-constraint violation, mitter.io will return a 409 CONFLICT HTTP status and the response body will have a field errorCode set to duplicate_entity. Refer to the platform reference for a complete list of error scenarios and their respective error codes and HTTP status codes.

  4. If we haven't encountered an error so far, it means that a user with the same id as was mapped to the username provided to our token server as a request exists in mitter.io and the provided credentials have matched. We then use the userAuthClient to fetch a user access token for the user and then send a successful login response to the frontend. The loginSuccessfulResponse could be any function that combines the the user information, the mitter.io user information and any additional data and passes on this data to the fronted. An example would look like:


function loginSuccessfulResponse(res, userId, userToken) {
  res.status(200).send({
    mitterUserId: userId,
    mitterUserAuthorization: userToken
  })
}

Last updated