Creating applications¶
First of all, let's create an application, which will be the basis of our future
server API. The Application
is the core high-level abstraction
of the framework. Every application can consist of a number of interfaces,
each of which has its methods.
For this tutorial, we will create a pretty straightforward chat application
with two interfaces: auth
and chat
. The API will be quite plain with an
expected scenario like this:
- User registers a username and password if there is no "account" yet
- User logins with valid credentials
- Then the user can send messages to all connected users and receive messages from others
For the sake of simplicity we won't use a real database here for storing users' credentials or any cryptographic techniques for security, which you probably would implement in real projects, because our primary goal is to show the basic principles of using JSTP and don't get distracted by implementing a lot of other unrelated functionality.
Let's create a chatApp.js
file in our project directory:
'use strict'; const jstp = require('@metarhia/jstp'); const errors = { ERR_NOT_AUTHENTICATED: 1000, ERR_ALREADY_REGISTERED: 1001, ERR_INVALID_CREDENTIALS: 1002, ERR_ALREADY_AUTHENTICATED: 1003, }; const users = new Map(); const auth = { register: (connection, username, password, callback) => { if (users.has(username)) { callback(errors.ERR_ALREADY_REGISTERED); return; } users.set(username, password); callback(); }, login: (connection, username, password, callback) => { if (connection.session.has('user')) { callback(errors.ERR_ALREADY_AUTHENTICATED); return; } const pass = users.get(username); if (!pass || pass !== password) { callback(errors.ERR_INVALID_CREDENTIALS); return; } connection.session.set('user', username); callback(); }, }; const chat = { sendMessage: (connection, message, callback) => { const username = connection.session.get('user'); if (!username) { callback(errors.ERR_NOT_AUTHENTICATED); return; } const { server } = connection; for (const conn of server.getClients()) { if (conn !== connection) { conn.emitRemoteEvent('chat', 'message', [username, message]); } } callback(); }, }; const chatApp = new jstp.Application('chatApp', { auth, chat }); module.exports = chatApp;
This code may raise some questions, so let’s go over it to see how it works.
After requiring jstp
module, we created an object to map error names to their
codes and return them to client in case something went wrong.
const errors = { ERR_NOT_AUTHENTICATED: 1000, ERR_ALREADY_REGISTERED: 1001, ERR_INVALID_CREDENTIALS: 1002, ERR_ALREADY_AUTHENTICATED: 1003, };
Next, we created a Map
for users' credentials, which emulates a real
project database. Here we'll store user names and passwords, when they register.
const users = new Map();
After these preparatory steps, we are ready to implement two interfaces for
the chat application: auth
interface with the register
and login
methods and chat
with method sendMessage
. This example API is quite
simplified, and its implementation only shows the main purpose of methods,
so we won't examine them in detail.
Note that first argument of every method in interfaces is always
connection
and last is always callback with error first contract, so that error (ornull
if there is no error) and all the results will be returned in it.
There is one more thing you can be curious about -
connection.session
, which extends Map
class and thus
can be used to store the current session state independently of connection.
For example, in auth
interface's login
method after the user logged in,
we just set key 'user'
to username
value in current client session to check
later if the user has been authorized.
connection.session.set('user', username);
Further in chat
interface's sendMessage
method we access connection's
server
so that we can get all server's client connections. For each
of them, we send the given message and the username of the sender by emitting a
remote event in their connections (more about remote events):
const { server } = connection; for (const conn of server.getClients()) { if (conn !== connection) { conn.emitRemoteEvent('chat', 'message', [username, message]); } }
An alternative way to send a message for all other connected users in chat is to use
server.broadcast()
method:
const { server } = connection; server.broadcast('chat', 'message', username, message);
In this case, you should check in client event handler (more about this here) if the sender's username is not client's username because the server will send the event message to all of the connected clients and we don't want to receive our messages back.
After we've implemented interfaces, we are ready to create our chat application:
const chatApp = new jstp.Application('chatApp', { auth, chat });
JSTP supports multiple versions per server
application
, application name should contain version after '@' (e.g. app@1.0.0). As we don't specify the version in our app, the default1.0.0
is used.
So we've just created a simple application
with some basic
interfaces for our future server, which will be created at the next step.