Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Usage with a custom server #8

Closed
GGAlanSmithee opened this issue Nov 1, 2023 · 14 comments · Fixed by #35
Closed

Usage with a custom server #8

GGAlanSmithee opened this issue Nov 1, 2023 · 14 comments · Fixed by #35

Comments

@GGAlanSmithee
Copy link

Hi,

Is there an easy way to access the server socket when using a custom server?

Thanks for this awesome plugin and in advance for answering my question!

@apteryxxyz
Copy link
Owner

You want to be able to access the WebSocket server outside of a SOCKET route? There isn't currently anyway to do that I believe, but I'll look into it.

@GGAlanSmithee
Copy link
Author

Thanks for your answer.

Yes, more specifically in a custom server.

I'm my case, I need to run some cronjobs at startup which needs to access the socket, but it could also be useful for the more general case where you want to use the connected event for whatever reason.

@GGAlanSmithee
Copy link
Author

GGAlanSmithee commented Nov 2, 2023

I haven't actually looked at this myself, but since (I guess) this package monkey patches nextjs with ws functionality, maybe the server sticker is already "there" at the time of startup?

@apteryxxyz
Copy link
Owner

I've tried a few methods but I'm able to make the WS server globally available. Do you have a Next.js custom server sort of template that I can use to test?

@GGAlanSmithee
Copy link
Author

GGAlanSmithee commented Nov 2, 2023

@apteryxxyz I will put up an repo right away.

interestingly, when actually testing this right now, it seems that next-ws is not compatible with next.js custom server at all atm.

Notice the different outcome in the image below when running next dev vs node server.js:

image

(which does make sense, since the server is not patched in the node server.js case. But it would be very interesting to have an api to be able to path it yourself. Or maybe I can deduce it from looking through the code)

@GGAlanSmithee
Copy link
Author

Here is a repo to test with, just run npm run dev: https://github.com/GGAlanSmithee/nextjs-ws-custom-server

@apteryxxyz
Copy link
Owner

Yeah I get that error as well, it comes somewhere from app.prepare().
Next ws grabs the http server from the server options of the next server, aka the object you pass to the next function. Even if you do pass the custom server to that, it doesn't exist on the server options next ws receives. I'm not too sure why.

@55Cancri
Copy link

55Cancri commented Nov 4, 2023

Not being able to use with custom server is also preventing me from adopting this package as well

@apteryxxyz
Copy link
Owner

I've just published an experimental version of Next WS (next-ws@experimental) that adds support for custom servers. It appears to work but it's not fully tested.

The way it works is by you assigning your own http server (and optionally ws server) to a global variable, which Next WS will check for when setting up the server. I'm not a fan of assigning to global, but I prefer this over having to patch more things in Next.js.

CustomHttpServer, CustomWsServer are two symbols that get exported from next-ws/server and can be used to assign your custom http server to the global object.

This is the setup I used:

// server.js
const { Server } = require('http');
const { WebSocketServer } = require('ws');
const { parse } = require('url');
const next = require('next');
const { CustomHttpServer, CustomWsServer } = require('next-ws/server');

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

const httpServer = new Server();
global[CustomHttpServer] = httpServer;
const wsServer = new WebSocketServer({ noServer: true });
global[CustomWsServer] = wsServer;

const app = next({ dev, hostname, port });
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}`);
    });
});

Let me know if you try it and how it goes, I'll do more testing when I have more time.

@55Cancri
Copy link

55Cancri commented Nov 6, 2023

Thanks so much man, it seems to be working for me as well, even when creating https and wss servers. Some things I noticed:

  • you still must run pnpx next-ws-cli@latest patch, even for custom server
  • the servers must be attached at the global level (not created and attached within the app.prepare statement else the app will fail)
  • package doesn't seem to have any esm exports, so if I set type: "module" in package.json, change to module export in next.config.js, and change all the require statements in server.js to import statements, then importing from next-ws/server fails.

Otherwise, the changes look good to me and I will use like this. Thanks again for doing this! So sad nextjs doesn't natively support websockets and makes it so difficult to include.

@apteryxxyz
Copy link
Owner

apteryxxyz commented Nov 6, 2023

you still must run pnpx next-ws-cli@latest patch, even for custom server

Yeah, there's a few reasons for this but the main is the Next WS needs access to the NodeNextServer. And there is no reliable (as in has everything Next WS needs) way that I know of to get that in the server.js file. There are a lot of "solutions" to the problems I encounter but they just cause more problems to arise, which is why I ended up going with using global.

the servers must be attached at the global level

The WebSocket server is setup when app.prepare is called, so the custom HTTP server needs to be defined before that.

package doesn't seem to have any esm exports

I've been meaning to replace compiling with TSC to tsup, I'll use that to add ESM exports. For the time being I could replace the CustomHttpServer symbol with a plain string so you don't have to grab it from the package in order to define.

Edit: I just remembered that Next.js has a ESM directory in their dist that Next WS doesn't patch (only patches the CommonJS versions). I not sure in which case these are used, I added type: "module" (and use next.config.mjs) to my test apps package.json and it still works. I'll have to look into that.

@apteryxxyz apteryxxyz changed the title usage with a custom server Usage with a custom server Nov 6, 2023
@apteryxxyz
Copy link
Owner

apteryxxyz commented Nov 6, 2023

I'll just published a new next-ws@experimental version (1.0.1-experimental.2), that replaces the custom symbols with strings so you no longer have to grab the symbols from the next-ws package.

This is my new server.js file:

import { Server } from 'node:http';
import { parse } from 'node:url';
import next from 'next';
import { WebSocketServer } from 'ws';

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

const httpServer = new Server();
global['NextWS::CustomHttpServer'] = httpServer;
const wsServer = new WebSocketServer({ noServer: true });
global['NextWS::CustomWsServer'] = wsServer;

const app = next({ dev, hostname, port });
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}`);
    });
});

@apteryxxyz apteryxxyz linked a pull request Nov 6, 2023 that will close this issue
@GGAlanSmithee
Copy link
Author

Thanks so much for doing this @apteryxxyz. I haven't been able to test this just yet, because I got caught up in a lot of work but I intend to test it ASAP

@Alex-Mastin
Copy link

I'm finding I have a somewhat similar use-case. I'm building an app that needs an external source to be able to send messages to the WebSocket server, but I'm hitting a wall in getting that working. I tried the above solution, and was able to get it working locally, but since my project is built in standalone mode (for Docker) it wasn't super clear how to use the custom server alongside the server.js that gets generated by Next. Also didn't really love losing TypeScript support to have a custom server.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants