@wwu-restek/rt-api-server
v1.2.20
Published
Server architecture for standard, Express based Node servers.
Downloads
65
Readme
ResTek API Server Framework
This is a Node server framework designed with the needs of ResTek in mind. It allows for cleaner namespacing of routes and structuring servers in a way that's scalable.
Setup
This package isn't available on npm (yet), so we'll need to install it to a project via git+ssh.
npm install @wwu-restek/rt-api-server
Documentation
If you want to look at more detailed documentation, run the following:
cd rt-api-server
npm run build_docs
This will create a directory called docs
. Open the index.html
file in a web browser for the documentation.
Usage
HTTP Server
To setup a basic HTTP server, do the following:
const { RTServer } = require('@wwu-restek/rt-api-server').Server;
const { Route, RouteContainer } = require('@wwu-restek/rt-api-server').Routing;
const server = new RTServer(5000);
const rc = new RouteContainer('test');
rc.addRoute(new Route('GET', '/hello', (req, res) => {
res.send('Hello, world!');
}));
server.loadRoutes(rc).start();
This creates a server at http://localhost:5000
with a single endpoint http://localhost:5000/test/hello
that returns the text Hello, world!
.
Routing
There are many ways to add/remove routes at runtime.
We can add or remove a Route at runtime to a RouteContainer/RouteModule:
const { Route, RouteContainer } = require('@wwu-restek/rt-api-server').Routing;
const rc = new RouteContainer('/');
const route = new Route('GET', 'test', (req, res) => res.send('Hello, world!'));
// Add route
rc.addRoute(route);
// Remove route
rc.removeRoute(route);
In order to remove a route, you MUST save a reference to the route.
We can also add/remove RouteContainers to other RouteContainers or RouteModules:
const childRC = new RouteContainer('child');
const parentRC = new RouteContainer('parent');
// Merge the child RouteContainer
parentRC.addRouteContainer(childRC);
// Remove the child RouteContainer
parentRC.removeRouteContainer(childRC);
Similar to removing Routes, you MUST save a reference to the child RouteContainer in order to remove it.
Route Modules
We can wrap up an entire API server's routing tree and export that as a plugin that other microservices/APIs can use.
const { RouteModule } = require('@wwu-restek/rt-api-server').Routing;
const rc = require('./someRouteContainer'); // This is a RouteContainer object
const rm = new RouteModule('/');
rm.addRouteContainer(rc);
module.exports = rm;
We can now use this RouteModule in other APIs/microservices as a pluggable routing module.
Route modules can also quickly create their own small RTServers that are great for development or testing.
const { Route, RouteModule } = require('@wwu-restek/rt-api-server').Routing;
const route = new Route('GET', '/test', (req, res) => res.send('Hello, world!'));
const rm = new RouteModule('/');
rm.addRoute(route);
rm.start(); // Picks a port that is available for use to serve /test
Authentication
@wwu-restek/rt-api-server
comes bundled with configuration options and middleware to make CAS authentication
easy to use. It uses the @wwu-restek/simple-cas
module to accomplish everything related to CAS.
At the server level
To configure an RTServer
object to use CAS globally:
const { RTServer } = require('@wwu-restek/rt-api-server').Server;
const server = new RTServer(80, {
session: { /* session configuration */},
auth: { baseHost: <some base>, serviceUrl: <CAS redirect URL> },
});
auth
can be set to a boolean to use the default behavior or can be an object with two optional parameters.
The default behavior is to redirect the client to CAS and have them return to the exact same route that they
requested originally.
baseHost
is an optional paramater for the hostname for CAS to redirect to after authentication. This is useful
if you have a server running at localhost:8000
since req.hostname
doesn't preserve the port number in the
hostname.
serviceUrl
is a full URL for CAS to redirect to after authentication. This is optional in case you need users
to redirect back to a front-end server after being redirected from the backend.
NOTE - You need to configure sessions in order to use CAS authentication. This is because the server needs somewhere to store CAS authentication information. The middleware that is used will throw an error if there is no session.
As middleware
There is also middleware that can be used just to protect individual routes or route containers:
const { RouteContainer } = require('@wwu-restek/rt-api-server').Routing;
const { auth } = require('@wwu-restek/rt-api-server').Auth;
const rc = new RouteContainer('/', auth({ baseHost: 'some base', serviceUrl: 'some service url' }));
auth
takes either true
or a configuration object. The behavior is the same as the server config.
Middleware
We can add middleware a couple of different ways.
We can add middleware to an RTServer, RouteContainer, or Route at instantiation:
// Add middleware to all routes of a server
const server = new RTServer(5000, { middleware: [(req, res, next) => { console.log('Hello, world!'); next(); }] });
// Add middleware to all routes within a RouteContainer
const rc = new RouteContainer('test',
(req, res, next) => { console.log("I'm middleware 1"); next(); },
(req, res, next) => { console.log("I'm middleware 2"); next(); },
);
// Add middleware to a single route
const route = new Route('GET',
'/foo',
(req, res, next) => { console.log("I'm route-specific middleware"); next(); },
(req, res) => { res.send('foo'); },
);
We can also add middleware to any of these objects after instantiation:
server.addMiddleware(middleware1, middleware2);
rc.addMiddleware(middleware3);
route.addMiddleware(middleware4, middleware5, middleware6);
Error Handling
At any point, you can throw an RTError
to send a client an RFC 7807 compliant error message:
const { RTError } = require('rt-api-server').ErrorHandling;
const route = new Route('GET', '/error', (req, res) => {
throw new RTError('This is a test error message', // Description of error
400, // HTTP status code
{ custom: 'Custom error message' }); // (Optional) More details about error for client
});
Socket.io
We can also utilize socket.io
for duplex communication:
// server.js
const server = new RTServer(5000, { socket: true });
server.addSocketListener('connection', () => { console.log('Received connection'); });
server.start();
// client.js
const io = require('socket.io-client');
const client = io('http://localhost');
client.on('connect', () => { console.log('Connected'); };