Skip to content

Commit

Permalink
fix: server mounting [BREAKING CHANGE]
Browse files Browse the repository at this point in the history
  • Loading branch information
chimurai committed Mar 20, 2022
1 parent 8a2e7a6 commit 91c5715
Show file tree
Hide file tree
Showing 9 changed files with 68 additions and 71 deletions.
64 changes: 30 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,14 @@ const { createProxyMiddleware } = require('http-proxy-middleware');

const app = express();

app.use('/api', createProxyMiddleware({ target: 'http://www.example.org', changeOrigin: true }));
app.use(
'/api',
createProxyMiddleware({ target: 'http://www.example.org/secret', changeOrigin: true })
);
app.listen(3000);

// http://localhost:3000/api/foo/bar -> http://www.example.org/api/foo/bar
// proxy and change the base path from "/api" to "/secret"
// http://localhost:3000/api/foo/bar -> http://www.example.org/secret/foo/bar
```

```typescript
Expand All @@ -42,9 +46,13 @@ import { createProxyMiddleware, Filter, Options, RequestHandler } from 'http-pro

const app = express();

app.use('/api', createProxyMiddleware({ target: 'http://www.example.org', changeOrigin: true }));
app.use(
'/api',
createProxyMiddleware({ target: 'http://www.example.org/api', changeOrigin: true })
);
app.listen(3000);

// proxy and keep the same base path "/api"
// http://localhost:3000/api/foo/bar -> http://www.example.org/api/foo/bar
```

Expand All @@ -57,7 +65,7 @@ _All_ `http-proxy` [options](https://github.com/nodejitsu/node-http-proxy#option
<!-- // spell-checker:disable -->

- [Install](#install)
- [Core concept](#core-concept)
- [Basic usage](#basic-usage)
- [Express Server Example](#express-server-example)
- [app.use(path, proxy)](#appusepath-proxy)
- [Options](#options)
Expand Down Expand Up @@ -87,24 +95,23 @@ _All_ `http-proxy` [options](https://github.com/nodejitsu/node-http-proxy#option
npm install --save-dev http-proxy-middleware
```

## Core concept
## Basic usage

Create and configure a proxy middleware with: `createProxyMiddleware(config)`.

```javascript
const { createProxyMiddleware } = require('http-proxy-middleware');

const apiProxy = createProxyMiddleware({
pathFilter: '/api',
target: 'http://www.example.org',
changeOrigin: true,
});

// 'apiProxy' is now ready to be used as middleware in a server.
```

