Skip to content

Proxy websockets #62

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

Open
Kerumen opened this issue Mar 3, 2022 · 11 comments
Open

Proxy websockets #62

Kerumen opened this issue Mar 3, 2022 · 11 comments
Labels
help wanted Extra attention is needed

Comments

@Kerumen
Copy link
Contributor

Kerumen commented Mar 3, 2022

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?

@stegano stegano added the help wanted Extra attention is needed label Mar 6, 2022
@stegano
Copy link
Owner

stegano commented Mar 6, 2022

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 😀

@Kerumen
Copy link
Contributor Author

Kerumen commented Mar 7, 2022

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).

@stegano
Copy link
Owner

stegano commented Mar 8, 2022

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.

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

@Kerumen
Copy link
Contributor Author

Kerumen commented Mar 10, 2022

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.

@stegano
Copy link
Owner

stegano commented Mar 11, 2022

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?

@k2xl
Copy link

k2xl commented Nov 22, 2022

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

@murilob1337
Copy link

murilob1337 commented Nov 22, 2022

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

@k2xl
Copy link

k2xl commented Nov 22, 2022

@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

@murilob1337
Copy link

murilob1337 commented Nov 22, 2022

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

@Jared-Dahlke
Copy link

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

@stegano
Copy link
Owner

stegano commented Oct 14, 2023

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

5 participants