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

TypeScript regression in 2.0.3 #713

Closed
2 tasks done
ryanelian opened this issue Feb 18, 2022 · 6 comments · Fixed by #882
Closed
2 tasks done

TypeScript regression in 2.0.3 #713

ryanelian opened this issue Feb 18, 2022 · 6 comments · Fixed by #882

Comments

@ryanelian
Copy link

ryanelian commented Feb 18, 2022

Checks

Describe the bug (be clear and concise)

import { createProxyMiddleware, Options } from 'http-proxy-middleware';
import { AppSettings } from '../../../functions/AppSettings';

// Great way to avoid using CORS and to transparently add Access Token to API requests
// Proxy requests from /api/demo/... to http://localhost:5000/...
const apiProxyOptions: Options = {
    target: AppSettings.current.backendHost,
    changeOrigin: true,
    // Set ws to true if proxying WebSocket
    // Recommendation: create a dedicated API gateway for WebSocket, don't mix!
    // If troublesome, just hit the WebSocket endpoint directly (CORS)
    ws: false,
    xfwd: true,
    pathRewrite: {
        '^/api/demo': '/'
    },
    logLevel: 'warn'
};

const apiProxy = createProxyMiddleware(apiProxyOptions);

const apiGateway = async (req, res) => {
    apiProxy(req, res, (result: unknown) => {
        if (result instanceof Error) {
            throw result;
        }

        throw new Error(`Failed to proxy request: '${req.url}'`);
    });
}

export default apiGateway;

export const config = {
    api: {
        externalResolver: true,
    },
}

This is the entire code.

In version 2.0.2, that code can be does not emit compile error in TypeScript.

But when upgrading to version 2.0.3, an error appeared:

[2:24:03 PM] Starting compilation in watch mode...

pages/api/demo/[...apiGateway].ts:23:5 - error TS2349: This expression is not callable.
  Type 'RequestHandler' has no call signatures.

