├── .gitignore ├── LICENSE ├── Procfile ├── README.md ├── app.json ├── index.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | /.env 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Sasha Vodnik 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: npm start 2 | worker: node -e "setInterval(function(){console.log('working')}, 1000);" 3 | clock: node -e "setInterval(function(){console.log('tick')}, 1000);" 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ajax proxy 2 | 3 | A proxy server for redirecting HTTP requests from client-side code using credentials stored on the back end. 4 | 5 | Based on [Expressjs](https://expressjs.com) and [http-proxy-middleware](https://github.com/chimurai/http-proxy-middleware). 6 | 7 | Configuration instructions include steps for deploying to Heroku, but proxy can be deployed to any Node environment. 8 | 9 | ## Configuration 10 | 11 | 1. Clone this repo to your local machine. 12 | 2. [Create a new app on Heroku](https://dashboard.heroku.com/apps). 13 | 3. Store your API credentials using config vars. 14 | - On the Heroku dashboard for your app, click Settings, then click Reveal Config Vars. 15 | - Enter a name for the config var, paste your API key or other credential as the value, then click Add. 16 | 17 | 4. Customize index.js for your endpoint: 18 | 19 | - In the `filter` function, specify your app's front end origin as the value for `req.headers.origin`. 20 | ```js 21 | // replace www.myapp.example with origin(s) that your content will be served from 22 | return (req.headers.origin === 'https://www.myapp.example'); 23 | // multiple origin version: 24 | // return ((req.headers.origin === 'http://myapp.example') || 25 | // (req.headers.origin === 'https://www.myapp.example')); 26 | ``` 27 | - In the `apiOptions` object, specify the URL of the web service you're connecting to as the value for `target`. 28 | 29 | ```js 30 | var apiOptions = { 31 | // replace api.datasource.example with the url of your target host 32 | target: 'https://api.datasource.example', 33 | ... 34 | }; 35 | ``` 36 | 37 | - In the `apiOptions` object, update `onProxyReq` to use the keyname provided by your target host and the name of the config var you created in Heroku to store your credential value. 38 | 39 | ```js 40 | var apiOptions = { 41 | ... 42 | onProxyReq: (proxyReq) => { 43 | // append key-value pair for API key to end of path 44 | // using KEYNAME provided by web service 45 | // and KEYVALUE stored in Heroku config var 46 | proxyReq.path += ('&KEYNAME=' + process.env.KEYVALUE); 47 | }, 48 | logLevel: 'debug' // verbose server logging 49 | }; 50 | ``` 51 | 52 | 5. In your browser, return to your Heroku Dashboard, click Deploy, then follow the steps to deploy your customized code from your local machine to Heroku. 53 | 54 | 6. In the front-end code for your app, rewrite the URL in your ajax request to use your Heroku proxy app as the base URL. 55 | 56 | ## Example 57 | The following code uses the U.S. [National Park Service API](). 58 | 59 | ```js 60 | var express = require('express'); 61 | var proxy = require('http-proxy-middleware'); 62 | 63 | var filter = function (pathname, req) { 64 | return ((req.headers.origin === 'http://127.0.0.1:5500') || 65 | (req.headers.origin === 'https://127.0.0.1:5500')); 66 | }; 67 | 68 | var npsOptions = { 69 | target: 'https://developer.nps.gov', 70 | changeOrigin: true, 71 | pathRewrite: { 72 | '^/nps/': '/', 73 | }, 74 | onProxyReq: (proxyReq, req, res) => { 75 | proxyReq.path += ('&api_key=' + process.env.NPS_APIKEY); 76 | }, 77 | logLevel: 'debug' 78 | }; 79 | 80 | var npsProxy = proxy(filter, npsOptions); 81 | 82 | var app = express(); 83 | app.set('port', (process.env.PORT || 5000)); 84 | 85 | app.use('/nps', npsProxy); 86 | 87 | app.listen(app.get('port')); 88 | 89 | ``` 90 | 91 | Starting with a front-end request like 92 | 93 | ```http 94 | https://.herokuapp.com/nps/api/v1/parks?stateCode=ca 95 | ``` 96 | 97 | the proxy rewrites and forwards the request as 98 | 99 | ```http 100 | https://developer.nps.gov/api/v1/parks?stateCode=ca&api_key=######## 101 | ``` 102 | 103 | (where `########` is the secret key stored in the Heroku Config Var with the name `NPS_APIKEY`) 104 | 105 | ## License 106 | 107 | [The MIT License (MIT)](https://choosealicense.com/licenses/mit/) 108 | 109 | Copyright (c) 2019 Sasha Vodnik 110 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": {}, 3 | "formation": { 4 | "web": { 5 | "quantity": 1 6 | } 7 | }, 8 | "addons": [], 9 | "buildpacks": [ 10 | { 11 | "url": "heroku/nodejs" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var proxy = require('http-proxy-middleware'); 3 | 4 | // proxy middleware options 5 | var filter = function (pathname, req) { 6 | // replace www.myapp.example with origin(s) that your content will be served from 7 | return (req.headers.origin === 'https://www.myapp.example'); 8 | // multiple origin version: 9 | // return ((req.headers.origin === 'http://www.myapp.example') || (req.headers.origin === 'https://www.myapp.example')); 10 | }; 11 | 12 | var apiOptions = { 13 | // replace api.datasource.example with the url of your target host 14 | target: 'https://api.datasource.example', 15 | changeOrigin: true, // needed for virtual hosted sites like Heroku 16 | pathRewrite: { 17 | '/': '/', // remove endpoint from request path ('^/api/': '/') 18 | }, 19 | onProxyReq: (proxyReq) => { 20 | // append key-value pair for API key to end of path 21 | // using KEYNAME provided by web service 22 | // and KEYVALUE stored in Heroku environment variable 23 | proxyReq.path += ('&KEYNAME=' + process.env.KEYVALUE); 24 | }, 25 | logLevel: 'debug' // verbose server logging 26 | }; 27 | 28 | // create the proxy (without context) 29 | var apiProxy = proxy(filter, apiOptions); 30 | 31 | var app = express(); 32 | app.set('port', (process.env.PORT || 5000)); 33 | 34 | app.use('/api', apiProxy); 35 | 36 | app.listen(app.get('port')); 37 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "A minimal ajax proxy server using Express 4", 3 | "main": "index.js", 4 | "scripts": { 5 | "start": "node index.js" 6 | }, 7 | "dependencies": { 8 | "express": "^4.0.0", 9 | "http-proxy-middleware": "^0.19.1" 10 | }, 11 | "engines": { 12 | "ejs": "2.5.5" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/svodnik/ajaxproxy" 17 | }, 18 | "keywords": [ 19 | "node", 20 | "heroku", 21 | "express" 22 | ], 23 | "author": "Sasha Vodnik", 24 | "license": "MIT" 25 | } --------------------------------------------------------------------------------