Javascript

Last updated 3 months ago

The mitter.io web SDKs are a minimalist SDK that allows you to consume mitter.io services with just a few dependencies.

Setup

NOTE The following setup is for a web-setup only. Refer to the page on node.js for information on using the SDKs with node.js

To setup the mitter-web SDK using yarn (or npm), simply add the @mitter-io/web package as a dependency. If you want to use the base models used by mitter, you can also add @mitter-io/models package, although that is optional.

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

Or, with npm:

npm install --save @mitter-io/web @mitter-io/models

Typescript users do not need to add typings for these packages separately, they are both bundled in with the application itself. For those working on an IDE supporting typescript definitions (like Visual Studio Code), auto-complete and type checking will be enabled even for JavaScript users.

To be able to make API calls, you can either use the bundled API clients provided with the package or use an interceptor along with an HTTP library of your choice. Mitter npm packages ship with the interceptors for fetch and axios. They have been tested to work with both, the browser-bundled fetch and the whatwg-fetch polyfill. If you do want to use the mitter clients itself, you will need to add axios as a dependency

yarn add axios

Or, with npm:

npm install --save axios

Usage and user authorization

To begin using the mitter.io sdk, you need to provide it with an application id and a user authorization:

app.js
import { Mitter } from '@mitter-io/web'
const mitter = Mitter.forWeb(
'fb70ff76-ea33-4bb0-bd59-90853f103202' /* provide your application id here */
)

To provide it a user authorization, at any point in the application lifecycle, you can setup:

mitter.setUserAuthorization('eyJhbGciOiJIUz ... ')

The user authorization is usually fetched from your backend, which can verify your user's credentials and then fetch a token from mitter.io, federated authentication and/or from a hard-coded value fetched from the mitter.io dev panel (use this method only in dev environments for testing).

A sample workflow could be:

login.js
function onLogin(username, password) {
fetch('http://mybackend.example.com/login', {
method: 'POST',
data: {
user: username,
password: password
}
)
.then(response => response.json())
.then(auth => mitter.setUserAuthorization(auth.mitterAuthorization))
}

This call can be made multiple times, in case of a user logging out, or refreshing their authorization. The SDK will automatically reset all pipelines and credentials everywhere. Do note that this API is idempotent and multiple calls with the same user authorization will not have any effect (i.e. any pipelines that are setup will not be reset)

NOTE Setting a user authorization does not check if the provided token was valid. If you wish to verify the token, you can make a call to /v1/users/me using either the provided API clients (documented below) or using a fetch interceptor

Making API calls

To make API calls to the mitter service, there are two ways to do this: either using the inbuilt clients, or making HTTP calls using fetch/axios and enabling an interceptor.

Using the mitter clients

To access the mitter API clients, you can fetch a client set using the mitter.clients() call. On the returned object, then get the relevant client as you desire:

const userClient = mitter.clients().users()
const userAuthClient = mitter.clients().userAuth()
const channelsClient = mitter.clients().channels()
const messagesClient = mitter.clients().messages()

Every method on this client maps directly to a mitter.io API that follows exactly the same shape for request/response as the API. Since these are low-level clients, any errors are returned AS-IS with the status and data in the payload.

For example, to create a new channel with a specified id, but to ignore an error in case of a duplicate, one could:

create-channel.js
channelsClient.newChannel({
channelId: 'johns-personal-channel',
defaultRuleSet: 'io.mitter.ruleset.chats.GroupChat',
timelineEvents: [],
participation: [],
systemChannel: false,
entityMetadata: {},
entityProfile: {}
})
.then(channelId => personalChannelCreated(channelId)
.catch(error => {
if (error.status === 409 && error.data.error_code === 'duplicate_entity') {
// do nothing, or one can call personalChannelCreated() here again
} else {
errorCreatingPersonalChannel();
}
})

If the above calls seems too wordy, you can use the provided models that adds in defaults for most of the items (this requires the @mitter-io/models package):

import { Channel, StandardRuleSetNames } from '@mitter-io/models'
channelsClient.newChannel(new Channel(
'johns-personal-channel',
StandardRuleSetNames.GroupChat,
[ mitter.me() ]
))

NOTE The mitter.me returns a user-like object that can be used in any place in API calls which require a user identifier.

Using fetch

To directly make API calls using fetch one can use the fetch-interceptor

mitter.enableFetchInterceptor()
fetch('https://api.mitter.io/v1/users/me')
.then(response => response.json())
.then(user => console.log('Hello, ' + user.userId))

The fetch interceptor, unlike the axios interceptor, intercepts all fetch requests globally. This is due to the fact that fetch as an object itself happens to be declared in a global scope. The interceptor simply adds the user authorization to your mitter.io api requests (as headers) and will only intercept requests that are made to either https://api.mitter.io or https://api.staging.mitter.io. If you do not wish to have a global interceptor, a simpler solution would be to add the header yourself:

fetch('https://api.mitter.io/v1/users/me', {
headers: {
'X-Mitter-Issued-User-Authorization': 'eyJhbGciOiJIUz ... '
}
)

If you do not want to keep a copy of the user authorization and just use the one mitter has with itself, you can use:

mitter.getUserAuthorization().then(userAuthorization => {
fetch('https://api.mitter.io/v1/users/me', {
headers: {
'X-Mitter-Issued-User-Authorization': userAuthorization
}
})
})

The interceptor can be disabled at any time using

mitter.disableUserAuthorization()

Using axios

If you are using axios (and we recommend using it for making mitter.io API calls), then you can enable an interceptor on a specific axios instance

import axios from 'axios'
const myAxios = axios.create({ .. config .. })
mitter.enableAxiosInterceptor(myAxios)
// If you are not using axios instances, but a global interceptor,
// you can still enable an interceptor globally on the axios global
// instance
mitter.enableAxiosInterceptor(axios)

Similar to the fetch interceptor, even the axios interceptor will intercept only the requests that start with https://api.mitter.io and https://staging.api.mitter.io.

Consuming incoming messages

When you setup the basic mitter object, it automatically creates relevant pipelines to process incoming payloads. Mitter.io pushes different payloads to users on relevant activity. A few examples are:

  1. When the user receives a new message

  2. When the user is made part of a new channel

  3. When there is any type of stream data in a channel that the user is a part of.

To subscribe to these messages, you can use the subscribeToPayload function:

mitter.subscribeToPayload(payload => {
if (payload['@type'] === 'NewMessagePayload') {
if (!(payload.channelId.identifier in userData['messages'])) {
userData['messages'][payload.channelId.identifier] = []
}
userData['messages'][payload.channelId.identifier].push(payload.message)
}
})

The package @mitter-io/models also bundles with a number of type-identifying functions that can be used instead of custom matching. Using that, the same code would now look like:

import { isNewMessagePayload } from '@mitter-io/models'
mitter.subscriberToPayload(payload => {
if (isNewMessagePayload(payload)) {
...
}
})

Do note that these subscriptions stay active even when user-authorization switches. If your application switches between two users without reloading the page, the subscribed callbacks will still be called. To avoid that, use the clearSubscriptions method:

mitter.clearSubscriptions()