sans-server
Write code for a server, without the server.
- Make requests that are representative of HTTP requests to a function.
- Get responses that are representative of HTTP responses.
- Accepts connect middleware.
- Easier to test your server code (using direct function calls).
- Faster to test your server code (no HTTP required).
- Can be wrapped by any server.
- Logs and profiling grouped by request.
Example
The sans-server package is a tool for building a functional web server that is independent of networking. As a functional library, requests can be made to it and responses provided by it.
const SansServer = ; // create a server instanceconst server = ; // add middleware to the serverserver; // make a request against the serverserver; // make a request against the serverserver; // make a request using a promiseserver ;
Table of Contents
constructor
SansServer Create a Sans Server instance that follows the specified configuration.
Signature SansServer ([ config ]) : SansServer
Methods
- hook - Add a hook to each request.
- hook.define - Define a custom hook.
- hook.type - Get the primitive from a hook symbol.
- request - Make a request.
- use - Add a middleware to each request.
Static Methods
- hooks.validateMethod - A request hook for validating the HTTP method.
- hooks.transformResponse - A response hook for transforming the response body to a string and setting an unset
Content-Type
.
Parameters
Parameter | Description | Type | Default |
---|---|---|---|
config | An optional object defining specific behavior for the instance. To see what options are accepted see Config Options. | Object |
See Config Options |
Config Options
Option | Description | Type | Default |
---|---|---|---|
logs | A boolean that specifies whether the grouped logs should be output at the end of a request. | boolean |
true |
rejectable | A value that specifies if request promises should be rejected or automatically caught. If set to false then requests will always return a valid response. |
boolean |
false |
timeout | The number of seconds to wait prior to request timeout. Set this value to zero to disable the timeout. | number |
30 |
useBuiltInHooks | A boolean specifying whether built in hooks should run for each request. This includes request method validation and response transformation. If set to false the built in hooks can still be added manually. | boolean |
true |
Returns a Sans Server instance.
Example
const SansServer = ;const server = ;
SansServer#hook
Add a hook to each request. For an explanation on hooks see Hooks and Middleware.
Signature SansServer#hook (type, [ weight, ] hook [, hook... ]) : SansServer
Parameters
Parameter | Description | Type | Default |
---|---|---|---|
type | The type of hook to apply the hook function to. Out of the box this package has a request and response hook, but other packages or modules can add to the list of available hooks. |
string |
|
weight | The weight of the hook(s) being added. A lower number means the hook will run sooner and a higher number means it will run later. Negative numbers are allowed. | number |
0 |
hook | A hook function. Naming the function will improve log readability. Any number of hook functions can be defined at once. | function |
Returns The current SansServer instance.
Example: Single Hook
const SansServer = ;const server = ; server;
Example: Multiple Hooks
const SansServer = ;const server = ; server; { // run some logic here... ;} { // run some logic here... ;}
Example: Single Hook that Runs Earlier
const SansServer = ;const server = ; server;
SansServer#hook.define
Define a unique hook type. Attempting to define a second hook with the same name will throw an error.
Signature SansServer#hook.define ( type ) : Symbol
Parameters
Parameter | Description | Type | Default |
---|---|---|---|
type | The hook type to define. | string |
Returns a symbol that must be used to execute all hook functions that have subscribed to this hook chain.
Example
const SansServer = ;const server = ; // define custom hookconst key = serverhook; // during the request you can execute your custom hookserver;
SansServer#hook.type
Get the primitive from a hook symbol.
Signature SansServer#hook.type ( symbol ) : string
Parameters
Parameter | Description | Type | Default |
---|---|---|---|
symbol | The symbol to get the hook type for. | Symbol |
Returns the string that was used to create the symbol.
Example
const SansServer = ;const server = ; // define custom hookconst key = serverhook; const type = serverhooktypekey; // 'my-hook'
SansServer#request
Make a request against the server and get back a Request instance.
Signature SansServer#request ([ request ,] [ callback ]) : Request
Emits request
: Request
Parameters
Parameter | Description | Type | Default |
---|---|---|---|
request | A request configuration. If a string is used then the string is considered to be the path and all other request defaults will be applied. |
string object |
See Request Configuration |
callback | A function to call when the request has been completed. The callback receives any error as its first parameter. The response state is provided as the second parameter whether there was an error or not. | function |
Request Configuration
Option | Description | Type | Default |
---|---|---|---|
body | The body of the request. This can be any data type, generally a primitive or a plain object is recommended. If the body contains a form payload then it should follow the request body documentation. | '' |
|
headers | The request headers. This needs to be an object with string keys mapped to string values. For example: { headers: { 'content-type', 'plain/text' } } . |
object |
{} |
method | The request method. Must be one of 'GET' , 'HEAD' , 'POST' , 'PUT' , 'DELETE' , 'OPTIONS' , 'PATCH' . Case is not important. |
string |
'GET' |
query | The query string parameters. If a string then it will be parsed. If an object then each key must be a string and each value must be either a string, true , or an array of strings. |
object string |
{} |
path | The path for the request. The path should not include the protocol, domain, or port information. The path may contain query parameters and those will be pushed into the request query object automatically. | string |
'' |
Request Body
If the body is an object then it can be used to represent one of several content types: application/json
, application/x-www-form-urlencoded
, or multipart/form-data
.
The type that the object represents is determined by the Content-Type
header. If the body is an object and the Content-Type header is either application/x-www-form-urlencoded
or multipart/form-data
then the object should be in form body format.
Form body format is used when the body represents a submitted form, whether via application/x-www-form-urlencoded
or multipart/form-data
.
For example, look at this HTML form:
Full Name: Interests: Computers Outdoors Sports Profile Picture:
Assume that the form inputs had these values when the form was submitted:
- fullName -
'Bob Smith'
- interests - Both
Computers
andSports
where checked - picture - a file was selected
Then the request would look like this:
const body = fullName: headers: {} content: "Bob Smith" interests: headers: {} content: "Computers" headers: {} content: "Sports" picture: headers: {} content: "dGhpcyBpcyBhIHBpY3R1cmUgZmlsZQ==" ; const sansServer = ;sansServer;
Optionally the headers
property for each form input can be omitted, but the content
property is always required and must be a string.
Returns a Request instance.
Callback Example
const SansServer = ;const server = ; server;
Promise Example
const SansServer = ;const server = ; server ;
Example with Config Object
const SansServer = ;const server = ; server ;
Example of Event Listening
const SansServer = ;const server = ; const req = server ;
Example with Query Parameters
const SansServer = ;const server = ; // these two requests are equivalent:server;server;
SansServer#use
Add a middleware hook to each request. This works the same as connect middleware and is equivalent to calling sansServer.hook('request', 0, myMiddlewareFunction)
. For an explanation on hooks see Hooks and Middleware.
Signature SansServer#use (middleware [, middleware... ]) : SansServer
Parameters
Option | Description | Type | Default |
---|---|---|---|
middleware | A middleware function. Naming the function will improve log readability. Any number of middleware functions can be defined at once. | function |
Returns the current Sans Server instance.
Example: Single Middleware
const SansServer = ;const server = ; server;
Example: Multiple Middleware
const SansServer = ;const server = ; server; { // run some logic here... ;} { // run some logic here... ;}
SansServer.hooks.validateMethod
A static method that is best used early in the request hooks. It validates that the HTTP method is one of (case insensitive) 'GET'
, 'HEAD'
, 'POST'
, 'PUT'
, 'DELETE'
, 'OPTIONS'
, 'PATCH'
.
This method is automatically used as a request hook with weight -100000
if the SansServer configuration options has useBuiltInHooks set to true
. Otherwise you can add the hook manually like this:
Example
const SansServer = ; // create the Sans Server instance without using built in hooksconst sansServer = ; // add validate method request hooksansServer;
SansServer.hooks.transformResponse
A static method that is best used late in the response hook.
This method will automatically set the Content-Type
header if not set and it will transform the body into a string.
This is how the conversions are made. Content-Type
is only modified if unset, unless otherwise specified below.
Body Type | Sets Content-Type To | Body Transformation |
---|---|---|
Error |
Always 'text/plain' |
'Internal Server Error' |
Buffer |
'application/octet-stream' |
Convert to base64 encoded string. |
Object |
'application.json' |
Convert using JSON.stringify |
string |
'text/html' |
None |
Anything else | 'text/plain' |
Convert using String() |
Example
const SansServer = ; // create the Sans Server instance without using built in hooksconst sansServer = ; // add validate method request hooksansServer;
constructor
Request This constructor is invoked when calling SansServer#request and an instance of this constructor is returned by that function. This constructor cannot be invoked directly.
Because the request instance extends the Promise you can also use then
and catch
although the promise will never be rejected so you can skip using the catch
function.
Extends EventEmitter
Promise
Methods
- catch - Catch any request processing errors. If the Sans Server rejectable is not set to
true
then this method is useless. - hook - Add a hook to the request.
- hook.reverse - Run specified hook functions in reverse.
- hook.run - Run specified hook functions in order.
- log - Produce a request log event.
- logger - Produce a logging function.
- then - Assign a callback for the resolved promise.
Properties
body
- Get or set the request body.headers
- Get or set the request headers.id
- Get the unique request ID.method
- Get or set the request method.path
- Get or set the request path.query
- Get or set the request query parameters.res
- Get the Response instance tied to this request.server
- Get a reference to the Sans Server instance that made this request.url
- Get the request URL, a combination of the path and query string parameters.
Events
Unless otherwise noted, each of these events provide the Response
instance with the event.
error
- Fires when an error occurs and provides the error as event data.log
- Fires when a message is logged and provides the following structure as it's event data:{ action: string, category: string, details: object, message: string, timestamp: number }
.res-clear-header
- Fired when a header is cleared.res-complete
- Fires afterres-send
event and after all response hooks have completed.res-reset
- Fires when the body, status code, headers, and cookies have all been reset to empty.res-send
- Fires when the Response#send function has been called.res-set-body
- Fired when the body has been modified.res-set-cookie
- Fired when a cookie is set or cleared.res-set-header
- Fired when a header is set.res-set-status
- Fired when the status code changes.res-state-change
- Fired when any of the response state has been modified.
Hooks
request
- Runs when the request is initialized.
Request#catch
Add a rejection handler to the request promise.
Note, the request will never be rejected so any handler you define here will never be called. In the case of an error the request will still be resolved to a response with a 500
status code.
Signature Request#catch ( onRejected ) : Response
Parameters
Parameter | Description | Type | Default |
---|---|---|---|
onRejected | The function to call in case of promise rejection. | function |
Returns a Request instance.
Request#hook
Add a hook for just this request. Use SansServer#hook to add a hook for all requests.
Signature Request#hook ( type, [ weight, ] ...function ) : Request
Parameters
Parameter | Description | Type | Default |
---|---|---|---|
type | The hook for which the defined functions will be executed. | string |
|
weight | How soon the hook should run in the hook sequence. Lower numbers run sooner and higher numbers run later. | number |
0 |
function | The hook for which the defined functions will be executed. | string |
Returns the Request instance.
Example
const SansServer = ;const sansServer = ; const req = sansServer; req;
Request#hook.reverse
Run the specified set of hooks in reverse.
Signature Request#hook.reverse ( key [, next ] ) : Promise | undefined
Parameters
Parameter | Description | Type | Default |
---|---|---|---|
key | The key that will allow running of the hooks. | symbol |
|
next | An optional function that will be called after executing all hooks. If an error occurs this function will receive that as its first parameter, otherwise it will not receive a parameter. | function |
Returns a Promise if the next
function was not provided as a parameter.
Example
const SansServer = ;const sansServer = ; // define a custom hookconst key = sansServerhook; // define a hook function for the response hooksansServer;
Request#hook.run
Run the specified set of hooks in order.
Signature Request#hook.run ( key [, next ] ) : Promise | undefined
Parameters
Parameter | Description | Type | Default |
---|---|---|---|
key | The key that will allow running of the hooks. | symbol |
|
next | An optional function that will be called after executing all hooks. If an error occurs this function will receive that as its first parameter, otherwise it will not receive a parameter. | function |
Returns a Promise if the next
function was not provided as a parameter.
Example
const SansServer = ;const sansServer = ; // define a custom hookconst key = sansServerhook; // define a hook function for the response hooksansServer;
Request#log
Produce a log event while processing a request.
Signature Request#log ([ type, ] message [, details ]) : Request
Parameters
Parameter | Description | Type | Default |
---|---|---|---|
type | A category that can be defined to help identify grouped log messages. | string |
'log' |
message | The log message. | string object |
|
details | A string or object that only displays when logging is set to verbose. | string object |
{} |
Returns the Request instance.
Example
const SansServer = ;const server = ; server;
Request#logger
Produce a logging function. The returned function can be called to produce standardized log events. This function is used to produce the log function for the both the Request and Response.
Signature Request#logger (category, type, [ returnValue ]) : Function
Parameters
Parameter | Description | Type | Default |
---|---|---|---|
category | A short string that describes the overarching code that is producing the log function. A good option for this value would be the name of the NodeJS package producing the log function. | string |
|
type | A classification that describes the subset of code that is producing the log function. A good option for this value would be the name of the module of code that is producing the log function. | string |
|
returnValue | A value to run when the log function is called. Ideal for property chaining. | Any | undefined |
Returns the log producing function. The function can be called with any parameters and will format the logged data similar to the console.log
function, but this function will have the advantage of logging with the Debug package as well as grouping logs for a single request.
Example
This example is trivial and a bit of a waste. It would generally make more sense to produce a log function for a complex middleware that implements it's own sans-server-middleware.
const SansServer = ;const server = ; server;
Request#then
Add fulfillment or rejection handlers to the request promise.
Note, the request will never be rejected so it is a waste to provide an onRejected parameter. In the case of an error then request will be resolved to a response with a 500
status code.
Signature Request#catch ( onFulfilled [, onRejected ] ) : undefined
Parameters
Option | Description | Type | Default |
---|---|---|---|
onFulfilled | The function to call in case of promise resolution. The function will receive the response state as its input parameter. | function |
|
onRejected | The function to call in case of promise rejection. (Any function you supply to this will never be called.) | function |
Returns a Promise.
constructor
Response This constructor is invoked when calling SansServer#request and an instance of this constructor is attached to the Request instance. This constructor cannot be invoked directly.
Methods
- body - Set the response body.
- clearCookie - Remove a cookie by setting it as expired.
- clearHeader - Remove a response header.
- cookie - Set a response cookie.
- log - Produce a response log event.
- redirect - Redirect to client to a new location.
- reset - Reset the body, headers, cookies, and status code.
- send - Send the response.
- sendStatus - Send the response with a status code and status message.
- set - Alias for Response#setHeader .
- setHeader - Set a response header.
- status - Set the response status code.
Properties
- req :
Request
- Get the request object that is associated with this response object. - sent :
boolean
- Get whether the request has already been sent. - server :
SansServer
- Get the SansServer instance tied to this request. - state :
object
- Get the current response state. See response state for details. - statusCode :
number
- Get or set the current response status code.
Hooks
response
- Runs in reverse when the response is sent. Lower weights will still run first, but two functions of equal weight will run in reverse order from when they were set.
Response State
The response state is an object that represents the response at the point in time it was requested. It can be acquired using the Response#state` getter.
This object has the following structure:
{
body: *,
cookies: Array.<{ name: string, options: object, serialized: string, value: string }>,
headers: Object.<string,string>,
rawHeaders: Array.<string>,
statusCode: number
}
Response#body
Set the response body.
Signature Response#body ( value ) : Response
Parameters
Parameter | Description | Type | Default |
---|---|---|---|
value | The value to set the body to. This value can be set to anything but once all response hooks have run it will be converted to either a string, a Buffer, or a plain object. | any |
Returns the Response instance.
Emits res-set-body
res-state-change
Response#clearCookie
Remove a cookie by setting it as expired.
Signature Response#clearCookie ( name [, options ] ) : Response
Parameters
Parameter | Description | Type | Default |
---|---|---|---|
name | The name of the cookie to clear | string |
|
options | The cookie options. You will need to match the domain and path of the client to clear the cookie. | object |
{} |
Returns the Response instance.
Emits res-set-cookie
res-state-change
Response#clearHeader
Remove a response header.
Signature Response#clearHeader ( name ) : Response
Parameters
Parameter | Description | Type | Default |
---|---|---|---|
name | The name of the header to clear | string |
Returns the Response instance.
Emits res-clear-header
res-state-change
Response#cookie
Set a response cookie.
Signature Response#cookie ( name, value [, options ] ) : Response
Parameters
Parameter | Description | Type | Default |
---|---|---|---|
name | The name of the cookie to set | string |
|
value | The value of the cookie to set. | string |
|
options | The cookie options that will be passed to the cookie package. | object |
{} |
Returns the Response instance.
Emits res-set-cookie
res-state-change
Response#log
Produce a response log event.
Signature Response#log ( [ type, ], message [, details ] ) : Response
Parameters
Parameter | Description | Type | Default |
---|---|---|---|
type | The logged event's category. | string |
'log' |
message | A string message to log. | string |
|
details | A detailed object that contains information that contains information relative to the logged message. This object could be used by log event handlers that want to parse the log data. | object |
{} |
Returns the Response instance.
Emits log
Response#redirect
Redirect to client to a new location.
Signature Response#redirect ( url ) : Response
Parameters
Parameter | Description | Type | Default |
---|---|---|---|
url | The endpoint to redirect the client to. | string |
Returns the Response instance.
Emits res-set-status
res-set-header
res-send
res-state-change
Response#reset
Reset the body, headers, cookies, and status code.
Signature Response#reset ( ) : Response
Parameters None
Returns the Response instance.
Emits response-reset
res-state-change
Response#send
Send the response.
Signature Response#send ( [ body ] ) : Response
Parameters
Parameter | Description | Type | Default |
---|---|---|---|
body | Optionally set the body during send. | any |
Returns the Response instance.
Emits res-send
res-state-change
res-complete
error
Response#sendStatus
Send the response with a status code and status message.
Signature Response#sendStatus ( code ) : Response
Parameters
Parameter | Description | Type | Default |
---|---|---|---|
code | The status code to use to set the status and body. | number |
Returns the Response instance.
Emits res-set-status
res-set-header
res-set-body
res-send
res-complete
res-state-change
error
Response#set
Alias for Response#setHeader.
Response#setHeader
Set a response header.
Signature Response#setHeader ( name, value ) : Response
Parameters
Parameter | Description | Type | Default |
---|---|---|---|
name | The name of the header to set. | string |
|
value | The value of the header to set. | string |
Returns the Response instance.
Emits res-set-header
res-state-change
Response#status
Set the response status code.
Signature Response#status ( code ) : Response
Parameters
Parameter | Description | Type | Default |
---|---|---|---|
code | The status code to set. | number |
Returns the Response instance.
Emits res-set-status
res-state-change
Hooks and Middleware
A hook defines the logic that each request passes through to determine its result. Middleware is a type of hook that runs with the incoming request. Technically you could do anything with the middleware hooks, but it may require rewriting or extending existing functions. To ease development Sans Server enables the use of additional hooks and the ability to create new hooks.
There are two types of hooks:
-
Standard - these hooks are called when there are no errors. They receive three parameters: 1) the Request, 2) the Response, and 3) the next function.
{// do some logic;} -
Error Handling - these hooks are only called when there are errors. They receive four parameters: 1) the Error, 2) the Request, 3) the Response, and 4) the next function.
{// do some logic;}
When you define a hook the number of parameters in your hook function is used to determine if it is for handling errors or not.
Next
Hooks commonly run as a chain of functions, one runs, followed by another, and so on. The next
function is the mechanism used for continuing on to the next hook in line. By calling next()
within your hook you are signifying that the hook is done processing.
If during processing an error occurs you have two options: 1) throw the error (synchronous only) or 2) pass the error to the next
function (synchronous or asynchronous).
{ // do some logic throw Error'Oh no! An error.';}
{ // do some logic ;}
Hook Flow
If you have a hook that produces an error then all non-error handling hooks will be skipped until an error handling hook is found.
const SansServer = ;const sansServer = ; sansServer; sansServer; sansServer; sansServer; /*Console Output:134 */
Routing
Routing is not built into the core of sans-server, but you can add it with sans-server-router.