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

fix(types): make request & response generic and remove express dependency #478

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions src/http-proxy-middleware.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import * as express from 'express';
import * as httpProxy from 'http-proxy';
import * as _ from 'lodash';
import { createConfig } from './config-factory';
Expand Down Expand Up @@ -46,17 +45,17 @@ export class HttpProxyMiddleware {
public middleware: RequestHandler = async (
req: Request,
res: Response,
next: express.NextFunction
next?: (error?: any) => void
) => {
if (this.shouldProxy(this.config.context, req)) {
try {
const activeProxyOptions = await this.prepareProxyRequest(req);
this.proxy.web(req, res, activeProxyOptions);
} catch (err) {
next(err);
next && next(err);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Afaict next(err) could lead to bugs as next could be undefined

}
} else {
next();
next && next();
}

if (this.proxyOptions.ws === true) {
Expand Down
11 changes: 9 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import { HttpProxyMiddleware } from './http-proxy-middleware';
import { Filter, Options } from './types';
import { Filter, Options, RequestHandler } from './types';
import * as http from 'http';

export function createProxyMiddleware(context: Filter | Options, options?: Options) {
export function createProxyMiddleware<
Request extends http.IncomingMessage,
Response extends http.ServerResponse
>(
context: Filter<Request> | Options<Request, Response>,
options?: Options<Request, Response>
): RequestHandler<Request, Response> {
const { middleware } = new HttpProxyMiddleware(context, options);
return middleware;
}
Expand Down
25 changes: 19 additions & 6 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,31 @@
import * as express from 'express';
import * as http from 'http';
import * as httpProxy from 'http-proxy';
import * as net from 'net';

export interface Request extends express.Request {}
export interface Response extends express.Response {}
export interface Request extends http.IncomingMessage {
originalUrl?: string; // in case express, connect
hostname?: string; // in case express
host?: string; // in case express
}
export interface Response extends http.ServerResponse {}

export interface RequestHandler extends express.RequestHandler {
export interface RequestHandler<
Request extends http.IncomingMessage = http.IncomingMessage,
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making type parameters in all exported types optional to save some possibly unnecessary breaking errors like RequestHandler requires two parameters none passed. More here.

Response extends http.ServerResponse = http.ServerResponse
> {
(request: Request, response: Response, next?: (err: any) => void): void;
upgrade?: (req: Request, socket: net.Socket, head: any) => void;
}

export type Filter = string | string[] | ((pathname: string, req: Request) => boolean);
export type Filter<Request extends http.IncomingMessage = http.IncomingMessage> =
| string
| string[]
| ((pathname: string, req: Request) => boolean);

export interface Options extends httpProxy.ServerOptions {
export interface Options<
Request extends http.IncomingMessage = http.IncomingMessage,
Response extends http.ServerResponse = http.ServerResponse
> extends httpProxy.ServerOptions {
pathRewrite?:
| { [regexp: string]: string }
| ((path: string, req: Request) => string)
Expand Down
82 changes: 81 additions & 1 deletion test/types.spec.ts → test/types-spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { createProxyMiddleware as middleware } from '../src';
import { createProxyMiddleware, createProxyMiddleware as middleware } from '../src';
import { Options } from '../src/types';
import * as http from 'http';
import * as express from 'express';
import * as connect from 'connect';
import * as browserSync from 'browser-sync';
/* tslint:disable:no-unused-expression because we're expecting expressions to compile */

describe('http-proxy-middleware TypeScript Types', () => {
let options: Options;
Expand Down Expand Up @@ -156,4 +161,79 @@ describe('http-proxy-middleware TypeScript Types', () => {
});
});
});

describe('request response inference', () => {
interface FooBarRequest extends http.IncomingMessage {
foo: string;
}
interface FooBarResponse extends http.ServerResponse {
bar: number;
}
const fooBarUse = (handler: (request: FooBarRequest, response: FooBarResponse) => void) => {};

fooBarUse(createProxyMiddleware((_, request) => request.foo && true));
fooBarUse(
createProxyMiddleware({
onError: (_, request, response) => {
request.foo;
response.bar;
// @ts-expect-error
request.params;
// @ts-expect-error
response.json;
},
})
);
});

describe('works for third-party libraries', () => {
express().use(
'/proxy',
createProxyMiddleware({
onError: (_, request, response) => {
request.params;
response.json;
// @ts-expect-error
request.lol;
// @ts-expect-error
response.lol;
},
})
);

connect().use(
'/proxy',
createProxyMiddleware({
onError: (_, request, response) => {
// todo, non trivial fix @ts-expect-error
request.params;
/*
problem with connect types,
request somehow gets inferred to `any` because of `connect.ErrorHandleFunction`

connect types are anyway pretty weird, like in `connect().use((req, res) => {})`,
`req` and `req` get inferred to `any`
*/

// @ts-expect-error
response.json;
},
})
);

browserSync.create().init({
server: {
middleware: [
createProxyMiddleware({
onError: (_, request, response) => {
// @ts-expect-error
request.params;
// @ts-expect-error
response.json;
},
}),
],
},
});
});
});