Repository: chimurai/http-proxy-middleware (ts)

Issue:
Allow blank 'target' when specifying a custom router
### Is this a feature request?

Yes

### Motivation
I have a proxy which needs to calculate the target at runtime, using infomation in the request, therefore in the options I specify a 'router'. However, it's not possible to run the proxy without also specifying a static 'target' option. This is forcing me to configure a random target which I never actually want to be resolved.

It seems like I'm not the only one who would find this useful, see [here](https://stackoverflow.com/questions/56571972/how-to-proxy-to-many-different-targets-using-http-proxy-middleware).

Thank you!

Pull request description:
fix(option): optional `target` when `router` is used
fixes #448

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7f4d71f..87562e2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
 # Changelog
 
+## next
+
+- fix(option): optional `target` when `router` is used ([#512](https://github.com/chimurai/http-proxy-middleware/pull/512))
+
 ## [v1.1.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v1.1.0)
 
 - fix(errorHandler): fix confusing error message ([#509](https://github.com/chimurai/http-proxy-middleware/pull/509))
diff --git a/src/config-factory.ts b/src/config-factory.ts
index 5453bef..f57dd70 100644
--- a/src/config-factory.ts
+++ b/src/config-factory.ts
@@ -6,9 +6,11 @@ import { Filter, Options } from './types';
 
 const logger = getInstance();
 
-export function createConfig(context, opts?) {
+export type Config = { context: Filter; options: Options };
+
+export function createConfig(context, opts?: Options): Config {
   // structure of config object to be returned
-  const config = {
+  const config: Config = {
     context: undefined,
     options: {} as Options,
   };
@@ -38,7 +40,7 @@ export function createConfig(context, opts?) {
 
   configureLogger(config.options);
 
-  if (!config.options.target) {
+  if (!config.options.target && !config.options.router) {
     throw new Error(ERRORS.ERR_CONFIG_FACTORY_TARGET_MISSING);
   }
 
diff --git a/src/http-proxy-middleware.ts b/src/http-proxy-middleware.ts
index dbb24cb..4e3abc2 100644
--- a/src/http-proxy-middleware.ts
+++ b/src/http-proxy-middleware.ts
@@ -1,7 +1,7 @@
 import * as https from 'https';
 import * as express from 'express';
 import * as httpProxy from 'http-proxy';
-import { createConfig } from './config-factory';
+import { createConfig, Config } from './config-factory';
 import * as contextMatcher from './context-matcher';
 import * as handlers from './handlers';
 import { getArrow, getInstance } from './logger';
@@ -11,7 +11,7 @@ import { Filter, Request, RequestHandler, Response, Options } from './types';
 
 export class HttpProxyMiddleware {
   private logger = getInstance();
-  private config;
+  private config: Config;
   private wsInternalSubscribed = false;
   private serverOnCloseSubscribed = false;
   private proxyOptions: Options;


Test patch:
diff --git a/test/unit/config-factory.spec.ts b/test/unit/config-factory.spec.ts
index c329111..e2f1824 100644
--- a/test/unit/config-factory.spec.ts
+++ b/test/unit/config-factory.spec.ts
@@ -136,11 +136,27 @@ describe('configFactory', () => {
         };
       });
 
-      it('should throw an error when target option is missing', () => {
+      it('should throw an error when target and router option are missing', () => {
         expect(fn).toThrowError(Error);
       });
     });
 
+    describe('optional option.target when option.router is used', () => {
+      let fn;
+
+      beforeEach(() => {
+        fn = () => {
+          createConfig('/api', {
+            router: (req) => 'http://www.example.com',
+          });
+        };
+      });
+
+      it('should not throw an error when target option is missing when router is used', () => {
+        expect(fn).not.toThrowError(Error);
+      });
+    });
+
     describe('faulty config. mixing classic with shorthand', () => {
       beforeEach(() => {
         result = createConfig('http://localhost:3000/api', {