stegano / next-http-proxy-middleware

HTTP Proxy middleware available in API Middleware provided by Next.js.
231 stars 19 forks source link

Proxy websockets #62

Open Kerumen opened 2 years ago

Kerumen commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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 commented 1 year ago

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

murilob1337 commented 1 year ago

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 commented 1 year ago

@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 commented 1 year ago

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 commented 11 months ago

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

stegano commented 11 months ago

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