- **options.pathFilter**: Determine which requests should be proxied to the target host.
(more on [path filter](#path-filter))
- **options.target**: target host to proxy to. _(protocol + host)_
- **options.changeOrigin**: for virtual hosted sites

- see full list of [`http-proxy-middleware` configuration options](#options)

Expand All @@ -117,28 +124,19 @@ An example with `express` server.
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');

const app = express();

// proxy middleware options
/** @type {import('http-proxy-middleware/dist/types').Options} */
const options = {
target: 'http://www.example.org', // target host
target: 'http://www.example.org/api', // target host with the same base path
changeOrigin: true, // needed for virtual hosted sites
ws: true, // proxy websockets
pathRewrite: {
'^/api/old-path': '/api/new-path', // rewrite path
'^/api/remove/path': '/path', // remove base path
},
router: {
// when request.headers.host == 'dev.localhost:3000',
// override target 'http://www.example.org' to 'http://localhost:8000'
'dev.localhost:3000': 'http://localhost:8000',
},
};

// create the proxy
const exampleProxy = createProxyMiddleware(options);

// mount `exampleProxy` in web server
const app = express();
app.use('/api', exampleProxy);
app.listen(3000);
```
Expand All @@ -149,7 +147,13 @@ If you want to use the server's `app.use` `path` parameter to match requests.
Use `pathFilter` option to further include/exclude requests which you want to proxy.

```javascript
app.use('/api', createProxyMiddleware({ target: 'http://www.example.org', changeOrigin: true }));
app.use(
createProxyMiddleware({
target: 'http://www.example.org/api',
changeOrigin: true,
pathFilter: '/api/proxy-only-this-path',
})
);
```

`app.use` documentation:
Expand All @@ -164,20 +168,11 @@ http-proxy-middleware options:

### `pathFilter` (string, []string, glob, []glob, function)

Decide which requests should be proxied; In case you are not able to use the server's [`path` parameter](http://expressjs.com/en/4x/api.html#app.use) to mount the proxy or when you need more flexibility.

[RFC 3986 `path`](https://tools.ietf.org/html/rfc3986#section-3.3) is used in `pathFilter`.

```ascii
foo://example.com:8042/over/there?name=ferret#nose
\_/ \______________/\_________/ \_________/ \__/
| | | | |
scheme authority path query fragment
```
Narrow down which requests should be proxied. The `path` used for filtering is the `request.url` pathname. In Express, this is the `path` relative to the mount-point of the proxy.

- **path matching**

- `createProxyMiddleware({...})` - matches any path, all requests will be proxied.
- `createProxyMiddleware({...})` - matches any path, all requests will be proxied when `pathFilter` is not configured.
- `createProxyMiddleware({ pathFilter: '/api', ...})` - matches paths starting with `/api`

- **multiple path matching**
Expand Down Expand Up @@ -205,12 +200,13 @@ Decide which requests should be proxied; In case you are not able to use the ser
/**
* @return {Boolean}
*/
const filter = function (path, req) {
const pathFilter = function (path, req) {
return path.match('^/api') && req.method === 'GET';
};

const apiProxy = createProxyMiddleware(filter, {
const apiProxy = createProxyMiddleware({
target: 'http://www.example.org',
pathFilter: pathFilter,
});
```

Expand Down
2 changes: 1 addition & 1 deletion examples/connect/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const { createProxyMiddleware } = require('../../dist'); // require('http-proxy-
* Configure proxy middleware
*/
const jsonPlaceholderProxy = createProxyMiddleware({
target: 'http://jsonplaceholder.typicode.com',
target: 'http://jsonplaceholder.typicode.com/users',
changeOrigin: true, // for vhosted sites, changes host header to match to target's host
logLevel: 'debug',
});
Expand Down
2 changes: 1 addition & 1 deletion examples/express/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const { createProxyMiddleware } = require('../../dist'); // require('http-proxy-
* Configure proxy middleware
*/
const jsonPlaceholderProxy = createProxyMiddleware({
target: 'http://jsonplaceholder.typicode.com',
target: 'http://jsonplaceholder.typicode.com/users',
changeOrigin: true, // for vhosted sites, changes host header to match to target's host
logLevel: 'debug',
});
Expand Down
4 changes: 2 additions & 2 deletions recipes/async-response.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Sometimes we need the ability to modify the response headers of the response of

```javascript
const myProxy = createProxyMiddleware({
target: 'http://www.example.com',
target: 'http://www.example.com/api',
changeOrigin: true,
selfHandleResponse: true,
onProxyReq: (proxyReq, req, res) => {
Expand Down Expand Up @@ -45,7 +45,7 @@ const entryMiddleware = async (req, res, next) => {
};

const myProxy = createProxyMiddleware({
target: 'http://www.example.com',
target: 'http://www.example.com/api',
changeOrigin: true,
selfHandleResponse: true,
onProxyReq: (proxyReq, req, res) => {
Expand Down
18 changes: 5 additions & 13 deletions recipes/basic.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,19 @@ This example will create a basic proxy middleware.
const { createProxyMiddleware } = require('http-proxy-middleware');

const apiProxy = createProxyMiddleware({
pathFilter: '/api',
target: 'http://localhost:3000',
changeOrigin: true,
});
```

## Alternative configuration

The proxy behavior of the following examples are **exactly** the same; Just different ways to configure it.

```javascript
app.use(createProxyMiddleware('/api', { target: 'http://localhost:3000', changeOrigin: true }));
```

```javascript
app.use(createProxyMiddleware('http://localhost:3000/api', { changeOrigin: true }));
```

```javascript
app.use('/api', createProxyMiddleware('http://localhost:3000', { changeOrigin: true }));
app.use('/api', createProxyMiddleware({ target: 'http://localhost:3000/api', changeOrigin: true }));
```

```javascript
app.use('/api', createProxyMiddleware({ target: 'http://localhost:3000', changeOrigin: true }));
app.use(
createProxyMiddleware({ target: 'http://localhost:3000', changeOrigin: true, pathFilter: '/api' })
);
```
30 changes: 20 additions & 10 deletions recipes/servers.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ https://github.com/expressjs/express
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');

const apiProxy = createProxyMiddleware('/api', {
target: 'http://www.example.org',
const apiProxy = createProxyMiddleware({
target: 'http://www.example.org/api',
changeOrigin: true, // for vhosted sites
});

const app = express();

app.use(apiProxy);
app.use('/api', apiProxy);
app.listen(3000);
```

Expand All @@ -48,13 +48,13 @@ const http = require('http');
const connect = require('connect');
const { createProxyMiddleware } = require('http-proxy-middleware');

const apiProxy = createProxyMiddleware('/api', {
target: 'http://www.example.org',
const apiProxy = createProxyMiddleware({
target: 'http://www.example.org/api',
changeOrigin: true, // for vhosted sites
});

const app = connect();
app.use(apiProxy);
app.use('/api', apiProxy);

http.createServer(app).listen(3000);
```
Expand Down Expand Up @@ -88,6 +88,12 @@ export default function handler(req: NextApiRequest, res: NextApiResponse) {
});
}

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

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

Expand All @@ -101,9 +107,10 @@ https://github.com/BrowserSync/browser-sync
const browserSync = require('browser-sync').create();
const { createProxyMiddleware } = require('http-proxy-middleware');

const apiProxy = createProxyMiddleware('/api', {
const apiProxy = createProxyMiddleware({
target: 'http://www.example.org',
changeOrigin: true, // for vhosted sites
pathFilter: '/api',
});

browserSync.init({
Expand Down Expand Up @@ -226,9 +233,10 @@ As a `function`:
```javascript
const { createProxyMiddleware } = require('http-proxy-middleware');

const apiProxy = createProxyMiddleware('/api', {
const apiProxy = createProxyMiddleware({
target: 'http://www.example.org',
changeOrigin: true, // for vhosted sites
pathFilter: '/api',
});

grunt.initConfig({
Expand Down Expand Up @@ -262,9 +270,10 @@ gulp.task('connect', function () {
connect.server({
root: ['./app'],
middleware: function (connect, opt) {
const apiProxy = createProxyMiddleware('/api', {
const apiProxy = createProxyMiddleware({
target: 'http://www.example.org',
changeOrigin: true, // for vhosted sites
pathFilter: '/api',
});

return [apiProxy];
Expand All @@ -284,9 +293,10 @@ https://github.com/BrowserSync/grunt-browser-sync
```javascript
const { createProxyMiddleware } = require('http-proxy-middleware');

const apiProxy = createProxyMiddleware('/api', {
const apiProxy = createProxyMiddleware({
target: 'http://www.example.org',
changeOrigin: true, // for vhosted sites
pathFilter: '/api',
});

grunt.initConfig({
Expand Down
7 changes: 1 addition & 6 deletions src/http-proxy-middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,7 @@ export class HttpProxyMiddleware {
* Determine whether request should be proxied.
*/
private shouldProxy = (pathFilter: Filter, req: Request): boolean => {
const path = (req as Request<express.Request>).originalUrl || req.url;
return matchPathFilter(pathFilter, path, req);
return matchPathFilter(pathFilter, req.url, req);
};

/**
Expand All @@ -113,10 +112,6 @@ export class HttpProxyMiddleware {
* @return {Object} proxy options
*/
private prepareProxyRequest = async (req: Request) => {
// https://github.com/chimurai/http-proxy-middleware/issues/17
// https://github.com/chimurai/http-proxy-middleware/issues/94
req.url = (req as Request<express.Request>).originalUrl || req.url;

// store uri before it gets rewritten for logging
const originalPath = req.url;
const newProxyOptions = Object.assign({}, this.proxyOptions);
Expand Down
5 changes: 5 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ export interface RequestHandler {
export type Filter = string | string[] | ((pathname: string, req: Request) => boolean);

export interface Options extends httpProxy.ServerOptions {
/**
* Narrow down requests to proxy or not.
* Filter on {@link http.IncomingMessage.url `pathname`} which is relative to the proxy's "mounting" point in the server.
* Or use the {@link http.IncomingMessage `req`} object for more complex filtering.
*/
pathFilter?: Filter;
pathRewrite?:
| { [regexp: string]: string }
Expand Down
7 changes: 3 additions & 4 deletions test/e2e/http-proxy-middleware.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,9 @@ describe('E2E http-proxy-middleware', () => {

describe('pathFilter matching', () => {
describe('do not proxy', () => {
const mockReq: Request<express.Request> = {
const mockReq: Request = {
url: '/foo/bar',
originalUrl: '/foo/bar',
} as Request<express.Request>;
} as Request;
const mockRes: Response = {} as Response;
const mockNext: express.NextFunction = jest.fn();

Expand Down Expand Up @@ -410,7 +409,7 @@ describe('E2E http-proxy-middleware', () => {
agent = request(
createAppWithPath(
'/api',
createProxyMiddleware({ target: `http://localhost:${mockTargetServer.port}` })
createProxyMiddleware({ target: `http://localhost:${mockTargetServer.port}/api` })
)
);
});
Expand Down

0 comments on commit 91c5715

Please sign in to comment.