npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

phoenix-websocket

v2.0.5

Published

A custom implementation of the Channels API for communicating with a Phoenix backend via WebSockets.

Downloads

1,093

Readme

phoenix-websocket

NPM version NPM

A custom implementation of the Channels API for communicating with an Elixir/Phoenix backend via WebSockets.

Full Documentation: https://inkarnaterpg.github.io/phoenix-websocket/

How this differs from the phoenix package

  • Built with async/await.
  • Allows defining multiple message handler functions that are called depending on the content of received messages.
  • Opinionated in assuming the server will send pre-defined messages with variable payloads.
  • Only supports the WebSocket protocol, does not have a LongPoll fallback.
  • Does not include built in Presence support.
  • Configurable timeout which can help avoid flooding the dev console with connection errors.
  • Built with TypeScript and includes definitions without a second dependency.

Install

npm install phoenix-websocket

Usage

Setup and Configuration

First create a PhoenixWebsocket instance, normally you should only need one instance per backend endpoint, which can then be shared throughout your application.

const socket = new PhoenixWebsocket("wss://example.io/channel-endpoint")

You can optionally pass in a params object to be included with the connection request.

const socket = new PhoenixWebsocket("wss://example.io/channel-endpoint", { token: "auth-token" })

You can also optionally pass in a timeout value (in milliseconds), which can be helpful if you need a more or less aggressive connection retry, or if you want to avoid having connection errors flooding your development console while testing locally.

const socket = new PhoenixWebsocket(
    "wss://example.io/channel-endpoint",
    { token: "auth-token" },
    process.env.NODE_ENV === 'development' ? (1000*60*15) : (1000*30)
)

You may also wish to add callbacks for connected/disconnected events.

socket.onConnectedCallback = () => {
    // Do something when connected, such as updating the UI
}

socket.onDisconnectedCallback = () => {
     // Do something when disconnected, such as updating the UI
}

Connecting

You can initiate the connection to the server by calling .connect() on your PhoenixWebsocket instance.

socket.connect().then(() => {
    //Connected!
})

Once .connect() is called, this library will continue attempting to keep the connection alive, and will automatically try to reconnect after any disconnections, until .disconnect() is called.

Note: Awaiting the .connect() promise and setting a .onConnectedCallback listener are both ways to learn when the connection is successfully opened. The difference is that .onConnectedCallback will also be called anytime a reconnection happens, and so it is recommended to use this callback for any UI updates.

Subscribing to Topics

To send or receive messages, you'll first need to subscribe to a topic. The simplest way to do this is:

socket.subscribeToTopic("exampleTopic")

To include a payload object with the request, pass it in as the second parameter:

socket.subscribeToTopic("exampleTopic", {userId: "1"})

Calling .subscribeToTopic with one or two parameters will connect to the topic but will not include any way to handle messages or broadcasts received from the server. As such this method is only recommended when you intend to interact with the server purely in the Client -> Server direction. See the following section for instructions on handling server messages.

Note: Attempting to subscribe to the same topic twice will result in a warning being logged to the console, but otherwise no action will be taken and the original topic subscription will remain active. This differs from the behavior of the phoenix package which will rejoin a channel on subsequent subscription attempts. To rejoin a channel with this library, you must first explicitly leave it with .unsubscribeToTopic() before re-joining.

Receiving Server Messages

To handle messages broadcast from the server, you can pass in callbacks to the .subscribeToTopic() function which are called when a message is received whose content matches one of the callbacks. This library currently makes the opinionated assumption that the messages you receive are predefined by the server, and any dynamic content is only included in the payload. In otherwords, messages are treated as event names while the payload can include any additional data for that event.

Callbacks are passed in as a single object containing key/value entries mapping a message to a callback. For example:

socket.subscribeToTopic("exampleTopic", {userId: "1"},
    {
        user_joined: (payload) => { /** User joined! */},
        user_left: (payload) => { /** User left! */},
    }
)

Handling Error Responses to Topic Join Requests

If the server responds to a join request with an 'error' status, the topic will not be added to your PhoenixWebsocket instance and the promise will be rejected with a PhoenixRespondedWithError error object. An example of handling a join error conditionally based on the message payload is:

socket.subscribeToTopic("exampleTopic", {userId: "1"}).catch((error) => {
    if (error instanceof PhoenixRespondedWithError) {
      if (error.reply.response.reason === "Invalid User") {
        // Do something
      }
    }
    else {
      console.error(error)
    }
  }
)

Sending Messages To The Server

To send a message to a subscribed topic, you can call .sendMessage() and optionally include a payload. To wait for and handle the server's response, you can await the promise returned from .sendMessage().

To send a message without a payload:

socket.sendMessage("exampleTopic", "notify_server")

To send a message with a payload:

socket.sendMessage("exampleTopic", "send_message", {message: "Hello!", attachments: []})

To handle a message response:

socket.sendMessage("exampleTopic", "query_users").then((reply) => {
    this.userList = [...reply.response.users]
})

Handling Errors With .sendMessage()

The promise returned by .sendMessage() will only resolve if the server responds with a successful ("ok") response. If the server returns an error response, then the promise will be rejected with a PhoenixRespondedWithError error. This error type contains the response body in case you need to implement further logic on error.

The .sendMessage() function may also throw either a PhoenixInvalidTopicError or a PhoenixInvalidStateError if you try to send a message to a topic you aren't subscribed to, or if the websocket is not currently connected. These types are exported and can be used to catch these library-specific errors and handle them. For ex:

try {
    socket.sendMessage("topicImNotSubscribedTo", "test")
}
catch (error) {
    if (error instanceof PhoenixInvalidTopicError || error instanceof PhoenixInvalidStateError) {
        // Do something
    }
    else {
        throw error
    }
}

Using in a Node Context

This library was primarily intended for use within a browser, so it relies on the browser WebSocket API. If you want to use this library in a node context, you will need to expose a node implementation of the WebSocket API, which can be done with the wspackage.

After installing ws, add the following to the top of your entrypoint:

import WebSocket from "ws"
(global as any).WebSocket ??= WebSocket

Maintainer

This library is maintained by Inkarnate.

In addition, this library is currently in use by us in a high-traffic, production environment. While we strive to keep this library up-to-date and bug free, there may be use cases we have not encountered with unknown bugs. As such, Issue reports and PRs are most welcome.