spandx is an HTTP switchboard. With it, you can weave together pieces of a large, complex website by choosing which resources should come from your local system and which should come from a remote environment.
For example, you could point spandx at your production site, but route /static/js
to a local directory, which allows you to test your local JS against the production environment. Code in production, it's fun.
More technically, spandx is a flexible, configuration-based reverse proxy for local development.
(Note: if you're a developer on the Customer Portal, please use this guide)
While I recommend local install, if you want to quickly take spandx for a whirl, the fastest way to get started is by installing spandx as a global package.
npm install -g spandx
Generate a sample configuration file:
spandx init > spandx.config.js
Launch!
spandx
To launch spandx, simply run spandx
. If a spandx.config.js
file exists in the current directory, it will be used.
Option | Description | Example |
---|---|---|
-c , --config |
Specify an alternate config file. Config files are JS by default (to enable commenting), but JSON is also accepted. | spandx -c ./configs/spandx.json |
-v , --version |
Print the current version of spandx. | spandx -v |
Generate a configuration file. When invoked with no arguments, it prints a barebones config file. Redirect to a file (this could be improved by writing the file automatically).
spandx init > spandx.config.js
After spandx init
has generated a configuration file for you, there are many ways you can tweak it.
Here are the configuration options accepted by the config file.
Option | Description | Type |
---|---|---|
host |
The hostname you wish to use to access spandx. Usually "localhost", or a multi-host object. | string or object |
port |
The port for spandx to listen on, or "auto". | number or "auto" |
open |
Whether to open a browser tab when spandx is launched. | boolean |
startPath |
The URL path to open, if open is true. ex: "/site" . |
string |
verbose |
Display English summary of configuration settings and display Browsersync logs, or not. | boolean |
silent |
Print nothing to stdout. | boolean |
routes |
Define where to send requests for any number of URL paths, best explained in routes by example. | object |
bs |
A Browsersync config object, in case you need to further customize spandx's Browsersync instance. | object |
portalChrome |
Settings related to Portal Chrome, see Portal Chrome settings. | object |
esi |
Set to true to enable processing ESI tags, or pass in a nodesi config object. |
boolean or object |
proxy |
Define a proxy host and a URL regex pattern for target URLs that should be proxied. | object |
Routes are the core of spandx's flexibility. They allow you to define where to pull assets at any given URL path.
Route all requests to palebluepixel.org (a perfect reverse proxy), unless the request falls under /theme
, in which case look for files in ~/projects/pbp/theme
.
routes: {
"/theme" : "~/projects/pbp/theme/",
"/" : { host: "https://palebluepixel.org/" },
},
Here's how this configuration would play out.
- visit localhost:1337 and you see what looks like palebluepixel.org
- one exception, the page includes
/theme/site.css
- because it falls under the
/theme
route, spandx fetches~/projects/pbp/theme/site.css
instead ofpalebluepixel.org/theme/site.css
This effectively overlays a local directory of static files on top of a remote server. In other words... test in production!
In addition, because ~/projects/pbp/theme
is a local directory, changes to files inside it will trigger a Browsersync refresh.
To route to a local directory, the destination should be a string. The directory path can be absolute or relative. If relative, it's resolved relative to the spandx.config.js file.
routes: {
"/incoming": "./destination"
}
To route to a server, the destination should be an object with a host
property.
routes: {
"/incoming": {
host: "http://localhost:8080"
}
}
In this form, requests to /incoming
will route to http://localhost:8080/incoming
. If you would rather route to a different path (such as http://localhost:8080
), you can specify a path
property as follows.
Many projects have multiple remote webservers, for example a dev server, qa, staging, and production. To simplify dealing with multiple remotes, spandx offers multi-host routing, whether the local hostname determines which remote host to proxy to. Here's an example config.
module.exports = {
host: {
+---< dev: "dev-local.foo.com",
| prod: "prod-local.foo.com"
| },
| routes: {
| "/": {
| host: {
+-----------> dev: "http://dev.foo.com",
prod: "http://www.foo.com"
}
}
}
};
In this case, dev-local.foo.com and prod-local.foo.com should be entered in /etc/hosts
, pointing to 127.0.0.1
. Then, when spandx is visited at dev-local.foo.com, spandx knows it's the "dev" host and proxies to dev.foo.com. The names "dev" and "prod" can be any names you choose. See the examples dir for a working example.
routes: {
"/my-app": {
host: "http://localhost:8080",
path: "/"
}
}
With this path setting, requests to /my-app
will route to http://localhost:8080/
. This is particularly useful when using a local development server (like webpack-dev-server) where your app lives at /
.
When a route has single: true
, spandx will send all requests under that route to a single index.html file (except requests for assets).
This is intended to be used with pushState
and enables hash-free user-friendly URLs for single-page apps.
routes: {
"/my-app": {
host: "http://localhost:8080",
single: true
}
}
With this config, a request to /my-app/users/active
would be routed to http://localhost:8080/my-app/index.html
.
single
can also be combined with path
.
routes: {
"/my-app": {
host: "http://localhost:8080",
path: "/",
single: true
}
}
Here, a request to /my-app/users/active
would be routed to http://localhost:8080/
.
If you're developing an app that needs to connect to a proxy, use the proxy object to define the proxy host and define a URL regex pattern for request that should go through the proxy.
proxy: {
host: "http://someproxy.com:1234",
pattern: "^https:\/\/(.*?).someurl.com"
}
Here, when a URL like https://api.qa.someurl.com
is requested, it would be routed through the proxy.
Internally, spandx uses Browsersync to power some features like live-reloading. Custom Browsersync options can be embedded in your spandx.config.js file under the bs
property.
For example, let's enable HTTPS.
You can enable HTTPS by
module.exports = {
bs: {
https: true,
}
};
For extra customization (like providing your own certs), see Browsersync's HTTPS options.
The quick-start has you install spandx as a global package for simplicity, but installing it locally per-project is a better approach in many ways.
Go to your project, install spandx as a dev dependency, and create a config file:
cd $YOUR_PROJECT
npm install --save-dev spandx
npx spandx init > spandx.config.js
Modify spandx.config.js
to reflect the needs of your application.
Then edit your package.json
and add a start
script which launches spandx.
"scripts": {
"start": "spandx"
}
Now you and your fellow contributors can run npm start
without having to install or even understand spandx!
Contributions are very welcome! There's not much here yet, but when there's enough content it can be split out into a dedicated CONTRIBUTING.md.
Starting with spandx v2.0.0, one goal is to adhere to the Conventional Commits style of writing commit messages. The rewards of doing so are automating semantic version bumps and CHANGELOG updates. For now, the commit style will be used and later on, the tooling (conventional-changelog-cli perhaps) will be added.
When writing a test, or debugging a failing test, you may want to run only that test instead of the entire suite. To do that, you can filter by the name of the test. Just be specific enough to target only the test (or tests) you want to run.
For example, to run the test named "should reject invalid multi-host configs":
npm test -- --filter="invalid multi"
When curling spandx, if you are requesting an HTML document, it's important to include an appropriate Accept
header. There are two spandx features, described below, that will not work without that header.
The URL-rewriting feature is powered by browserSync's rewriteRules. However, Browsersync will only perform the rewrites if text/html
is present in the request's Accept
header. Web browsers include it by default, but if you're using cURL, you'll need to add that header in order for URLs to be rewritten.
For example:
curl -H 'Accept: text/html' localhost:1337
Just like body URL rewriting, the URL rewrite associated with the single
also needs the incoming Accept
header to include text/html
.
All other spandx features work with or without text/html
in the Accept
header.
If spandx doesn't fit, here are a few other tools that offer similar features.
- devd, a local development server with a focus on flexible reverse proxying, much like spandx. Written in Go.
- http-server, a simple command-line HTTP server. The
--proxy
flag provides a remote fallback for requests that can't be resolved locally. Written in JS. - dprox, a declarative reverse proxy for local development. Similar configuration philosophy to spandx.'
- traefik