diff --git a/src/http-proxy-middleware.ts b/src/http-proxy-middleware.ts index e3012ec5..693b348a 100644 --- a/src/http-proxy-middleware.ts +++ b/src/http-proxy-middleware.ts @@ -1,4 +1,3 @@ -import * as express from 'express'; import * as httpProxy from 'http-proxy'; import * as _ from 'lodash'; import { createConfig } from './config-factory'; @@ -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); } } else { - next(); + next && next(); } if (this.proxyOptions.ws === true) { diff --git a/src/index.ts b/src/index.ts index 9e421cd6..633c7577 100644 --- a/src/index.ts +++ b/src/index.ts @@ -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 | Options, + options?: Options +): RequestHandler { const { middleware } = new HttpProxyMiddleware(context, options); return middleware; } diff --git a/src/types.ts b/src/types.ts index e845cf81..a2cef27e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -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, + 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 = + | 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) diff --git a/test/types.spec.ts b/test/types-spec.ts similarity index 66% rename from test/types.spec.ts rename to test/types-spec.ts index 421ffaa1..36b5d58d 100644 --- a/test/types.spec.ts +++ b/test/types-spec.ts @@ -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; @@ -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; + }, + }), + ], + }, + }); + }); });