Skip to main content

Quick Start

note

This document assumes installation is complete, and you're using the async/await API with a modern TS/JS compiler.

Connecting to Maglev Infrastructure#

Step one is to create a Maglev instance. In this case we'll use the demo Relay. You'll want to deploy your own relay if you need an SLA. Keep in mind that Maglev is still in Alpha though.

import { Maglev } from '@wlabs/maglev';
const maglev = new Maglev({
relay: { url: 'wss://relay.maglev.wlabs.dev' },
wrtc: { iceServers: [{ urls: 'stun:stun3.l.google.com' }] },
});

Connections are established lazily, so this doesn't actually do anything on it's own. If you need to wait for the Maglev object to connection to the relay, you can await maglev.reachable.whenEqual(true).

Talking to Another Node#

Talking to another node is very easy. All you need is that node's unique ID (or a matching Node Tag result), and Maglev handles all the messy relaying and direct-connection negotiation.

const nodeRef = maglev.nodes.get('OTHER-NODE-ID');

Connections#

Connections are managed by the Maglev client itself. When you get a reference to another node, the Maglev client will immediately begin connection negotiation and authentication with that node. Traffic can be sent over the first connection established (which is almost always a relayed connection through a Maglev relay). In the background the client will attempt to 'upgrade' the connection to a direct-connect. The switch over is transparent, apart from the likely large reduction in latency. This works even in browsers.

Defining an RPC#

Maglev RPCs are defined in proto3 format. Take a moment to familiarize yourself with it's syntax if it's new to you.

Confused about what an RPC is, what this syntax means, and why it's useful? We have an entire page on that here. Think of them like strongly typed REST endpoint definitions. Except better in every possible way.

Create a greeter.proto file somewhere. We recommend keeping all .proto files in a directory called protos at the root of your project.

syntax = "proto3";
service GreeterService {
// A very simple unary RPC called "SayHello".
rpc SayHello(HelloRequest) returns (HelloResponse);
}
message HelloRequest {
string your_name = 1;
}
message HelloResponse {
string response = 1;
int32 visitor_number = 2;
}

Compiling Protobuf Files#

Proto syntax isn't understood by a JavaScript interpreter, so a compiler is used to translate the proto definition into something that the Maglev core library can use at runtime. It also produces TypeScript type definitions for strong end-to-end typing. This is a good command to add to your package.json build scripts.

# Use the Maglev CLI to compile proto definitions.
yarn maglev gen ts --input protos/**/*.proto --output src/gen

Hosting an RPC Endpoint#

Maglev does not disambiguate "server" and "client" instances. Any node on the Maglev network can both serve and/or call RPC endpoints. This is made possible by not having to expose 'ports' like you would on a traditional TCP server. Lets setup a basic RPC handler for our "Greeter Service" defined above.

import { Maglev } from '@wlabs/maglev';
import { GreeterService } from './gen/proto_gen';
const maglev = new Maglev({
relay: { url: 'wss://relay.maglev.wlabs.dev' },
wrtc: { iceServers: [{ urls: 'stun:stun3.l.google.com' }] },
});
// Keep track of how many visitors we have had.
let visitorNum = 1;
GreeterService.registerHandler(maglev, {
// Note that in JS/TS the names are translated to camelCase.
sayHello: async (req, _ctx) => {
return {
response: `Hello, ${req.yourName}!`,
visitorNumber: visitorNum++,
};
},
});

Calling an RPC Endpoint#

The other side of calling a Maglev RPC endpoint is even easier, thanks to the generated code the Maglev CLI produces.

import { Maglev } from '@wlabs/maglev';
import { GreeterService } from './gen/proto_gen';
const maglev = new Maglev({
relay: { url: 'wss://relay.maglev.wlabs.dev' },
wrtc: { iceServers: [{ urls: 'stun:stun3.l.google.com' }] },
});
const nodeRef = maglev.nodes.get('OTHER-NODE-ID');
// Synthesize a client instance of our service, where each method is already
// bound to the node.
const client = GreeterService.createClient(nodeRef);
// Then we can call RPCs on the client directly. Unary RPCs return a promise of
// the result, so we await until the RPC completes. This is the first time we've
// had to await on the client side, other async logic is managed for us.
(async () => {
const { response, visitorNumber } = await client.sayHello({
// Little Bobby Tables, we call him.
yourName: "Robert'); DROP TABLE students;--",
});
console.log(`Response: ${response}, Number: ${visitorNumber}`);
})();