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

next-ws

v1.1.1

Published

Add support for WebSockets in Next.js 13 app directory

Downloads

6,132

Readme

🤔 About

Next WS (next-ws) is an advanced Next.js plugin that seamlessly integrates WebSocket server capabilities directly into routes located in the app directory. With Next WS, you no longer require a separate server for WebSocket functionality.

[!IMPORTANT]
Next WS is designed for use in server-based environments. It is not suitable for serverless platforms like Vercel, where WebSocket servers are not supported. Furthermore, this plugin is built for the app directory and does not support the older pages directory.

This module is inspired by the now outdated next-plugin-websocket, if you are using an older version of Next.js, that module may work for you.

🏓 Table of Contents

📦 Installation

Setting up a WebSocket server with Next WS involves patching your local Next.js installation. Next WS simplifies this process with a CLI command that automatically detects and patches your Next.js version, ensuring compatibility. Note that Next.js version 13.1.1 or higher is required.

npx next-ws-cli@latest patch

[!NOTE]
If at any point your local Next.js installation is changed or updated you will need to re-run the patch command.

After successfully patching Next.js, install the Next WS package along with its peer dependency, ws, into your project:

npm install next-ws ws

🚀 Usage

Using WebSocket functionality in your Next.js application with Next WS is simple and requires no additional configuration. Simply export a SOCKET function from any route file. This function will be invoked whenever a client establishes a WebSocket connection to that specific route.

The SOCKET function receives three arguments: the WebSocket client instance, the incoming HTTP request - which you can use to get the URL path, query parameters, and headers - and the WebSocket server instance.

export function SOCKET(
  client: import('ws').WebSocket,
  request: import('http').IncomingMessage,
  server: import('ws').WebSocketServer,
) {
  // ...
}

With a Custom Server

In production, Next.js uses a worker process for routes, which can make it difficult to access the WebSocket server from outside a SOCKET handler, especially when the WebSocket server exists on the main process. For those needing to overcome this challenge or preferring a custom server setup, Next WS provides a solution.

The next-ws/server module offers functions for setting the HTTP and WebSocket servers. You use these functions to tell Next WS to use your server instances instead of creating its own. This allows you to then access the instances you created yourself from anywhere in your app. Refer to the example below.

🌀 Examples

For more detailed examples, refer the examples directory.

Creating a Socket

Creating an API route anywhere within the app directory and exporting a SOCKET function from it is all that is required. Below is an example of a simple echo server, which sends back any message it receives.

// app/api/ws/route.ts (can be any route file in the app directory)

export function SOCKET(
  client: import('ws').WebSocket,
  request: import('http').IncomingMessage,
  server: import('ws').WebSocketServer,
) {
  console.log('A client connected');

  client.on('message', (message) => {
    console.log('Received message:', message);
    client.send(message);
  });

  client.on('close', () => {
    console.log('A client disconnected');
  });
}

Using a Custom Server

To use a custom server, all you need to do is tell Next WS to use your server instead of creating its own. This can be done by calling the setHttpServer and setWebSocketServer functions from next-ws/server and passing your server instances.

// server.js

const { setHttpServer, setWebSocketServer } = require('next-ws/server');
const { Server } = require('node:http');
const { parse } = require('node:url');
const next = require('next');
const { WebSocketServer } = require('ws');

const dev = process.env.NODE_ENV !== 'production';
const hostname = 'localhost';
const port = 3000;

const httpServer = new Server();
const webSocketServer = new WebSocketServer({ noServer: true });
// Tell Next WS about the HTTP and WebSocket servers before starting the custom server
setHttpServer(httpServer);
setWebSocketServer(webSocketServer);

const app = next({ dev, hostname, port, customServer: httpServer });
const handle = app.getRequestHandler();

app.prepare().then(() => {
  httpServer
    .on('request', async (req, res) => {
      const parsedUrl = parse(req.url, true);
      await handle(req, res, parsedUrl);
    })
    .listen(port, () => {
      console.log(` ▲ Ready on http://${hostname}:${port}`);
    });
});

Accessing the WebSocket Server

Along with setters, Next WS also provides getters for the HTTP and WebSocket servers. These functions can be used to access the servers from anywhere in your app.

[!IMPORTANT]
In order to use the getWebSocketServer and getHttpServer functions, you must be using a custom server, this is due to a limitation in Next.js. Refer to the With a Custom Server.

// app/api/stats/route.ts

import { getWebSocketServer } from 'next-ws/server';
// There is also a `getHttpServer` function available

export function GET() {
  const wsServer = getWebSocketServer();
  // Response with the number of connected clients
  return Response.json({ count: wsServer.clients.size });
}

Client-Side Utilities

To make it easier to connect to your new WebSocket server, Next WS also provides some client-side utilities. These are completely optional, you can use your own implementation if you wish.

// layout.tsx
'use client';

import { WebSocketProvider } from 'next-ws/client';

export default function Layout({ children }: React.PropsWithChildren) {
  return <html>
    <body>
      <WebSocketProvider
        url="ws://localhost:3000/api/ws"
      >
        {children}
      </WebSocketProvider>
    </body>
  </html>;
}

The following is the props interface for the WebSocketProvider component, containing all the available options.

interface WebSocketProviderProps {
  children: React.ReactNode;

  /** The URL for the WebSocket to connect to. */
  url: string;
  /** The subprotocols to use. */
  protocols?: string[] | string;
  /** The binary type to use. */
  binaryType?: BinaryType;
}

Now you can use the useWebSocket hook to get the WebSocket instance, and send and receive messages.

// page.tsx
'use client';

import { useCallback, useEffect, useRef, useState } from 'react';
import { useWebSocket } from 'next-ws/client';

export default function Page() {
  const ws = useWebSocket();
  //    ^? WebSocket on the client, null on the server

  const inputRef = useRef<HTMLInputElement>(null);
  const [message, setMessage] = useState<string | null>(null);

  useEffect(() => {
    async function onMessage(event: MessageEvent) {
      const payload =
        typeof event.data === 'string' ? event.data : await event.data.text();
      const message = JSON.parse(payload) as Message;
      setMessages((p) => [...p, message]);
    }

    ws?.addEventListener('message', onMessage);
    return () => ws?.removeEventListener('message', onMessage);
  }, [ws]);

  return <>
    <input
      ref={inputRef}
      type="text"
    />

    <button
      onClick={() => ws?.send(inputRef.current?.value ?? '')}
    >
      Send message to server
    </button>

    <p>
      {message === null
        ? 'Waiting to receive message...'
        : `Got message: ${message}`}
    </p>
  </>;
}