├── .all-contributorsrc ├── .editorconfig ├── .gitignore ├── .templates ├── .editorconfig ├── .gitignore ├── template-sample-react-component │ ├── index.jsx │ └── index.scss └── template-sample │ └── index.js ├── README.md ├── build ├── index.d.ts ├── index.js └── index.test.d.ts ├── package-lock.json ├── package.json ├── rollup.config.js ├── src ├── index.test.ts └── index.ts ├── template.config.js └── tsconfig.json /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "commit": false, 7 | "contributors": [ 8 | { 9 | "login": "iamdenny", 10 | "name": "Denny Lim", 11 | "avatar_url": "https://avatars.githubusercontent.com/u/1505166?v=4", 12 | "profile": "http://iamdenny.com", 13 | "contributions": [ 14 | "bug", 15 | "code" 16 | ] 17 | }, 18 | { 19 | "login": "larrifax", 20 | "name": "Kristian Tryggestad", 21 | "avatar_url": "https://avatars.githubusercontent.com/u/144189?v=4", 22 | "profile": "https://github.com/larrifax", 23 | "contributions": [ 24 | "bug", 25 | "code" 26 | ] 27 | }, 28 | { 29 | "login": "gthb", 30 | "name": "Gunnlaugur Thor Briem", 31 | "avatar_url": "https://avatars.githubusercontent.com/u/153580?v=4", 32 | "profile": "https://github.com/gthb", 33 | "contributions": [ 34 | "code", 35 | "ideas" 36 | ] 37 | }, 38 | { 39 | "login": "ottovw", 40 | "name": "Otto von Wesendonk", 41 | "avatar_url": "https://avatars.githubusercontent.com/u/1045946?v=4", 42 | "profile": "https://ottovw.com", 43 | "contributions": [ 44 | "security" 45 | ] 46 | }, 47 | { 48 | "login": "dsilvasc", 49 | "name": "Daniel Silva", 50 | "avatar_url": "https://avatars.githubusercontent.com/u/24484414?v=4", 51 | "profile": "https://github.com/dsilvasc", 52 | "contributions": [ 53 | "ideas" 54 | ] 55 | }, 56 | { 57 | "login": "Kerumen", 58 | "name": "Yann Pringault", 59 | "avatar_url": "https://avatars.githubusercontent.com/u/5436545?v=4", 60 | "profile": "https://lumenstudio.dev/", 61 | "contributions": [ 62 | "code" 63 | ] 64 | }, 65 | { 66 | "login": "lorenzodejong", 67 | "name": "Lorenzo", 68 | "avatar_url": "https://avatars.githubusercontent.com/u/30781484?v=4", 69 | "profile": "https://github.com/lorenzodejong", 70 | "contributions": [ 71 | "doc" 72 | ] 73 | }, 74 | { 75 | "login": "tgrassl", 76 | "name": "Timon Grassl", 77 | "avatar_url": "https://avatars.githubusercontent.com/u/34568407?v=4", 78 | "profile": "https://medium.com/@timon.grassl", 79 | "contributions": [ 80 | "bug" 81 | ] 82 | }, 83 | { 84 | "login": "abhinavkumar940", 85 | "name": "Abhinav Kumar", 86 | "avatar_url": "https://avatars.githubusercontent.com/u/1189133?v=4", 87 | "profile": "https://github.com/abhinavkumar940", 88 | "contributions": [ 89 | "doc" 90 | ] 91 | }, 92 | { 93 | "login": "JackCuthbert", 94 | "name": "Jack Cuthbert", 95 | "avatar_url": "https://avatars.githubusercontent.com/u/5564612?v=4", 96 | "profile": "https://jackcuthbert.dev/", 97 | "contributions": [ 98 | "doc" 99 | ] 100 | }, 101 | { 102 | "login": "FDiskas", 103 | "name": "Vytenis", 104 | "avatar_url": "https://avatars.githubusercontent.com/u/468006?v=4", 105 | "profile": "https://vytenis.kuciauskas.lt", 106 | "contributions": [ 107 | "doc" 108 | ] 109 | }, 110 | { 111 | "login": "dariosky", 112 | "name": "Dario Varotto", 113 | "avatar_url": "https://avatars.githubusercontent.com/u/705644?v=4", 114 | "profile": "https://dariosky.it", 115 | "contributions": [ 116 | "doc" 117 | ] 118 | }, 119 | { 120 | "login": "johannbrynjar", 121 | "name": "johannbrynjar", 122 | "avatar_url": "https://avatars.githubusercontent.com/u/2641440?v=4", 123 | "profile": "https://github.com/johannbrynjar", 124 | "contributions": [ 125 | "bug", 126 | "code" 127 | ] 128 | }, 129 | { 130 | "login": "bever1337", 131 | "name": "bever1337", 132 | "avatar_url": "https://avatars.githubusercontent.com/u/28774413?v=4", 133 | "profile": "https://github.com/bever1337", 134 | "contributions": [ 135 | "doc" 136 | ] 137 | } 138 | ], 139 | "contributorsPerLine": 7, 140 | "projectName": "next-http-proxy-middleware", 141 | "projectOwner": "stegano", 142 | "repoType": "github", 143 | "repoHost": "https://github.com", 144 | "skipCi": true, 145 | "commitType": "docs", 146 | "commitConvention": "angular" 147 | } 148 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # @see https://editorconfig-specification.readthedocs.io/en/latest/ 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | indent_style = space 11 | indent_size = 2 12 | charset = utf-8 13 | 14 | # 4 space indentation 15 | [*.py] 16 | indent_style = space 17 | indent_size = 4 18 | 19 | # Tab indentation (no size specified) 20 | [Makefile] 21 | indent_style = tab 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # @see https://git-scm.com/docs/gitignore 2 | 3 | # `.DS_Store` is a file that stores custom attributes of its containing folder 4 | .DS_Store 5 | 6 | # Logs 7 | logs 8 | *.log 9 | 10 | # Dependencies 11 | node_modules 12 | bower_components 13 | vendor 14 | 15 | # Caches 16 | .cache 17 | .npm 18 | .eslintcache 19 | 20 | # Temporaries 21 | .tmp 22 | .temp 23 | 24 | # Built 25 | dist 26 | target 27 | built 28 | output 29 | out 30 | -------------------------------------------------------------------------------- /.templates/.editorconfig: -------------------------------------------------------------------------------- 1 | # @see https://editorconfig-specification.readthedocs.io/en/latest/ 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | indent_style = space 11 | indent_size = 2 12 | charset = utf-8 13 | 14 | # 4 space indentation 15 | [*.py] 16 | indent_style = space 17 | indent_size = 4 18 | 19 | # Tab indentation (no size specified) 20 | [Makefile] 21 | indent_style = tab 22 | -------------------------------------------------------------------------------- /.templates/.gitignore: -------------------------------------------------------------------------------- 1 | # @see https://git-scm.com/docs/gitignore 2 | 3 | # `.DS_Store` is a file that stores custom attributes of its containing folder 4 | .DS_Store 5 | 6 | # Logs 7 | logs 8 | *.log 9 | 10 | # Dependencies 11 | node_modules 12 | bower_components 13 | vendor 14 | 15 | # Caches 16 | .cache 17 | .npm 18 | .eslintcache 19 | 20 | # Temporaries 21 | .tmp 22 | .temp 23 | 24 | # Built 25 | dist 26 | target 27 | built 28 | output 29 | out 30 | -------------------------------------------------------------------------------- /.templates/template-sample-react-component/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import classNames from "classnames/bind"; 3 | 4 | import styles from "./index.scss"; 5 | 6 | const cx = classNames.bind(styles); 7 | 8 | function __templateNameToPascalCase__() { 9 | return
Hello :)
; 10 | } 11 | 12 | export default __templateNameToPascalCase__; 13 | -------------------------------------------------------------------------------- /.templates/template-sample-react-component/index.scss: -------------------------------------------------------------------------------- 1 | .__templateNameToParamCase__ { 2 | display: inline-block; 3 | } 4 | -------------------------------------------------------------------------------- /.templates/template-sample/index.js: -------------------------------------------------------------------------------- 1 | export default function __templateNameToPascalCase__() { 2 | console.log("TemplateName -> __templateName__"); 3 | console.log("TemplateName to ParamCase -> __templateNameToParamCase__"); 4 | console.log("TemplateName to PascalCase -> __templateNameToPascalCase__"); 5 | } 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Next.js HTTP Proxy Middleware 2 | ![NPM License](https://img.shields.io/npm/l/next-http-proxy-middleware) 3 | ![NPM Downloads](https://img.shields.io/npm/dw/next-http-proxy-middleware) 4 | [![All Contributors](https://img.shields.io/badge/all_contributors-14-orange.svg?style=flat-square)](#contributors-) 5 | 6 | 7 | HTTP Proxy middleware available in API Middleware provided by Next.js. 8 | 9 | 10 | ## ⭐️ Before using 11 | Please try the solutions below before using this library. 😀 12 | 13 | ### Try using `Next.js` Rewrites(recommended) 14 | * This function is supported by Next.js. No additional libraries need to be installed! 15 | * https://nextjs.org/docs/api-reference/next.config.js/rewrites 16 | ```ts 17 | // next.config.js 18 | async rewrites() { 19 | return [ 20 | { 21 | source: "/api/:path*", 22 | destination: "http://example.com/api/:path*", 23 | }, 24 | ]; 25 | }, 26 | ``` 27 | 28 | ### Try using `Http-Proxy` 29 | * `next-http-proxy-middleware` is implemented using `http-proxy` internally. Since the implementation is not complicated, it is recommended to try `http-proxy` directly. 30 | ```ts 31 | // pages/api/[...all].ts 32 | import httpProxy from "http-proxy"; 33 | 34 | export const config = { 35 | api: { 36 | // Enable `externalResolver` option in Next.js 37 | externalResolver: true, 38 | bodyParser: false, 39 | }, 40 | }; 41 | 42 | export default (req, res) => 43 | new Promise((resolve, reject) => { 44 | const proxy: httpProxy = httpProxy.createProxy(); 45 | proxy.once("proxyRes", resolve).once("error", reject).web(req, res, { 46 | changeOrigin: true, 47 | target: process.env.NEXT_PUBLIC_API_PROXY_URL, 48 | }); 49 | }); 50 | ``` 51 | 52 | ## Installation 53 | 54 | The easiest way to install `next-http-proxy-middleware` is with [npm](https://www.npmjs.com/). 55 | 56 | ```bash 57 | npm install next-http-proxy-middleware 58 | ``` 59 | 60 | Alternately, download the source. 61 | 62 | ```bash 63 | git clone https://github.com/stegano/next-http-proxy-middleware.git 64 | ``` 65 | 66 | ## Features 67 | 68 | This middleware is implemented using the [`http-proxy`](https://www.npmjs.com/package/http-proxy) library. You can use the existing options provided by `http-proxy`. And you can rewrite the api path using `pathRewrite`, an additional option provided by this middleware. 69 | 70 | - [http-proxy options](https://www.npmjs.com/package/http-proxy#options) 71 | 72 | ### `pathRewrite` option 73 | 74 | - Replaces URL information matching the pattern with another string. 75 | - Priority is determined in the order entered in the array. 76 | - If the request URL matches the pattern `pathRewrite.patternStr` replace the URL string with the value `pathRewrite.replaceStr`. 77 | 78 | ### `onProxyInit` option 79 | - You can access the `http-proxy` instance using the `onProxyInit` option. See the example below. 80 | 81 | ```ts 82 | import type { NextApiRequest, NextApiResponse } from "next"; 83 | import type { NextHttpProxyMiddlewareOptions } from "next-http-proxy-middleware"; 84 | import httpProxyMiddleware from "next-http-proxy-middleware"; 85 | 86 | const handleProxyInit: NextHttpProxyMiddlewareOptions["onProxyInit"] = (proxy) => { 87 | /** 88 | * Check the list of bindable events in the `http-proxy` specification. 89 | * @see https://www.npmjs.com/package/http-proxy#listening-for-proxy-events 90 | */ 91 | proxy.on("proxyReq", (proxyReq, req, res) => { 92 | // ... 93 | }); 94 | proxy.on("proxyRes", (proxyRes, req, res) => { 95 | // ... 96 | }); 97 | }; 98 | 99 | export default async (req: NextApiRequest, res: NextApiResponse) => 100 | httpProxyMiddleware(req, res, { 101 | target: "http://example.com", 102 | onProxyInit: handleProxyInit, 103 | }); 104 | ``` 105 | 106 | #### Example 107 | 108 | - Refer to the following for how to use Next.js API Middleware 109 | 110 | - [Next.js API Middlewares Guide](https://nextjs.org/docs/api-routes/api-middlewares) 111 | 112 | ```ts 113 | // pages/api/[...all].ts 114 | import type { NextApiRequest, NextApiResponse } from "next"; 115 | import httpProxyMiddleware from "next-http-proxy-middleware"; 116 | 117 | const isDevelopment = process.env.NODE_ENV !== "production"; 118 | 119 | export const config = { 120 | api: { 121 | // Enable `externalResolver` option in Next.js 122 | externalResolver: true, 123 | }, 124 | } 125 | 126 | export default (req: NextApiRequest, res: NextApiResponse) => ( 127 | isDevelopment 128 | ? httpProxyMiddleware(req, res, { 129 | // You can use the `http-proxy` option 130 | target: "https://www.example.com", 131 | // In addition, you can use the `pathRewrite` option provided by `next-http-proxy-middleware` 132 | pathRewrite: [{ 133 | patternStr: "^/api/new", 134 | replaceStr: "/v2" 135 | }, { 136 | patternStr: "^/api", 137 | replaceStr: "" 138 | }], 139 | }) 140 | : res.status(404).send(null) 141 | ); 142 | ``` 143 | - `externalResolver` is an explicit flag that tells the server that this route is being handled by an external resolver. Enabling this option disables warnings for unresolved requests. 144 | - See the issues below 145 | - https://github.com/stegano/next-http-proxy-middleware/issues/32 146 | - https://github.com/stegano/next-http-proxy-middleware/issues/21 147 | 148 | #### Using `multipart/form-data` 149 | * If you are using the `multipart/form-data`, refer to the Issues below 150 | * https://github.com/stegano/next-http-proxy-middleware/issues/33 151 | * https://github.com/vercel/next.js/pull/7686 152 | 153 | 154 | ## Other projects 155 | * [ReactRenderStateHook](https://www.npmjs.com/package/react-render-state-hook) 156 | * `react-render-state-hook` is a React hook that enables declarative management of components based on data processing states. It facilitates straightforward state management and data sharing among multiple components in a React. 157 | 158 | 159 | ## Contributors ✨ 160 | 161 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 |
Denny Lim
Denny Lim

🐛 💻
Kristian Tryggestad
Kristian Tryggestad

🐛 💻
Gunnlaugur Thor Briem
Gunnlaugur Thor Briem

💻 🤔
Otto von Wesendonk
Otto von Wesendonk

🛡️
Daniel Silva
Daniel Silva

🤔
Yann Pringault
Yann Pringault

💻
Lorenzo
Lorenzo

📖
Timon Grassl
Timon Grassl

🐛
Abhinav Kumar
Abhinav Kumar

📖
Jack Cuthbert
Jack Cuthbert

📖
Vytenis
Vytenis

📖
Dario Varotto
Dario Varotto

📖
johannbrynjar
johannbrynjar

🐛 💻
bever1337
bever1337

📖
188 | 189 | 190 | 191 | 192 | 193 | 194 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! 195 | -------------------------------------------------------------------------------- /build/index.d.ts: -------------------------------------------------------------------------------- 1 | import { NextApiResponse, NextApiRequest } from "next"; 2 | import httpProxy, { ServerOptions } from "http-proxy"; 3 | export interface NextHttpProxyMiddlewareOptions extends ServerOptions { 4 | pathRewrite?: { 5 | [key: string]: string; 6 | } | { 7 | patternStr: string; 8 | replaceStr: string; 9 | }[]; 10 | onProxyInit?: (httpProxy: httpProxy) => void; 11 | } 12 | /** 13 | * If pattern information matching the input url information is found in the `pathRewrite` array, 14 | * the url value is partially replaced with the `pathRewrite.replaceStr` value. 15 | * @param url 16 | * @param pathRewrite 17 | */ 18 | export declare const rewritePath: (url: string, pathRewrite: { 19 | [key: string]: string; 20 | } | { 21 | patternStr: string; 22 | replaceStr: string; 23 | }[] | undefined) => string; 24 | /** 25 | * Next.js HTTP Proxy Middleware 26 | * @see https://nextjs.org/docs/api-routes/api-middlewares 27 | * @param {NextApiRequest} req 28 | * @param {NextApiResponse} res 29 | * @param {NextHttpProxyMiddlewareOptions} httpProxyOptions 30 | */ 31 | declare const httpProxyMiddleware: (req: NextApiRequest, res: NextApiResponse, httpProxyOptions?: NextHttpProxyMiddlewareOptions) => Promise; 32 | export default httpProxyMiddleware; 33 | -------------------------------------------------------------------------------- /build/index.js: -------------------------------------------------------------------------------- 1 | "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var httpProxy=require("http-proxy");function _interopDefaultLegacy(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var httpProxy__default=_interopDefaultLegacy(httpProxy),__assign=function(){return(__assign=Object.assign||function(e){for(var t,r=1,n=arguments.length;ri[0]&&t[1]", 13 | "Denny Lim", 14 | "Kristian Tryggestad", 15 | "Gunnlaugur Thor Briem", 16 | "Otto von Wesendonk", 17 | "Daniel Silva", 18 | "Yann Pringault", 19 | "Lorenzo", 20 | "Timon Grassl", 21 | "Abhinav Kumar", 22 | "Jack Cuthbert", 23 | "Vytenis", 24 | "Dario Varotto", 25 | "johannbrynjar" 26 | ], 27 | "dependencies": { 28 | "@types/http-proxy": "1.17.3", 29 | "http-proxy": "^1.18.1" 30 | }, 31 | "devDependencies": { 32 | "@types/jest": "^25.1.3", 33 | "typescript": "^3.8.2", 34 | "jest": "^29.7.0", 35 | "next": "^14.1.1", 36 | "rollup": "^2.38.0", 37 | "rollup-plugin-typescript2": "^0.29.0", 38 | "rollup-plugin-uglify": "^6.0.4", 39 | "ts-jest": "^26.4.4" 40 | }, 41 | "jest": { 42 | "transform": { 43 | "^.+\\.ts$": "ts-jest" 44 | }, 45 | "testRegex": "\\.test\\.ts$", 46 | "moduleFileExtensions": [ 47 | "ts", 48 | "js" 49 | ], 50 | "globals": { 51 | "ts-jest": { 52 | "enableTsDiagnostics": true 53 | } 54 | } 55 | }, 56 | "license": "MIT", 57 | "bugs": { 58 | "url": "https://github.com/stegano/next-http-proxy-middleware/issues" 59 | }, 60 | "keywords": [ 61 | "reverse", 62 | "proxy", 63 | "middleware", 64 | "http", 65 | "https", 66 | "connect", 67 | "express", 68 | "websocket", 69 | "ws", 70 | "cors", 71 | "next", 72 | "next.js" 73 | ], 74 | "homepage": "https://github.com/stegano/next-http-proxy-middleware#readme", 75 | "engines": { 76 | "node": ">=10.0.0" 77 | } 78 | } -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from "rollup-plugin-typescript2"; 2 | import { uglify } from "rollup-plugin-uglify"; 3 | 4 | export default { 5 | input: "./src/index.ts", 6 | output: { 7 | file: "./build/index.js", 8 | format: "cjs", 9 | exports: "named" 10 | }, 11 | plugins: [typescript(), uglify()], 12 | external: ['next', 'http-proxy'] 13 | }; 14 | -------------------------------------------------------------------------------- /src/index.test.ts: -------------------------------------------------------------------------------- 1 | import { rewritePath } from "./index"; 2 | 3 | describe("Test `rewritePath` functionn ", () => { 4 | test("[deprecated] Replace root URI context", () => { 5 | const originUrl = "/api/a/b"; 6 | expect("/auth/a/b").toEqual(rewritePath(originUrl, { "/api": "/auth" })); 7 | }); 8 | 9 | test("[deprecated] Remove root URI context", () => { 10 | const originUrl = "/api/a/b"; 11 | expect("/auth/a/b").toEqual(rewritePath(originUrl, { "/api": "/auth" })); 12 | }); 13 | 14 | test("Replace root URI context", () => { 15 | const originUrl = "/api/a/b"; 16 | expect("/auth/a/b").toEqual(rewritePath(originUrl, [{ 17 | patternStr: "/api", 18 | replaceStr: "/auth" 19 | }, 20 | ])); 21 | }); 22 | 23 | test("Remove root URI context", () => { 24 | const originUrl = "/api/a/b"; 25 | expect("/auth/a/b").toEqual(rewritePath(originUrl, [{ 26 | patternStr: "/api", 27 | replaceStr: "/auth" 28 | }])); 29 | }); 30 | 31 | test("Issue(#45) test", () => { 32 | const originUrl = "/test/api/graphql"; 33 | expect("/test").toEqual(rewritePath(originUrl, { 34 | "/api/graphql": "", 35 | })); 36 | }); 37 | 38 | test("Issue(#45) test", () => { 39 | const originUrl = "https://naver.com/test/api/graphql"; 40 | expect("https://naver.com/test/api/graphql").toEqual(rewritePath(originUrl, [{ 41 | patternStr: "/api/graphql", 42 | replaceStr: "" 43 | }])); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { NextApiResponse, NextApiRequest } from "next"; 2 | import httpProxy, { ServerOptions } from "http-proxy"; 3 | export interface NextHttpProxyMiddlewareOptions extends ServerOptions { 4 | pathRewrite?: { [key: string]: string } 5 | | { patternStr: string, replaceStr: string }[]; 6 | onProxyInit?: (httpProxy: httpProxy) => void 7 | } 8 | 9 | /** 10 | * Please refer to the following links for the specification document for HTTP. 11 | * @see https://tools.ietf.org/html/rfc7231 12 | * @see https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol 13 | */ 14 | const hasRequestBodyMethods: string[] = ["HEAD", "POST", "PUT", "DELETE", "CONNECT", "OPTIONS", "PATCH"]; 15 | 16 | /** 17 | * If pattern information matching the input url information is found in the `pathRewrite` array, 18 | * the url value is partially replaced with the `pathRewrite.replaceStr` value. 19 | * @param url 20 | * @param pathRewrite 21 | */ 22 | export const rewritePath = ( 23 | url: string, 24 | pathRewrite: NextHttpProxyMiddlewareOptions['pathRewrite'] 25 | ) => { 26 | if(Array.isArray(pathRewrite)){ 27 | for (const item of pathRewrite) { 28 | const { 29 | patternStr, 30 | replaceStr 31 | } = item; 32 | const pattern = RegExp(patternStr); 33 | if (pattern.test(url as string)) { 34 | return url.replace(pattern, replaceStr); 35 | } 36 | } 37 | } else { 38 | console.warn('[next-http-proxy-middleware] Use array instead of object for \`pathRewrite\` value ' 39 | + '(related issue: https://github.com/stegano/next-http-proxy-middleware/issues/39)'); 40 | for (const patternStr in pathRewrite) { 41 | const pattern = RegExp(patternStr); 42 | const path = pathRewrite[patternStr]; 43 | if (pattern.test(url as string)) { 44 | return url.replace(pattern, path); 45 | } 46 | } 47 | } 48 | return url; 49 | }; 50 | 51 | /** 52 | * Next.js HTTP Proxy Middleware 53 | * @see https://nextjs.org/docs/api-routes/api-middlewares 54 | * @param {NextApiRequest} req 55 | * @param {NextApiResponse} res 56 | * @param {NextHttpProxyMiddlewareOptions} httpProxyOptions 57 | */ 58 | const httpProxyMiddleware = async ( 59 | req: NextApiRequest, 60 | res: NextApiResponse, 61 | httpProxyOptions: NextHttpProxyMiddlewareOptions = {}, 62 | ): Promise => 63 | new Promise((resolve, reject) => { 64 | const { pathRewrite, onProxyInit, ...serverOptions } = httpProxyOptions; 65 | 66 | /** 67 | * @see https://www.npmjs.com/package/http-proxy 68 | */ 69 | const proxy: httpProxy = httpProxy.createProxy(); 70 | 71 | if(typeof onProxyInit === 'function') { 72 | onProxyInit(proxy); 73 | } 74 | 75 | if (pathRewrite) { 76 | req.url = rewritePath(req.url as string, pathRewrite); 77 | } 78 | 79 | if (hasRequestBodyMethods.indexOf(req.method as string) >= 0 && typeof req.body === "object") { 80 | req.body = JSON.stringify(req.body); 81 | } 82 | proxy 83 | .once("proxyReq", ((proxyReq: any, req: any): void => { 84 | if (hasRequestBodyMethods.indexOf(req.method as string) >= 0 && typeof req.body === "string") { 85 | proxyReq.write(req.body); 86 | proxyReq.end(); 87 | } 88 | }) as any) 89 | .once("proxyRes", resolve as any) 90 | .once("error", reject) 91 | .web(req, res, { 92 | changeOrigin: true, 93 | ...serverOptions 94 | }); 95 | }); 96 | 97 | export default httpProxyMiddleware; 98 | -------------------------------------------------------------------------------- /template.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This file is a configuration file generated by the `Template` extension on `vscode` 3 | * @see https://marketplace.visualstudio.com/items?itemName=yongwoo.template 4 | */ 5 | module.exports = { 6 | // You can change the template path to another path 7 | templateRootPath: "./.templates", 8 | // After copying the template file the `replaceFileTextFn` function is executed 9 | replaceFileTextFn: (fileText, templateName, utils) => { 10 | // @see https://www.npmjs.com/package/change-case 11 | const { changeCase } = utils; 12 | // You can change the text in the file 13 | return fileText 14 | .replace(/__templateName__/gm, templateName) 15 | .replace( 16 | /__templateNameToPascalCase__/gm, 17 | changeCase.pascalCase(templateName) 18 | ) 19 | .replace( 20 | /__templateNameToParamCase__/gm, 21 | changeCase.paramCase(templateName) 22 | ); 23 | }, 24 | replaceFileNameFn: (fileName, templateName, utils) => { 25 | const { path } = utils; 26 | // @see https://nodejs.org/api/path.html#path_path_parse_path 27 | const { base } = path.parse(fileName); 28 | // You can change the file name 29 | return base; 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "esnext", 5 | "outDir": "build", 6 | "sourceMap": false, 7 | "strict": true, 8 | "lib": ["dom", "dom.iterable", "esnext"], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "noEmit": true, 13 | "esModuleInterop": true, 14 | "moduleResolution": "node", 15 | "resolveJsonModule": true, 16 | "isolatedModules": true, 17 | "jsx": "preserve", 18 | "declaration": true 19 | }, 20 | "include": ["src/**/*"], 21 | "exclude": ["node_modules"] 22 | } 23 | --------------------------------------------------------------------------------