diff --git a/README.md b/README.md index 86d8081e6d..4342b093db 100644 --- a/README.md +++ b/README.md @@ -1107,6 +1107,7 @@ module.exports = { hot: false, // HTTP proxy specific requests to a different target + // For advanced usage see https://github.com/react-cosmos/react-cosmos/pull/875 httpProxy: { context: '/api', target: 'http://localhost:4000/api' diff --git a/packages/react-cosmos-flow/config.js b/packages/react-cosmos-flow/config.js index e3e45cdaa3..609848194e 100644 --- a/packages/react-cosmos-flow/config.js +++ b/packages/react-cosmos-flow/config.js @@ -9,6 +9,10 @@ type WebpackConfigOverride = (WebpackConfig, { env: string }) => WebpackConfig; export type PluginConfig = { [prop: string]: mixed }; +type BasicHttpProxyConfig = { context: string }; +type AdvancedHttpProxyConfig = { [contextKey: string]: string | {} }; +export type HttpProxyConfig = BasicHttpProxyConfig | AdvancedHttpProxyConfig; + export type Config = { next: boolean, rootPath: string, @@ -26,7 +30,7 @@ export type Config = { publicPath?: string, publicUrl: string, containerQuerySelector?: string, - httpProxy?: {| context: string, target: string |}, + httpProxy?: HttpProxyConfig, watchDirs: Array, modulesPath: string, plugin: PluginConfig, diff --git a/packages/react-cosmos/src/server/shared/__tests__/http-proxy.js b/packages/react-cosmos/src/server/shared/__tests__/http-proxy.js new file mode 100644 index 0000000000..f866e7f490 --- /dev/null +++ b/packages/react-cosmos/src/server/shared/__tests__/http-proxy.js @@ -0,0 +1,82 @@ +import httpProxyMiddleware from 'http-proxy-middleware'; + +import { setupHttpProxy } from '../http-proxy'; + +jest.mock('http-proxy-middleware'); + +describe('setup http proxy', () => { + beforeEach(() => { + httpProxyMiddleware.mockReset(); + }); + + it('with basic configuration', () => { + const app = { + use: jest.fn() + }; + + const httpProxyConfig = { + context: '/api', + target: 'https://example.com', + otherValue: { + test: 'value' + } + }; + + httpProxyMiddleware.mockReturnValueOnce('__MOCKED_MIDDLEWARE__'); + + setupHttpProxy(app, httpProxyConfig); + + expect(app.use).toHaveBeenCalledWith('/api', '__MOCKED_MIDDLEWARE__'); + expect(httpProxyMiddleware).toHaveBeenCalledWith({ + target: 'https://example.com', + otherValue: { + test: 'value' + } + }); + }); + + it('with advanced configuration', () => { + const app = { + use: jest.fn() + }; + + const httpProxyConfig = { + '/api': { + target: 'https://example-api.com', + apiStuff: { + api: 'value' + } + }, + '/assets': { + target: 'https://example-assets.com', + assetsStuff: { + asset: 'value' + } + } + }; + + httpProxyMiddleware.mockReturnValueOnce('__MOCKED_API_MIDDLEWARE__'); + httpProxyMiddleware.mockReturnValueOnce('__MOCKED_ASSETS_MIDDLEWARE__'); + + setupHttpProxy(app, httpProxyConfig); + + expect(app.use).toHaveBeenCalledWith('/api', '__MOCKED_API_MIDDLEWARE__'); + expect(httpProxyMiddleware).toHaveBeenCalledWith({ + target: 'https://example-api.com', + apiStuff: { + api: 'value' + } + }); + + expect(app.use).toHaveBeenCalledWith( + '/assets', + '__MOCKED_ASSETS_MIDDLEWARE__' + ); + expect(httpProxyMiddleware).toHaveBeenCalledWith({ + target: 'https://example-assets.com', + assetsStuff: { + asset: 'value' + } + }); + }); +}); diff --git a/packages/react-cosmos/src/server/shared/http-proxy.js b/packages/react-cosmos/src/server/shared/http-proxy.js new file mode 100644 index 0000000000..af15dff45d --- /dev/null +++ b/packages/react-cosmos/src/server/shared/http-proxy.js @@ -0,0 +1,19 @@ +// @flow + +import httpProxyMiddleware from 'http-proxy-middleware'; +import type { HttpProxyConfig } from 'react-cosmos-flow/config'; + +export function setupHttpProxy( + app: express$Application, + httpProxy: HttpProxyConfig +) { + const { context, ...options } = httpProxy; + if (typeof context === 'string') { + app.use(context, httpProxyMiddleware(options)); + } else { + Object.keys(httpProxy).forEach(contextKey => { + const options = httpProxy[contextKey]; + app.use(contextKey, httpProxyMiddleware(options)); + }); + } +} diff --git a/packages/react-cosmos/src/server/shared/server.js b/packages/react-cosmos/src/server/shared/server.js index 49aa0c373d..f07499d3ca 100644 --- a/packages/react-cosmos/src/server/shared/server.js +++ b/packages/react-cosmos/src/server/shared/server.js @@ -4,9 +4,9 @@ import { join, relative } from 'path'; import { createServer as createHttpServer } from 'http'; import promisify from 'util.promisify'; import express from 'express'; -import httpProxyMiddleware from 'http-proxy-middleware'; import launchEditor from 'react-dev-utils/launchEditor'; import { getPlaygroundHtml, getPlaygroundHtmlNext } from './playground-html'; +import { setupHttpProxy } from './http-proxy'; import type { Config } from 'react-cosmos-flow/config'; import type { PlaygroundOpts } from 'react-cosmos-flow/playground'; @@ -22,8 +22,7 @@ export function createServerApp({ const app = express(); if (httpProxy) { - const { context, target } = httpProxy; - app.use(context, httpProxyMiddleware(target)); + setupHttpProxy(app, httpProxy); } const playgroundHtml =