next-http-proxy-middleware icon indicating copy to clipboard operation
next-http-proxy-middleware copied to clipboard

Proxy websockets

Open Kerumen opened this issue 2 years ago • 11 comments

I'm trying to use this library to proxy websockets. I've tried several approaches but so far none of them are working.

import { NextApiRequest, NextApiResponse } from "next";
import httpProxyMiddleware from "next-http-proxy-middleware";

export const config = {
  api: {
    bodyParser: false,
    externalResolver: true,
  },
};

export default async (req: NextApiRequest, res: NextApiResponse) => {
  return httpProxyMiddleware(req, res, {
    target: "ws://localhost:3001/graphql",
    ws: true,
  });
};

Is it possible to do it with this library?

Kerumen avatar Mar 03 '22 17:03 Kerumen

Hi @Kerumen It seems to force NextJS to remove the slash (/) at the end of the URL if the API URL path contains a '.'. In my case, the ws connection was successful using socketio.

Try this

// [...all.ts]
export default async (req: NextApiRequest, res: NextApiResponse) => {
  return httpProxyMiddleware(req, res, {
    target: "ws://localhost:3000",
    ws: true,
    pathRewrite: [
      {
        patternStr: '^/api',
        replaceStr: '',
      },
    ],
  }
};
// frontend code
...
const socket = io('localhost:3000', {
  path: 'socketio' // < (important) do not include `.` character in pathname
});
...
// ws server
...
var io = require('socket.io')(server,  {
  path: '/socketio' // < The same name as the socketio instance configuration pathname on the frontend
});
...

thanks 😀

stegano avatar Mar 06 '22 04:03 stegano

Thanks for your reply. I don't have any . in my URL. In fact, the URL is the same between the normal HTTP endpoint and for the WebSocket endpoint (it's /graphql).

I'm using the following pathRewrite:

pathRewrite: [
  {
    patternStr: "^/api",
    replaceStr: "",
  },
],

And on the frontend, I'm trying to connect with Apollo WS (the HTTP link with the proxy is working).

Kerumen avatar Mar 07 '22 14:03 Kerumen

Thanks for your reply. I don't have any . in my URL. In fact, the URL is the same between the normal HTTP endpoint and for the WebSocket endpoint (it's /graphql).

I'm using the following pathRewrite:

pathRewrite: [
  {
    patternStr: "^/api",
    replaceStr: "",
  },
],

And on the frontend, I'm trying to connect with Apollo WS (the HTTP link with the proxy is working).

the NextJs API does not seem to recognize the ws scheme because it does an http proxy.

Looks like apollo client should support a mechanism to upgrade http protocol to ws.

  • reference
    • https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism#upgrading_http1.1_connections
    • https://socket.io/docs/v3/how-it-works/#upgrade-mechanism

The picture below is an error message when I put the http scheme in the graphql-ws library.

Please let me know if I'm misunderstanding anything.

스크린샷 2022-03-08 오후 1 17 11

stegano avatar Mar 08 '22 04:03 stegano

I don't understand what you tried here. My use case is very simple: I have a Next.js API route which is basically the proxy:

// pages/api/graphql.ts

export const config = {
  api: {
    bodyParser: false,
    externalResolver: true,
  },
};

export default async (req: NextApiRequest, res: NextApiResponse) => {
  return httpProxyMiddleware(req, res, {
    target: "http://localhost:3001/graphql",
    pathRewrite: [
      {
        patternStr: "^/api",
        replaceStr: "",
      },
    ],
  });
};

On the frontend I have the Apollo's HTTP link:

new HttpLink({
  uri: "http://localhost:3000/api/graphql",
  credentials: "include",
  headers,
});

This setup is working fine. I'm trying to replicate it for WS:

export default async (req: NextApiRequest, res: NextApiResponse) => {
  return httpProxyMiddleware(req, res, {
    ws: true,
    target: "ws://localhost:3001/graphql",
    pathRewrite: [
      {
        patternStr: "^/api",
        replaceStr: "",
      },
    ],
  });
};

And on the frontend, with the WS link:

new WebSocketLink({
  uri: "ws://localhost:3000/api/graphql",
  options: {
    reconnect: true,
  },
});

And it tries to connect in loop without success. Side note, if i change the WebSocketLink's uri and directly set the backend endpoint (ws://localhost:3001/graphql) it works. Hence, it's not a problem with Apollo but rather with the proxy itself.

Kerumen avatar Mar 10 '22 15:03 Kerumen

I don't understand what you tried here. My use case is very simple: I have a Next.js API route which is basically the proxy:

In my case i used graphql-ws/createClient client

// client
import { createClient } from 'graphql-ws';
...
const client = createClient({
      url: 'http://localhost:3000/api/graphql',
    });
    (async () => {
      const result = await new Promise((resolve, reject) => {
        let result;
        client.subscribe(
          {
            query: '{ hello }',
          },
          {
            next: (data) => (result = data),
            error: reject,
            complete: () => resolve(result),
          },
        );
      });
// server
const { GraphQLSchema, GraphQLObjectType, GraphQLString } = require('graphql');
const { WebSocketServer } = require('ws');
const { useServer } = require('graphql-ws/lib/use/ws');

const schema = new GraphQLSchema({
  query: new GraphQLObjectType({
    name: 'Query',
    fields: {
      hello: {
        type: GraphQLString,
        resolve: () => 'world',
      },
    },
  }),
  subscription: new GraphQLObjectType({
    name: 'Subscription',
    fields: {
      greetings: {
        type: GraphQLString,
        subscribe: async function* () {
          for (const hi of ['Hi', 'Bonjour', 'Hola', 'Ciao', 'Zdravo']) {
            yield { greetings: hi };
          }
        },
      },
    },
  }),
});

const server = new WebSocketServer({
  port: 4000,
  path: '/graphql',
});

useServer({ schema }, server);

console.log('Listening to port 4000');

--

If the problem is still not resolved, could you please share the server/client sample source code on Stackblitz or Github repository?

stegano avatar Mar 11 '22 01:03 stegano

did you end up solving this ? i'm having same issue of getting proxy to resolve...

k2xl avatar Nov 22 '22 20:11 k2xl

Unsolved to date, nextjs has a big problem with websocket support in reverse proxy. I managed to make it work but after a few refreshes the connection drops and it only works again by restarting

murilob1337 avatar Nov 22 '22 20:11 murilob1337

@murilob1337 can you share code you got to work? I am deciding whether to throw out this path and just do a nginx frontend ... complicates development environment but pulling my hair out here

k2xl avatar Nov 22 '22 20:11 k2xl

I gave up for now and deleted the old code because there was no way for it to work perfectly, I made a reverse proxy only on the endpoint that does not have websocket and the one that has ws I put it on another port. I went through this friend, I thought about abandoning nextjs because of this

murilob1337 avatar Nov 22 '22 20:11 murilob1337

just make a standalone node server for this. unfortunately nextjs doesnt seem to help with websockets much

Jared-Dahlke avatar Oct 13 '23 14:10 Jared-Dahlke

This library is dependent on Next.js. Therefore, unless Next.js supports WebSocket communication methods, we cannot implement WebSocket communication.

stegano avatar Oct 14 '23 02:10 stegano