Getting Started with Wampy
Wampy is a community-maintained WAMP client for JavaScript that runs in both the browser and Node.js. By the end of this tutorial you will have a small Node.js program that connects to a Bondy router, serves a procedure and calls it, and publishes and receives an event, using nothing but the public Wampy API.
Community client
Wampy isn't maintained by the Bondy team. It's one of several JavaScript WAMP clients — see WAMP Client Libraries for the full list. Everything below is standard WAMP, so the same steps apply, in shape, to any of them.
Before you start
You need:
- Node.js (a recent LTS release) and npm.
- A running Bondy node reachable over WebSocket on
127.0.0.1:18080(Bondy's defaultwamp_wslistener). If you don't have one yet, follow Get Bondy first. - A realm your client can attach to. This tutorial uses anonymous authentication against a realm named
com.example.realm. The realm must permit theanonymoususer and grantwamp.call,wamp.register,wamp.subscribeandwamp.publish. See Realms and the Realm reference if you need to create one.
Everything below is plain JavaScript, typed into a node shell one block at a time — copy each block in order and it will work as shown. Node's REPL supports top-level await, so there's no wrapping async function to write.
Step 1 — Install Wampy
mkdir bondy-wampy-quickstart && cd bondy-wampy-quickstart
npm init -y
npm install wampy wsWampy needs a WebSocket implementation handed to it explicitly when it runs outside a browser — that's what ws is for.
Step 2 — Connect to Bondy
Start a Node shell in the same directory:
nodeThe Wampy constructor takes a URL and an options object describing the realm and, for Node.js, which WebSocket module to use:
const { Wampy } = require('wampy');
const WebSocket = require('ws');
const wampy = new Wampy('ws://127.0.0.1:18080/ws', {
realm: 'com.example.realm',
ws: WebSocket
});
await wampy.connect();You should see: the awaited promise resolves with the session's WELCOME details (roles, features, and — if you authenticated with more than anonymous — your
authid/authrole). Confirm the session is up withwampy.getSessionId(), which returns a positive integer once connected andnullbefore. Wampy connects anonymously by default — you only passauthid/authmethodswhen a realm requires real credentials, which is why this call needed nothing beyond the realm name.
Keep this shell open — wampy is reused in every step that follows. Whenever you're done, close it with:
await wampy.disconnect();Step 3 — Serve a procedure and call it
A Callee serves a procedure by registering a URI with a handler: a function that receives a single object argument — argsList, argsDict, and details — and returns the reply. Register an echo procedure:
await wampy.register('com.example.echo', (data) => {
return { argsList: data.argsList, argsDict: data.argsDict };
});You should see: the promise resolves to
{ topic: 'com.example.echo', requestId: ..., registrationId: ... }. The handler now runs every time anyone on this realm callscom.example.echo, including this same connection.
Now call it, as a Caller, from the same connection. A call's payload is a single object: argsList for positional arguments, argsDict for keyword arguments — both together if you need both:
const result = await wampy.call('com.example.echo', {
argsList: ['hello'],
argsDict: { lang: 'en' }
});You should see:
result.argsListis['hello']andresult.argsDictis{ lang: 'en' }— exactly what the handler returned.call()resolves once the reply arrives (or rejects if the call times out) and hands you back exactly what the callee returned.
A call to a URI nobody has registered fails cleanly rather than hanging:
try {
await wampy.call('com.example.does_not_exist');
} catch (error) {
error.errorUri; // 'wamp.error.no_such_procedure'
}You should see: the
catchblock runs, anderror.errorUriis'wamp.error.no_such_procedure'. Every rejected call carries aCallErrorwitherrorUri,details,argsListandargsDict— the same shape as a successful result, but for the error case.
Step 4 — Publish and subscribe
A Subscriber registers a handler against a topic; a Publisher sends events to that topic. Both roles work the same way whether they're the same connection or two different ones — here, again, one connection plays both.
Subscribe first, so there's a listener in place before anything is published:
await wampy.subscribe('com.example.ticks', (event) => {
console.log('tick:', event.argsList);
});Now publish an event to that topic. By default Bondy does not deliver an event to the session that published it, so this publication sets exclude_me: false:
await wampy.publish('com.example.ticks', { argsList: ['tock'] }, { exclude_me: false });You should see:
publish()resolves to{ topic: 'com.example.ticks', requestId: ..., publicationId: ... }, and — asynchronously, as the event comes back through the router — the subscriber's handler logstick: [ 'tock' ]. Publishing is fire-and-forget by default: the promise resolves once the event is on the wire, without waiting for any subscriber to receive it.
What you built
In one connection you exercised all four WAMP roles: Callee and Caller by serving and calling com.example.echo, and Publisher and Subscriber by publishing and receiving com.example.ticks. Along the way you saw the shape every Wampy call shares: a payload (or reply) is { argsList, argsDict }, and a rejected call carries that same shape on a CallError.
Next steps
- Read Routed RPC and Publish/Subscribe for the protocol-level mechanics behind what you just did — pattern-based registrations and subscriptions, call cancellation, retained events, and more.
- If your client needs to authenticate with real credentials rather than anonymously, see the Security concept page and Wampy's own
authid/authmethods/onChallengeoptions in its README. - For a complete worked application built the same way — RPC and Pub/Sub wiring a web app to backend services — see the Marketplace tutorial.