Layer Web SDK

The Layer Web SDK is a JavaScript library for adding chat services to your web application. For detailed documentation, tutorials and guides please visit our Web SDK documentation.
Supported Browsers
- IE 11 and Edge
- Safari 7 and up
- Chrome 42 and up
- Firefox 40 and up
Older versions of Chrome and Firefox will likely work.
Installation
All examples below assume your using the CDN installation method; adapting instructions to other methods should be straightforward.
CDN
Simplest approach to install the Web SDK is to add the following script tag:
<script src='//cdn.layer.com/sdk/2.0/layer-websdk.min.js'></script>
- For stricter code control, use
//cdn.layer.com/sdk/2.0.0/layer-websdk.min.js
instead.
All classes can then be accessed via the layer
namespace:
var client = new layer.Client({
appId: LAYER_APP_ID
});
NPM
npm install layer-websdk --save
All classes can then be accessed via the layer module:
var layer = require('layer-websdk');
var client = new layer.Client({
appId: LAYER_APP_ID
});
From source
Download the latest SDK release Source code archive, extract the files and run the following commands from the extracted project folder:
> npm install
> grunt build
A build/
folder will be generated that contains client.min.js
file.
Other build commands:
grunt debug
: Generates build/client.debug.js
which provides source-mapped files if you need to step through the Web SDK.grunt docs
: Generates docs/index.html
which you can open to view the API documentation.grunt test
: Run the unit tests
About this README
This README contains everything you need to get started. But is NOT an exhaustive source of documentation. For the full documentation, or for any topics that are missing here, go to the Web SDK documentation.
Getting Started
To start using Layer's Web SDK, you need to
- Initialize a layer.Client
- Setup event handlers
- Connect to Layer
1. Initializing a Client
Initialize a client with your Layer Application ID which can be found in the Developer Dashboard.
var client = new layer.Client({
appId: LAYER_APP_ID
});
2. Setup Event handlers
The client requires two event handlers to get started:
challenge
: Performs an authentication challenge requiring your application to provide an Identity Tokenready
: The client is authenticated and is ready for your UI
client.on('challenge', function(evt) {
getIdentityToken(evt.nonce, function(identityToken) {
evt.callback(identityToken);
})
});
client.on('ready', function() {
renderMyUI(client);
});
The following shorthand is also acceptable:
var client = new layer.Client({
appId: LAYER_APP_ID,
challenge: function(evt) {
getIdentityToken(evt.nonce, function(identityToken) {
evt.callback(identityToken);
})
},
ready: function() {
renderMyUI(client);
}
});
getIdentityToken()
is a method you provide for accessing your Identity Provider, and is covered in the Authentication section.
3. Connect to Layer
To start the client, you will call the connect
method with the userId of the user you are authenticating as:
client.connect('Frodo_the_Dodo');
This call will start the authentication process. If using the layer.Client.isTrustedDevice
property, it may try to restore your last Session Token and skip authentication.
Accessing Data
Data is accessed via layer.Query instances. Some notes about queries before going into details:
- The
update()
method lets you change properties of the Query and then fetches updated results. - Results of Queries will update as new Conversations or Messages are created locally or remotely.
- Results are accessed via the query's
data
property. - Events are triggered whenever there is a change to the query's results.
- Queries can be used to retrieve the following types of data:
- Conversations: model is
layer.Query.Conversation
- Messages: model is
layer.Query.Message
- Announcements: model is
layer.Query.Announcement
Conversation Query
var query = client.createQuery({
model: layer.Query.Conversation
});
query.on('change', function(evt) {
var conversations = query.data;
myConversationListRenderer(conversations);
});
At this time, no predicates are supported for filtering results.
Conversations can be sorted by lastMessage.sentAt
or createdAt
; the default sort is createdAt
; only Descending sorts are supported at this time.
var query = client.createQuery({
model: layer.Query.Conversation,
sortBy: [{'lastMessage.sentAt': 'desc'}]
});
Improved support for sorting and predicates will ship once the Layer's Client APIs adds a Query API.
Message Query
var query = client.createQuery({
model: layer.Query.Message,
predicate: 'conversation.id = "layer:///conversations/uuid"'
});
query.on('change', function(evt) {
var messages = query.data;
myMessageListRenderer(messages);
});
The Message query only accepts a predicate/filter of the form:
'predicate': 'conversation.id = "' + myConversation.id + '"'
Notes:
- Single and double quotes are both supported for this predicate.
- No sorting options are currently supported on Message queries. They are always sorted by Message
position
property in descending order.
Query Update
Any time you want to change properties of your query, you can call the update()
method:
Page the Query:
query.update({
paginationWindow: query.paginationWindow + 50
});
Paging the query will fire a request to the server and append additional data to the results. In this case, the next 50 results will be downloaded.
Change the Query:
query.update({
predicate: 'conversation.id = "' + conversation.id + '"'
});
Changing the predicate:
- clears the query's data
- resets its paginationWindow
- triggers a reset event
- fires a request to the server
- sets the data to the results from the server
- triggers a data event
A common pattern is to use a single layer.Query
bound to your MessageListView, changing the predicate as the user moves from Conversation to Conversation.
An alternate pattern of creating a new layer.Query
each time the user navigates to a new Conversation also works; however, you must destroy each query when done with it:
query.destroy();
The Query Builder
There is a layer.QueryBuilder
class that simplifies assembling of Queries:
var builder = QueryBuilder
.messages()
.forConversation(conversationOne.id)
.paginationWindow(50);
var query = client.createQuery(builder);
query.update(builder.forConversation(conversationTwo.id));
The QueryBuilder may suit some developer styles better.
Query Results
Any change to the Query results are announced via a change
event. The simplest pattern for using a change
event is to grab the query's data:
query.on('change', function(evt) {
renderCurrentMessages(query.data);
});
To get more granular details about the different types of change
events and how to access just the data within the results that have changed, see the Query API.
Authentication
Authentication requires that:
- you have set up an Identity Service
- your client sends a request to your identity service to get an Identity Token
- your client provides that identity token to the Layer Web SDK
Some of these concepts are explained in more detail in the Authentication Guide; concepts specific to the Layer Web SDK are described here.
Assuming that you have an Identity Provider, your javascript application will need to provide some method (getIdentityToken()
in the example below) that gets the identity token.
function getIdentityToken(nonce, callback) {
xhr({
url: 'https://myservice.com/identity',
method: 'POST',
data: {
nonce: nonce
}
}, function(result) {
callback(result.identity_token);
});
}
Presumably, before you try to initialize a Layer Client, the user has already logged into your application, and you already have a session validating who this user is for your server. Your server just needs to take this nonce and create and Identity Token.
The full sequence may look like:
var client = new layer.Client({
appId: LAYER_APP_ID,
isTrustedDevice: false
});
client.on('challenge', function(evt) {
getIdentityToken(evt.nonce, function(identityToken) {
evt.callback(identityToken);
})
});
Note that evt.callback
must be called to provide the Layer Client with the identity token and procede with authorization.
Reusing the previous Authorization?
If the client receives the isTrustedDevice
property, then it will store your user's last Session Token in localStorage.
When reloading a web page, the framework will try to restore the last session. However, it will only do this if:
isTrustedDevice
is true- A
userId
property is provided to the connect()
method - The
userId
property matches the userId of the last user to login
var client = new layer.Client({
appId: LAYER_APP_ID,
isTrustedDevice: true
});
client.connect('Frodo_the_Dodo');
- If there is no session cached for
Frodo_the_Dodo
, then your challenge
event handler will be called. - If there is a session but its expired, then your
challenge
event handler will be called. - If there is a session and its valid, then your
ready
event handler will be called.
Creating a Conversation
var conversation = client.createConversation({
participants: ['Frodo_the_Dodo','Samwise_the_Brave'],
distinct: false,
metadata: {
title: 'My conversation title'
}
});
The above Conversation will not be sent to the server until you send a message with it.
Creating and Sending a Message
var message = conversation.createMessage('Hello World').send();
var message = conversation.createMessage({
parts: [{
body: 'Hello World',
mimeType: 'text/plain'
})]
}).send({
title: 'New Message from the World',
text: 'The world Says "Hello World"; its a very self involved planet',
sound: 'ding.aiff'
});
Note that creation of a Message is separate from sending it. This allows you to add additional MessageParts to your message after creating it:
var message = conversation.createMessage('Hello World');
message.addPart({
body: 'Farewell World',
mimeType: 'text/plain'
});
message.send();
Also note that the send method takes an optional parameter for push notifications; without that parameter there will be no new message notification for mobile device users:
message.send({
title: 'New Message from World',
text: 'World Says "Hello World"; its a very self involved planet',
sound: 'ding.aiff'
});
The exact meaning of these fields, and how to use them will depend on the platform that is receiving them; please consult the IOS and Android docs for more information.
Retrieving a Conversation
A Conversation can be retrieved by ID using:
var conversation = client.getConversation('layer:///conversations/uuid');
A Conversation can be retrieved by ID from cache OR from the server if not cached using:
var conversation = client.getConversation('layer:///conversations/uuid', true);
If you need the Conversation's details to be loaded from the server, use the 'conversations:loaded' event to be notified when its ready:
var conversation = client.getConversation('layer:///conversations/uuid', true);
conversation.once('conversations:loaded', function() {
renderMyUI(conversation);
});
The conversations:loaded
event will be called immediately if its already loaded.
Retrieving a Message
A Message can be retrieved by ID using:
var Message = client.getMessage('layer:///messages/uuid');
A Message can be retrieved by ID from cache OR from the server if not cached using:
var Message = client.getMessage("layer:///messages/uuid", true);
If you need the Message's details to be loaded from the server, use the "messages:loaded" event.
var message = client.getMessage("layer:///messages/uuid", true);
message.once("messages:loaded", function() {
renderMyUI(message);
});
The messages:loaded
event will be called immediately if its already loaded.
Persistence
Options exist to cache data in the browser. All of these capabilities are protected by the isTrustedDevice
property as well as the isPersistenceEnabled
property. If the device is not trusted, no data will be stored. If isPersistenceEnabled
is not enabled, no data will be stored in IndexedDB (Your user's Session Token will still be persisted to localStorage). But if you set both of these to true, then data can be saved.
var client = new layer.Client({
appId: LAYER_APP_ID,
isTrustedDevice: true,
isPersistenceEnabled: true
});
By default, isTrustedDevice
will enable the following types of data to be stored:
- Conversations
- Messages
- Announcements
- Server Requests that have not yet completed
- The Session Token
Change Events
The Layer Web SDK provides not just a set of APIs for letting you create, retrieve and control Conversations and Messages, but also a websocket connection that keeps your objects up-to-date and in sync with everyone else. Changes, regardless of whether they are generated by websocket data or changes you have made programatically, will all result in Events being triggered.
Commonly Used Events
There are a lot of available events, but only a few that are really necessary for building applications:
Event Arguments
Event callbacks are called with an event
argument that is an instance of layer.LayerEvent. This contains a target
property identifying the object changed, along with any information about what changed. The target
allows an application to write:
client.on('conversation:change', function(event) {
var changedItem = event.target;
alert((changedItem.toString() + ' has changed');
});
Change events get some special handling; any event name that matches 'xxxx:change' will come with a changes
property containing an array of properties that have changed:
[{
oldValue: 5,
newValue: 10,
property: 'unreadCount'
}]
Change events also come with a getChangesFor
method for extracting all changes to a specific property:
client.on('conversation:change', function(event) {
var changedItem = event.target;
var participantChanges = event.getChangesFor('participants');
var metadataChanges = event.getChangesFor('metadata');
participantChanges.forEach(function(change) {
alert(changedItem.toString() + ' old value is ' + change.oldValue +
'; new value is ' + change.newValue);
});
metadataChanges.forEach(function(change) {
alert(changedItem.toString() + ' old value is ' + change.oldValue +
'; new value is ' + change.newValue);
});
}, this);
The Event system is built on top of the backbone.js events. This accepts:
object.on(eventName, callback, context);
object.on('eventName1 eventName2', callback, context);
object.on({
eventName1: callback,
eventName2: callback,
eventName3: callback
}, context);
object.off(eventName, null, null);
object.off(null, callback, null);
object.off(null, null, context);
Websockets
The layer.Client manages a websocket connection to the server. If the connection is lost, it will attempt to reconnect when able, and will catchup on missed events. Typically, all management of this connection should be left to the Layer Web SDK. After an extended period of disconnect (> 1 day)
the Client may simply refire all Queries and refresh its data instead of trying to catch up on missed events.
Testing
To run unit tests use the following command:
npm test
Contributing
Layer Web SDK is an Open Source project maintained by Layer, inc. Feedback and contributions are always welcome and the maintainers try to process patches as quickly as possible. Feel free to open up a Pull Request or Issue on Github.
Contact
Layer Web SDK was developed in San Francisco by the Layer team. If you have any technical questions or concerns about this project feel free to reach out to Layer Support.