23     apiProxy(req, res, (result: unknown) => {
       ~~~~~~~~

[2:24:05 PM] Found 1 error. Watching for file changes.

Step-by-step reproduction instructions

Place the above code in a TypeScript project.

If require a reproduction repository, run these commands:

git clone https://github.com/accelist/nextjs-starter
npm install [email protected] -E
npm update
npm run typescript

Expected behavior (be clear and concise)

No TypeScript type error in version 2..0.3

How is http-proxy-middleware used in your project?

PS D:\VS\accelist-nextjs-starter> npm ls http-proxy-middleware
[email protected] D:\VS\accelist-nextjs-starter
`-- [email protected]

What http-proxy-middleware configuration are you using?

const apiProxyOptions: Options = {
    target: AppSettings.current.backendHost,
    changeOrigin: true,
    // Set ws to true if proxying WebSocket
    // Recommendation: create a dedicated API gateway for WebSocket, don't mix!
    // If troublesome, just hit the WebSocket endpoint directly (CORS)
    ws: false,
    xfwd: true,
    pathRewrite: {
        '^/api/demo': '/'
    },
    logLevel: 'warn'
};

What OS/version and node/version are you seeing the problem?

System:
    OS: Windows 10 10.0.19044
    CPU: (12) x64 Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
    Memory: 22.21 GB / 31.85 GB
  Binaries:
    Node: 16.13.2 - C:\Program Files\nodejs\node.EXE
    npm: 8.1.2 - C:\Program Files\nodejs\npm.CMD
  Managers:
    pip3: 21.1.3 - ~\AppData\Local\Programs\Python\Python39\Scripts\pip3.EXE
  Utilities:
    Git: 2.34.0.
  Virtualization:
    Docker: 20.10.12 - C:\Program Files\Docker\Docker\resources\bin\docker.EXE
  SDKs:
    Windows SDK:
      AllowDevelopmentWithoutDevLicense: Enabled
      AllowAllTrustedApps: Enabled
  IDEs:
    Android Studio: Version  4.2.0.0 AI-202.7660.26.42.7351085
    VSCode: 1.64.2 - C:\Users\Ryan\AppData\Local\Programs\Microsoft VS Code\bin\code.CMD
    Visual Studio: 17.1.32210.238 (Visual Studio Community 2022)
  Languages:
    Bash: 5.0.17 - C:\Windows\system32\bash.EXE
    Java: 11.0.8
    Python: 3.9.6
  Browsers:
    Edge: Spartan (44.19041.1266.0), Chromium (98.0.1108.55)
    Internet Explorer: 11.0.19041.1566

Additional context (optional)

No response

@chimurai
Copy link
Owner

chimurai commented Feb 19, 2022

Hi.

I'm getting the same TypeScript error with hpm 2.0.2 on https://github.com/accelist/nextjs-starter

image

Personally, I haven't used NextJS yet.
But thinking about to add support for it.

For now you could use a temporary workaround with any:

const apiProxy = createProxyMiddleware(apiProxyOptions) as any;

Not sure if you can chain middlewares in NextJS; if it is possible perhaps you could your logic to a separate middleware; This way you don't have the "call" the HPM middleware like you are currently doing.

@cdaringe
Copy link

#721 uses the RequestMiddleware type, which is callable, and is tested to be (and TS checked)

@ryanelian
Copy link
Author

Hi.

I'm getting the same TypeScript error with hpm 2.0.2 on https://github.com/accelist/nextjs-starter

image

Hi, thanks for troubleshooting.

I am 1000% certain that the TypeScript error does not appear when using hpm 2.0.2. (But I do believe you, of course)

We are running that template for multiple projects in our organization and the TypeScript errors are encountered by many teams if they tried upgrading to hpm 2.0.3 but not when hpm 2.0.2 is pinned to the package.json.

Personally, I haven't used NextJS yet. But thinking about to add support for it.

Seeing that Next.js API endpoints support connect / express compatible middlewares and HPM is advertised in the README that it is a connect / express compatible middleware, they probably will work seamlessly if weren't for the type error.

We just need to make sure that the middleware type definition is callable.

For now you could use a temporary workaround with any:

const apiProxy = createProxyMiddleware(apiProxyOptions) as any;

Not sure if you can chain middlewares in NextJS; if it is possible perhaps you could your logic to a separate middleware; This way you don't have the "call" the HPM middleware like you are currently doing.

I think the other "API routes using 3rd party middlewares" example in the Next.js documentation page also demonstrates "calling" the middleware, so I think it's not a very alien practice imo.

Copy pasted from the official Next.js documentation:

import Cors from 'cors'

// Initializing the cors middleware
const cors = Cors({
  methods: ['GET', 'HEAD'],
})

// Helper method to wait for a middleware to execute before continuing
// And to throw an error when an error happens in a middleware
function runMiddleware(req, res, fn) {
  return new Promise((resolve, reject) => {
    fn(req, res, (result) => {
      if (result instanceof Error) {
        return reject(result)
      }

      return resolve(result)
    })
  })
}

async function handler(req, res) {
  // Run the middleware
  await runMiddleware(req, res, cors)

  // Rest of the API logic
  res.json({ message: 'Hello Everyone!' })
}

export default handler

@ryanelian
Copy link
Author

For those looking for a more permanent solution for Next.js, drop the http-proxy-middleware and use the primitive library http-proxy

This is a sample source code for forwarding an API route to a back-end server using http-proxy in Next.js:

https://github.com/accelist/nextjs-starter/blob/8b179c1892627697c61222c370531629290e6c1d/pages/api/demo/%5B...apiGateway%5D.ts

@chimurai
Copy link
Owner

http-proxy-middleware v3 added support for Next.js

It's still in beta. You can give it a try and install it with npm install http-proxy-middleware@beta

Happy to hear your feedback.

https://github.com/chimurai/http-proxy-middleware/blob/master/recipes/servers.md#nextjs

// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from 'next';
import { createProxyMiddleware } from 'http-proxy-middleware';

const proxyMiddleware = createProxyMiddleware({
  target: 'http://jsonplaceholder.typicode.com',
  changeOrigin: true,
  pathRewrite: {
    '^/api/users': '/users',
  },
});

export default function handler(req: NextApiRequest, res: NextApiResponse) {
  proxyMiddleware(req, res, (result: unknown) => {
    if (result instanceof Error) {
      throw result;
    }
  });
}

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

// curl http://localhost:3000/api/users

@francescovenica
Copy link

http-proxy-middleware v3 added support for Next.js

It's still in beta. You can give it a try and install it with npm install http-proxy-middleware@beta

Happy to hear your feedback.

https://github.com/chimurai/http-proxy-middleware/blob/master/recipes/servers.md#nextjs

// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from 'next';
import { createProxyMiddleware } from 'http-proxy-middleware';

const proxyMiddleware = createProxyMiddleware({
  target: 'http://jsonplaceholder.typicode.com',
  changeOrigin: true,
  pathRewrite: {
    '^/api/users': '/users',
  },
});

export default function handler(req: NextApiRequest, res: NextApiResponse) {
  proxyMiddleware(req, res, (result: unknown) => {
    if (result instanceof Error) {
      throw result;
    }
  });
}

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

// curl http://localhost:3000/api/users

Using apollo client I had to change the config in this way:

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

otherwise the request get stuck in pending.

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

Successfully merging a pull request may close this issue.

4 participants