c0d3x42 / nats.ws

WebSocket NATS

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

NATS - Websocket Javascript Client for the Browser

A websocket client for the NATS messaging system.

License Travis branch Coverage Statusnpm npm

Installation

⚠️ If you have used a preview version of nats.ws, the API for message callbacks has changed. Previous versions of the API simply required a message argument. The current version of the API normalizes against v2 branches for nats.js and nats.ts. The message handler signature is (err: NatsError|null, m: Msg), where an error will be set if there was a problem. This change enables the opportunity to associate errors related to the subscription such as JSON decoding errors, and others.

** ⚠️ NATS.ws is a preview** you can get the current development version by:

npm install nats.ws@next

Nats.ws requires a nats-server with websocket support. The nats-server implementation only supports WSS, so you'll need some TLS certificates.

Basic Usage

nats.ws supports Promises, depending on the browser/runtime environment you can also use async-await constructs.

Copy the nats.js library from node_modules, and place it where you can reference it, then load the library:

<script src='./nats.js'></script>

In another script block, reference the 'nats' global:

const init = async function () {
// create a connection
  const nc = await nats.connect({ url: 'wss://localhost:8080', payload: nats.Payload.STRING })

  // simple publisher
  nc.publish('hello', 'nats')

  // simple subscriber, if the message has a reply subject
  // send a reply
  const sub = await nc.subscribe('help', (err, msg) => {
    if (err) {
      // handle error
    }
    else if (msg.reply) {
      nc.publish(msg.reply, `I can help ${msg.data}`)
    }
  })

  // unsubscribe
  sub.unsubscribe()

  // request data - requests only receive one message
  // to receive multiple messages, create a subscription
  const msg = await nc.request('help', 1000, 'nats request')
  console.log(msg.data)

  // publishing a request, is similar to publishing. You must have
  // a subscription ready to handle the reply subject. Requests
  // sent this way can get multiple replies
  nc.publish('help', '', 'replysubject')


  // close the connection
  nc.close()

}

init()

Wildcard Subscriptions

// the `*` matches any string in the subject token
const sub = await nc.subscribe('help.*.system', (_, msg) => {
    if (msg.reply) {
        nc.publish(msg.reply, `I can help ${msg.data}`)
    }
})
sub.unsubscribe()

const sub2 = await nc.subscribe('help.me.*', (_, msg) => {
  if (msg.reply) {
    nc.publish(msg.reply, `I can help ${msg.data}`)
  }
})

// the `>` matches any tokens, can only be at the last token
const sub3 = await nc.subscribe('help.>', (_, msg) => {
  if (msg.reply) {
    nc.publish(msg.reply, `I can help ${msg.data}`)
  }
})

Queue Groups

// All subscriptions with the same queue name form a queue group.
// The server will select a single subscriber in each queue group
// matching the subscription to receive the message.
const qsub = await nc.subscribe('urgent.help', (_, msg) => {
  if (msg.reply) {
     nc.publish(msg.reply, `I can help ${msg.data}`)
  }
}, {queue: "urgent"})

Authentication

// if the websocket server requires authentication, 
// provide it in the URL. NATS credentials are specified
// in the `user`, `pass` or `token` options in the NatsConnectionOptions

let nc = nats.connect({url: "wss://wsuser:wsuserpass@localhost:8080", user: "me", pass: "secret"})
let nc1 = nats.connect({url: "wss://localhost:8080", user: "jenny", token: "867-5309"})
let nc3 = nats.connect({url: "wss://localhost:8080", token: "t0pS3cret!"})

Advanced Usage

Flush

// flush does a round trip to the server. When it
// returns the the server saw it
await nc.flush()

// or with a custom callback
nc.flush(()=>{
  console.log('sent!')
});

// or publish a few things, and wait for the flush
await nc.publish('foo').publish('bar').flush()

Auto unsubscribe

// subscriptions can auto unsubscribe after a certain number of messages
const sub = await nc.subscribe('foo', ()=> {}, {max:10})

// the number can be changed or set afterwards
// if the number is less than the number of received
// messages it cancels immediately
let next = sub.getReceived() + 1
sub.unsubscribe(next)

