Coronado Bridge
Purpose
Coronado Bridge is a simple general purpose router of HTTP requests. It does this by starting up an Express server
with a default route that accepts all requests. Then, when requests come in, they are parsed out and wrapped into a
JSON object, then passes this JSON object into an outbound provider (a class with logic of your choice that performs
additional processing.) The outbound provider can also respond to the request, and this response is delivered back to
Express for response back to the http client.
Why?
You might want to use coronado-bridge
to avoid having to implicitly import Express and write all the Express
boilerplate code and Express middleware to achieve similar functionality. With just a few lines of code, you can
accomplish what might require a good amount of Express boilerplate.
Note: If you already use Express in your application, and are very familiar with Express, this bridge might not be
all that helpful to you. :]
Outbound Provider
What is a outbound provider? An outbound provider is a class that implements a IOutboundProvider and provides a
handler()
method. All incoming http requests are passed into this function for handling. This function must return a
Promise and should resolve on success and reject on errors. The promise returned is used to respond to the original
http request.
The handler()
method is passed a req
object (of type IProviderReq). The req
object has the request method,
body, query string, request parameters (the path of the URL requested), and request headers.
Example Request:
- POST
- body:
{id: 1, name: Shane, page: 33}
- URL:
/article/55?page=54&loc=USA
- headers:
content-type: application/json
Outbound Provider req
Object:
{
method: 'POST',
body: {id: 1, name: Shane, page: 33},
query: {page: 54, loc: 'USA'},
params: ['article', '55']
headers: {
content-type: 'application/json'
}
}
Coronado Bridge accepts GET
, POST
, PUT
, DELETE
, OPTIONS
, and ignores all other HTTP methods. In fact, OPTIONS
is passed onto a CORS handler but not much else, in order to handle pre-flight of AJAX requests properly.
Example IOutboundProvider Class and handler method:
class OutboundConsole {
handler(req) {
return new Promise((resolve, reject) => {
console.log(req);
resolve({ someitem: 'somevalue' });
});
}
}
The above class will return the following to every request (and http status = 200):
{
"some-item": "some-value"
}
The above example is quite basic. You could implement a handler()
that processes the request and then communicates
with an external API or system e.g. Message Queue, SMS, HTTP, SOAP etc.
More examples can be found in ./examples
folder. Some are explained in more detail below.
Responding to Requests
Coronado Bridge automatically responds to the HTTP request with whatever the handler()
method resolves (or rejects)
with. The bridge will automatically respond with http status = 200 and either an object or a primitive you return
from the handler. However, in most cases you want to control more than this. For example, you may want to set the
response code, and control the headers that are sent back to the response.
For this, you can return the special object confirming to OutboundResponse. That object has the following structure:
- body: an object to be returned in the body of the response.
- status: a number to be set as the HTTP status for the response.
- headers?: an object of key-value pairs to be sent in the HTTP response as headers.
Here is a handler that uses this response object.
import {
IOutboundProvider,
IProviderReq,
OutboundResponse,
} from 'coronado-bridge';
export default class OutboundConsoleStructuredResponse
implements IOutboundProvider
{
handler(req: IProviderReq): Promise<any> {
return new Promise((resolve, reject) => {
const structuredResponse = new OutboundResponse(
{ response: 'OK!', yourRequest: req },
201
);
resolve(structuredResponse);
});
}
}
Similarly, here is a handler in JavaScript.
const { OutboundResponse } = require('coronado-bridge');
class OutboundFileJS {
handler(message) {
return new Promise((resolve, reject) => {
const structuredResponse = new OutboundResponse(
{ result: 'A-OK!' },
203,
{ 'some-header': 'some-value' }
);
resolve(structuredResponse);
});
}
}
module.exports = OutboundFileJS;
Getting Started
Installation
This is a Node.js module available through the npm registry.
Before installing, download and install Node.js. Review the file .nvmrc for the
recommended version of NodeJS to use.
Installation is done using the
npm install
command:
$ npm install coronado-bridge -s
Import / Require
Depending on the NodeJS flavor you're using, you can import the package as follows.
import {
IOutboundProvider,
IProviderReq,
OutboundResponse,
} from 'coronado-bridge';
const {
default: CoronadoBridge,
OutboundResponse,
BridgeError,
} = require('coronado-bridge');
The default export of the package is the CoronadoBridge object. When you initialize this, it bootstraps and starts the
application. The constructor requires a configuration object which is outlined below.
Coronado Bridge Configuration
The package has some configuration options.
Option | Type | Description | Required | Default |
---|
outboundProvider | A class that conforms to IOutboundProvider . | This class is passed all requests and should implement a handler method that determines what to do with the request. It is here that you insert your logic, call other systems, etc. | YES | N/A |
ports | Array<number> | An array with one or more port numbers that the HTTP Server will listen for requests on. | NO | 3000 |
logger | ILogger | An logger instance. | NO | N/A |
requestTimeoutMs | Number | A duration (number in milliseconds) that determines how long an Outbound Provider for an HTTP request is allowed to execute before the HTTP client is sent a timeout response. | NO | 30s |
corsOptions | object | Allows the bridge instance to control CORS headers. The option should be an object conforming to the structure documented in Express' CORS module. See https://github.com/expressjs/cors#configuration-options. | NO | CORS headers will be set to the most permissive Access-Control-Allow-Origin: * . |
Example:
const {
default: CoronadoBridge,
OutboundResponse,
} = require('coronado-bridge');
const log4js = require('log4js');
let logger = log4js.getLogger();
logger = log4js.getLogger('synchronous');
logger.level = 'all';
class OutboundFileJS {
handler(message) {
return new Promise((resolve, reject) => {
const structuredResponse = new OutboundResponse(
{ result: 'A-OK!' },
203,
{ 'some-header': 'some-value' }
);
resolve(structuredResponse);
});
}
}
const config = {
ports: [3000, 3002],
logger,
outboundProvider: new OutboundFileJS(),
requestTimeoutMs: 5000,
corsOptions: {
origin: 'https://yourapp.somedomain.com/',
methods: ['GET', 'POST'],
},
};
new CoronadoBridge(config);
Typescript
This package is built in typescript. If you are implementing this into a typescript project you can use the ICoronadoBridgeConfig
interface to help you define the configuration object.
import CoronadoBridge, { IBridgeConfig } from 'coronado-bridge';
const config: IBridgeConfig = {
ports: [3000, 3002],
logger,
outboundProvider: new OutboundFileJS(),
requestTimeoutMs: 5000,
corsOptions: {
origin: 'https://yourapp.somedomain.com/',
methods: ['GET', 'POST'],
},
};
new CoronadoBridge(config);
Outbound Provider Examples
This section is a walkthrough of some of the examples found in ./examples
. Please see the examples themselves for more
details.
Typescript
If you are implementing this into a typescript project you can use the IOutboundProvider
interface to help you define
the outbound provider (and the IProviderReq
interface to define the object passed in to the provider).
import {
IOutboundProvider,
IProviderReq,
OutboundResponse,
} from 'coronado-bridge';
class OutboundConsole implements IOutboundProvider {
handler(req: IProviderReq): Promise<void> {
return new Promise((resolve, reject) => {
const structuredResponse = new OutboundResponse(
{ response: 'OK!', yourRequest: req },
201
);
resolve(structuredResponse);
});
}
}
export default OutboundConsole;
You can also return an object or other primitive from an outbound provider's Promise:
import { IOutboundProvider, IProviderReq } from 'coronado-bridge';
class OutboundConsole implements IOutboundProvider {
handler(req: IProviderReq): Promise<any> {
return new Promise((resolve, reject) => {
console.log(req);
resolve(
'Some useful return here. Could also be an object instead of a string!'
);
});
}
}
Error Handling
This package exports a BridgeError class to handle errors in your outbound provider. The constructor
requires two
properties: an http status code, and an error message. It can also accept an error name!
import { IOutboundProvider, BridgeError } from 'coronado-bridge';
export default class OutboundError implements IOutboundProvider {
handler(req: object): Promise<void> {
return new Promise((resolve, reject) => {
const error = true;
if (error) {
const bridgeError = new BridgeError(
500,
'Flux capacitor not connected!',
'Error-Name-Here'
);
reject(bridgeError);
}
});
}
}
Running The Examples
To run the examples located in ./examples
, simple run the scripts outlined below. This section will also give you
some quick curls to test them.
Note: Be sure to run npm run bootstrap
before running any examples
NPM Scripts
bootstrap
: Installs all dependencies and builds package/examples.build
: Builds the package and examples.build:package
: Builds the package.build:examples
: Builds the examples.file
: Runs the file example (Typescript).console
: Runs the console example (Typescript).console-alternate
: Runs an alternate console example that demonstrates returning structed output back to the HTTP
request. (Typescript).error
: Runs the error example (Typescript).file-javascript-only
: Runs a javascript version of the file example.custom-file
: Runs the custom file example (Typescript).
Simple cURL - No query
Can be used with all examples
curl --location --request POST 'http://localhost:3000/' \
--header 'Content-Type: application/json' \
--data-raw '{
"data": {
"playerName": "Shane Mckenna",
"avatar": "http://j2-442.amazon.s3.com/shane",
"friend": "Dave"
}
}
'
Advanced cURL - With query
Can be used with the custom-file
example
curl --location --request POST 'http://localhost:3000/?id=2&type=camera' \
--header 'Content-Type: application/json' \
--data-raw '{
"data": {
"playerName": "Shane Mckenna",
"avatar": "http://j2-442.amazon.s3.com/shane",
"friend": "Dave"
}
}
'
Development
Be sure to read our CONTRIBUTING.md
before starting development.
Project Structure
The root directory consist of the following:
- .circleci - This is the config for CircleCI for automatic deploys of the browser app to servers.
- examples - node project with examples
- src - source code
- tests - Mocha tests
.prettierrc
- prettier configuration.nvmrc
- holds the NodeJS version number that is recommended for the projectCONTRIBUTING.md
- short guide to how you can help with this projectLICENSE.md
- package license informationpackage.json
- package dependencies, scripts etc.README.md
- This filetsconfig.json
- Typescript settings
NPM Scripts
Note: Be sure to run npm install
after you clone the project.
build
- Builds the CoronadoBridge packagetest:unit
- Runs the unit tests using Mocha
TypeScript
This project uses TypeScript, learn more about it here.
Code Formatting
This project uses Prettier, an opinionated automatic code formatter. Whenever you make a commit, Prettier will format the changed files automatically.
You probably want to integrate Prettier in your favorite editor. Read the section on Editor Integration on the Prettier GitHub page.
CI and CD
This project leverages CircleCI for continuous integration (CI) and continuous deployment (CD).
Settings for CircleCI should be adjusted in the configuration file at .circleci/config.yml
and not on the website for CircleCI.