lmc-api-util
v2.1.1
Published
Stateless helper functions for implementing RESTful APIs in express
Downloads
24
Readme
lmc-api-util
Stateless helper functions for implementing RESTful APIs in express
Usage
const express = require('express');
const api = require('lmc-api-util');
var app = express();
// Call to /api/ok returns status 200 OK and JSON:
// {"result":"OK","message":"You called OK"}
app.get('/api/ok', function(req, res) {
api.respondOk(res, 'You called OK');
});
// Call to /api/thing/338a0bbe-e675-43ae-ac10-0019f1606401 returns status 200 OK and JSON:
// {"result":"OK","message":"Found Thing 123","thing":{"foo":"bar"}}
// Call to /api/thing/bogus returns status 400 Bad Request and JSON:
// {"result":"FAIL","message":"Request parameter \"id\" must be a valid UUID"}
app.get('/api/thing/:id', function(req, res) {
try {
api.validateRequest(req.params, {id: api.VALIDATE_UUID});
api.respondFound(res, 'Thing 123', {thing: {foo: 'bar'}});
} catch(err) {
api.respondBadRequest(res, err.message);
}
});
// Call to /api/thing/notfound returns status 404 Not Found and JSON:
// {"result":"FAIL","message":"Could not find Thing 456"}
app.get('/api/thing/notfound', function(req, res) {
api.respondNotFound(res, 'Thing 456');
});
Installation
Install with npm:
$ npm install lmc-api-util
API
Respond Functions
This group of functions allows for the quick creation of normalized responses to REST API calls. They run the gamut from simple success to server errors, all generating a HTTP response code and a JSON object of the following pattern:
{
"result": "[OK|FAIL]",
"message": "[Friendly Message]",
"someOptionalObject": {}
}
All respond functions take an express
response object as a first parameter
and will throw an Error
if it is invalid.
respondOk(res, message, resultObj)
Creates a response JSON message with result
of OK and a supplied message
.
HTTP status is no explicitly set for this call, so will most likely be 200 OK.
res
(required) - express response objectmessage
(optional) - message object to supply with responseresultObj
(optional) - any members of this object will be passed along in the response. Useful for GET REST requests that return something.
respondFail(res, message, resultObj)
Creates a response JSON message with result
of FAIL and a supplied message
.
HTTP status is no explicitly set for this call, so will most likely be 200 OK.
res
(required) - express response objectmessage
(optional) - message object to supply with responseresultObj
(optional) - any members of this object will be passed along in the response. Useful for sending additional info on the failure.
respondFound(res, itemDesc, resultObj)
Creates a response JSON message with result
of OK and creates a message that
itemDesc
was found. HTTP status is set to 200 OK.
res
(required) - express response objectitemDesc
(optional) - description of what was foundresultObj
(optional) - any members of this object will be passed along in the response. Useful for GET REST requests that return something.
respondFoundZero(res, itemDesc, resultObj)
Creates a response JSON message with result
of OK and creates a message that
zero of itemDesc
were found. HTTP status is set to 200 OK. This function is
most useful for findAll()
type functions where nothing was found, rather than
findById()
type functions.
res
(required) - express response objectitemDesc
(optional) - description of what we were trying to findresultObj
(optional) - any members of this object will be passed along in the response. Useful for GET REST requests that return something.
respondCreated(res, itemDesc, resultObj)
Creates a response JSON message with result
of OK and creates a message that
itemDesc
was created. HTTP status is set to 201 CREATED. Usually want to
return either the ID of the newly created entity or the entity itself.
res
(required) - express response objectitemDesc
(optional) - description of what was createdresultObj
(optional) - any members of this object will be passed along in the response. Useful for GET REST requests that return something.resultObj.location
(optional) - this will set the location URL of the new entity in the header of the response. This is a REST best practice.
respondUpdated(res, itemDesc, resultObj)
Creates a response JSON message with result
of OK and creates a message that
itemDesc
was updated. HTTP status is set to 200 OK. Usually want to
return either the ID of the updated entity or the entity itself.
res
(required) - express response objectitemDesc
(optional) - description of what was updatedresultObj
(optional) - any members of this object will be passed along in the response. Useful for GET REST requests that return something.resultObj.location
(optional) - this will set the location URL of the new entity in the header of the response. This is a REST best practice.
respondDeleted(res, itemDesc, resultObj)
Creates a response JSON message with result
of OK and creates a message that
itemDesc
was deleted. HTTP status is set to 200 OK.
res
(required) - express response objectitemDesc
(optional) - description of what was deletedresultObj
(optional) - any members of this object will be passed along in the response.
respondBadRequest(res, message)
Creates a response JSON message with result
of FAIL and a supplied message
.
HTTP status is set to 400 Bad Request. This is most useful for missing or
malformed information in the request.
res
(required) - express response objectmessage
(optional) - message returned with the response
respondUnauthorized(res, message)
Creates a response JSON message with result
of FAIL and a supplied message. HTTP
status is set to 401 Unauthorized. This is most useful for dealing with
authentication issues.
res
(required) - express response objectmessage
(optional) - message returned with the response
respondForbidden(res, itemDesc)
Creates a response JSON message with result
of FAIL and creates a message that
itemDesc
cannot be accessed by the current user. HTTP status is set to 403
Forbidden. This is most useful for relaying to a user that they attempted to
access something that they do not have permissions to access.
res
(required) - express response objectitemDesc
(optional) - description of what was attempted to be accessed
respondNotFound(res, itemDesc)
Creates a response JSON message with result
of FAIL and creates a message that
itemDesc
was not found. HTTP status is set to 404 Not Found.
res
(required) - express response objectitemDesc
(optional) - description of what was not found
respondServerError(res, message)
Creates a response JSON message with result
of FAIL and a supplied message
.
HTTP status is set to 500 Internal Server Error. This is most useful for
indicating an unexpected failure in the server.
res
(required) - express response objectmessage
(optional) - message returned with the response
Request Functions
This group of functions manages and processes incoming requests. They generate normailzed functions for paging and queries and can validate incoming parameters.
calcPaging(params, options)
Generates a well-defined object that can be used for paging when looking up
data. Handles omitted parameters and calculates offset and limit values. limit
and offset
can also be passed in directly, but will be overriden if page
or
pageSize
are provided.
requestParams
(required) - array or object of provided parameters. Usually can simply passreq.query
. Recognized values are:page
- current page. Defaults to 1pageSize
- numer of objects per page. Defaults to 50, but is limited bymaxPageSize
belowlimit
- can be set directly ifpage
andpageSize
are omittedoffset
- can be set directly ifpage
andpageSize
are omitted_page
- JSON Server style alias forpage
_limit
- JSON Server style alias forpageSize
options
(optional) - other options for paging:maxPageSize
- maximum requestable page size. Defaults to 200
Return a single object with the following values (all as integers):
page
- current page of datapageSize
- number of objects per pageoffset
- number of objects to skipped to get to page of datalimit
- same aspageSize
Example:
const express = require('express');
const api = require('lmc-api-util');
var app = express();
// Call /api/paging returns:
// {"result":"OK","message":"Paging!","page":1,"pageSize":50,"offset":0,"limit":50}
// Call /api/paging?page=3 returns:
// {"result":"OK","message":"Paging!","page":3,"pageSize":50,"offset":100,"limit":50}
// Call /api/paging?page=4&pageSize=100 returns:
// {"result":"OK","message":"Paging!","page":4,"pageSize":100,"offset":300,"limit":100}
// Call /api/paging?_page=4&_limit=100 returns:
// {"result":"OK","message":"Paging!","page":4,"pageSize":100,"offset":300,"limit":100}
// Call /api/paging?page=5&pageSize=999 returns:
// {"result":"OK","message":"Paging!","page":4,"pageSize":200,"offset":400,"limit":200}
// Call /api/paging?limit=5&offset=10 returns:
// {"result":"OK","message":"Paging!","page":2,"pageSize":5,"offset":10,"limit":5}
// Call /api/paging?limit=5&offset=10&page=3&pageSize=12 returns:
// {"result":"OK","message":"Paging!","page":3,"pageSize":12,"offset":24,"limit":12}
app.get('/api/paging', function(req, res) {
const paging = api.calcPaging(req.query);
api.makeOk(res, 'Paging!', paging);
});
validateRequest(requestParams, required)
Checks for existence of all required
parameters in requestParams
. Returns
true if all the required paramaters are present or throws an Error
if any are
missing. Additionally, if required
is passed as an object, type checking will
be applied to the values as well, throwing an Error
if the value is invalid.
The message
parameter of the thrown Error
contains text that can be
directly returned by the API as an error message.
requestParams
(required) - object of provided parameters. Usually can simply passreq.query
orreq.body
required
(required) - if passed an array, it will ensure all values are present as keys inrequestParams
. If passed an object, it will first ensure all the keys of the object are present as keys inrequestParams
, then will do a check for valid types as specified:- VALIDATE_EMAIL - request value must be a valid email address
- VALIDATE_NOT_EMPTY - request value must not be empty or null
- VALIDATE_UUID - request value must be a valid UUID
Example:
const express = require('express');
const api = require('lmc-api-util');
var app = express();
// Call /api/checkrequired?foo=1&bar=2 returns status 200 OK and JSON:
// {"result":"OK","message":"Parameters OK"}
// Call /api/checkrequired?bar=2 returns status 400 Bad Request and JSON:
// {"result":"FAIL","message":"Missing required parameter: foo"}
// Call /api/checkrequired returns status 400 Bad Request and JSON:
// {"result":"FAIL","message":"Missing required parameters: foo, bar"}
app.get('/api/checkrequired', function(req, res) {
try {
api.validateRequest(req.query, ['foo', 'bar']);
api.makeOk(res, 'Parameters OK');
} catch(err) {
api.respondBadRequest(res, err.message);
}
});
// Call /api/[email protected]&bar=not+empty&baz=354245a5-ab13-41ff-8245-1271b5662eff returns status 200 OK and JSON:
// {"result":"OK","message":"Parameters OK"}
// Call /api/validate?bar=not+empty&baz=354245a5-ab13-41ff-8245-1271b5662eff returns status 400 Bad Request and JSON:
// {"result":"FAIL","message":"Missing required parameter: foo"}
// Call /api/[email protected]&bar=not+empty&baz=bad-uuid returns status 400 Bad Request and JSON:
// {"result":"FAIL","message":"Request parameter \"baz\" must be a valid UUID"}
// Call /api/[email protected]&bar=&baz=bad-uuid returns status 400 Bad Request and JSON:
// {"result":"FAIL","message":"Request parameter \"bar\" must not be empty"}
app.get('/api/validate', function(req, res) {
try {
api.validateRequest(req.query, {
foo: api.VALIDATE_EMAIL,
bar: api.VALIDATE_NOT_EMPTY,
baz: api.VALIDATE_UUID
});
api.makeOk(res, 'Parameters OK');
} catch(err) {
api.respondBadRequest(res, err.message);
}
});
Object Functions
At times it is more helpful to load a full dataset into memory and page/filter/sort the objects in memory. This group of functions provides helpers to manage this in a reliable way.
filterObjects(allObjs, attributes, requestParams)
Returns a subset of allObjs
that match filters defined by requestParams
.
Any key in requestParams
that starts with filter|
and exists in the
attributes
array will be used to check each object in allObjs
. Any object
that matches will be returned.
allObjs
(required) - full array of objects to be filteredattributes
(optional) - array of attribute names to check on objects. If null, will use any attribute associated with the objectsrequestParams
(required) - object of parameters as part of the request. Any parameter that starts withfilter|
will be used to filter the object list, checking againstattributes
and the matching value
pageObjects(allObjs, paging)
Takes a paging
object generated from calcPaging()
and pages the full array
allObjs
, returning only the correct page of objects. This also modifies the
paging
object, adding total
for the total objects and hasMore
if there
are more objects to be paged. This funcion should be called after filtering
and sorting.
allObjs
(required) - full array of objects to be pagedpaging
(required) - paging object generated fromcalcPaging()
searchObjects(allObjs, attributes, requestParams)
Returns a subset of allObjs
that match a case-insenitive search term defined
in requestParams
. If the key search
exists in requestParams
then each object
in allObjs
will be checked to see if any of the attributes as listed in
attributes
array contain the string defined in search
. Any object
that matches will be returned. attributes
can be deep object paths, allowing
for searches within subobjects.
allObjs
(required) - full array of objects to be searchedattributes
(required) - array of attribute names to check on objectsrequestParams
(required) - object of parameters as part of the request. Only the parametersearch
will be used to search the object list, checking if any of theattributes
contain thesearch
string.
sortObjects(allObjs, attributes, requestParams)
Returns a reordered array of allObjs
based on parameters in requestParams
.
Any key in requestParams
that starts with sort|
and exists in the
attributes
array will be used to sort the objects. The value assigned in the
sort|
parameter should be asc
or desc
to specify ascending or descending order.
In the case that you want to sort by multiple fields, you can append a number after the
asc
or desc
to specify the application of the ordering.
allObjs
(required) - full array of objects to be sortedattributes
(required) - array of attribute names to check on objectsrequestParams
(required) - object of parameters as part of the request. Any parameter that starts withsort|
will be used to sort the object list, checking againstattributes
and usingasc
ordesc
in the value to specify sort order.