Timeout Subscriptions

// subscriptions can specify attach a timeout
// timeout will clear with first message
const sub = await nc.subscribe('foo', ()=> {})
sub.setTimeout(300, ()=> {
  console.log('no messages received')
})

// if 10 messages are not received, timeout fires
const sub = await nc.subscribe('foo', ()=> {}, {max:10})
sub.setTimeout(300, ()=> {
    console.log(`got ${sub.getReceived()} messages. Was expecting 10`)
})

// timeout can be cancelled
sub.clearTimeout()

Error Handling

// when server returns an error, you are notified asynchronously
nc.addEventListener('error', (ex)=> {
  console.log('server sent error: ', ex)
});

// when disconnected from the server, the 'close' event is sent
nc.addEventListener('close', ()=> {
  console.log('the connection closed')
})

NATS in the Browser

Here's an example (check examples/chat.html for the latest version) of a chat application using ws-nats.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ws-nats chat</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
          crossorigin="anonymous">
</head>
<!-- when the browser exits, we publish a message -->
<body onunload="exiting()">

<!-- a form for entering messages -->
<div class="container">
    <h1>ws-nats chat</h1>
    <input type="text" class="form-control" id="data" placeholder="Message" autocomplete="off"><br/>
    <button id="send" onclick="send()" class="btn btn-primary">Send</button>
</div>
<br/>

<!-- a place to record messages -->
<div id="chats" class="container"></div>

<!-- load the nats library -->
<script src="../nats.js"></script>

<script>
const Payload = nats.Payload
const me = Date.now()

// create a connection, and register listeners
const init = async function () {
  // if the connection doesn't resolve, an exception is thrown
  // a real app would allow configuring the URL
  const conn = await nats.connect({ url: '{{WSURL}}', payload: Payload.JSON })

  // handle errors sent by the gnatsd - permissions errors, etc.
  conn.addEventListener('error', (ex) => {
    addEntry(`Received error from NATS: ${ex}`)
  })

  // handle connection to the server is closed - should disable the ui
  conn.addEventListener('close', () => {
    addEntry('NATS connection closed')
  })

  // the chat application listens for messages sent under the subject 'chat'
  conn.subscribe('chat', (_, msg) => {
    addEntry(msg.data.id === me ? `(me): ${msg.data.m}` : `(${msg.data.id}): ${msg.data.m}`)
  })

  // when a new browser joins, the joining browser publishes an 'enter' message
  conn.subscribe('enter', (_, msg) => {
    if (msg.data.id !== me) {
      addEntry(`${msg.data.id} entered.`)
    }
  })

  // when a browser closes, the leaving browser publishes an 'exit' message
  conn.subscribe('exit', (_, msg) => {
    if (msg.data.id !== me) {
      addEntry(`${msg.data.id} exited.`)
    }
  })

  // we connected, and we publish our enter message
  conn.publish('enter', { id: me })
  return conn
}

init().then(conn => {
  window.nc = conn
}).catch(ex => {
  addEntry(`Error connecting to NATS: ${ex}`)
})

// this is the input field
let input = document.getElementById('data')

// add a listener to detect edits. If they hit Enter, we publish it
input.addEventListener('keyup', (e) => {
  if (e.key === 'Enter') {
    document.getElementById('send').click()
  } else {
    e.preventDefault()
  }
})

// send a message if user typed one
function send () {
  input = document.getElementById('data')
  const m = input.value
  if (m !== '' && window.nc) {
    window.nc.publish('chat', { id: me, m: m })
    input.value = ''
  }
  return false
}

// send the exit message
function exiting () {
  if (window.nc) {
    window.nc.publish('exit', { id: me })
  }
}

// add an entry to the document
function addEntry (s) {
  const p = document.createElement('pre')
  p.appendChild(document.createTextNode(s))
  document.getElementById('chats').appendChild(p)
}
</script>
</body>
</html>

About

WebSocket NATS

License:Apache License 2.0


Languages

Language:TypeScript 99.4%Language:JavaScript 0.6%