Security News
Bun 1.2 Released with 90% Node.js Compatibility and Built-in S3 Object Support
Bun 1.2 enhances its JavaScript runtime with 90% Node.js compatibility, built-in S3 and Postgres support, HTML Imports, and faster, cloud-first performance.
Influential's Functional, Promise-based Express Middleware
npm install jigawatt --save
First, require the module.
const JW = require('jigawatt')
For this walkthrough, we'll also be using Ramda.
const R = require('ramda')
For this example, we have a poll and poll responses stored in a database.
Jigawatt middleware functions, such as getPollResults
in this case, are created to sanitize, validate, and/or normalize the incoming data (awesomize
), then use that awesomized data to query a database (io
), and lastly, format the results to our liking and return the output (transform
).
const getPollResults = {
awesomize: (v) => ({
pollId : {
read : R.path([ 'params', 'pollId' ])
, sanitize : [ R.toLower ]
, validate : [ v.required ]
}
})
, io: (req, data) => ({
poll : db.Poll.findOne({ _id: data.pollId })
, responses : db.Vote.find({ poll_id: data.pollId })
})
, transform: (req, data) => ({
poll : data.poll.title
, results : tallyVotes(
getCity(data.poll)
, getAnswer(data.responses)
)
})
}
Our Jigawatt middleware can then handle a given Express route. Consider the following endpoint:
...
app.get('/poll/:pollId', JW(getPollResults))
Example output:
{
"poll": "What is your favorite city?",
"results": [ { "Juneau": 2 }
, { "Vladivostok": 3 }
, { "Redding": 1 }
, { "Wilmington": 0 }
, { "Galveston": 2 }
]
}
This might be a bit overwhelming at first sight, so let's break it down in a bit more detail...
Jigawatt Middleware expects at least one of the following three properties:
awesomize :: Validator -> Request -> Object
Awesomize has four components, all of which are optional: read -> sanitize -> validate -> normalize
The read
component has access to the entire object passed to the awesomize function. Here, we'll use Ramda to target a specific value of the object passed:
awesomize: (v) => ({
id : {
read : R.path([ 'order', 'id' ])
...
sanitize
is an awesomize component that can manipulate the data before it is validated.
awesomize: (v) => ({
id : {
read : R.path([ 'order', 'id' ])
, sanitize : [ R.drop(2) ]
...
validate
is a validator that is passed to our function as v
. Awesomize's validate
component has a few built-in validator methods such as required
, isInt
, isFunction
, etc...
awesomize: (v) => ({
id : {
read : R.path([ 'order', 'id' ])
, validate : [ v.required, v.isInt ]
...
We can also chain validation methods, as seen above. For more info on awesomize validators, visit the documentation.
As a last note about validate
, we can create our own custom validator functions as well:
const isCorrectLength = (str) => R.equals(24, str.length)
...
awesomize: (v) => ({
pollId : {
sanitize : [ R.toLower ]
, validate : [ isCorrectLength ]
}
...
normalize
is the last awesomize component, called after the data has been validated.
awesomize: (v) => ({
id : {
read : R.path([ 'order', 'id' ])
, validate : [ v.required, v.isInt ]
, normalize : [ R.inc ]
}
})
A complete awesomize function can awesomize more than one value:
awesomize: (v) => ({
id : {
read : R.path([ 'order', 'id' ])
, validate : [ v.required, v.isInt ]
}
, product : { sanitize : [ R.trim ] }
, customer : { validate : [ v.required ] }
})
io :: Request -> Data -> Object
io
's primary use is to fetch data using the information passed to it from the awesomize function. In the example below, we have io
making two calls to two separate database tables. Once resolved, io
will pass the data fetched from the database along to the transform
method.
io: (req, data) => ({
poll : db.Poll.findOne({ _id: data.pollId })
, responses : db.Vote.find({ poll_id: data.pollId })
})
transform :: Request -> Data -> Object
transform
is used to structure the incoming data in a unique way. Remember that transform
is optional, and if omitted, the Jigawatt middleware simply returns the raw results.
const getAnswer = (arr) => R.compose(
R.map(R.dec)
, R.pluck('answer')
)(arr)
const getCity = (obj) => R.map(
R.replace(/\,.*$/, '')
, obj.questions
)
const tallyVotes = (options, responses) => {
// Tally total vote for a specific city
return R.map((str) => {
let ind = R.indexOf(str, options)
let votes = R.compose(
R.length
, R.filter(R.equals(ind))
)(responses)
return R.assoc(str, votes, {})
}, options)
}
...
transform: (req, data) => ({
poll : data.poll.title
, results : tallyVotes(
getCity(data.poll)
, getAnswer(data.responses)
)
})
Our Jigawatt middleware was used to take an incoming ID, query two separate database tables for that ID, and format the results to our liking. The final output of our Jigawatt middleware looks like this:
{
"poll": "What is your favorite city?",
"results": [ { "Juneau": 2 }
, { "Vladivostok": 3 }
, { "Redding": 1 }
, { "Wilmington": 0 }
, { "Galveston": 2 }
]
}
If you would like to use a Jigawatt middleware as a promise, you can use the JW.promisify
method:
const getSingleVote = {
awesomize: (v) => ...
, io: (req, data) => ...
, transform: (req, data) => ...
}
...
const voteDetails = JW.promisify(getSingleVote)
...
voteDetails(data).then((result) => // do with the data what you will
JW.pipe
can be used to chain multiple Jigawatt middleware together into a single unit:
const combinedJigawatts = JW.pipe(getPollResults, getSingleVote, ...)
app.get('/poll/:pollId', JW(combinedJigawatts))
JW.branch
should be given a predicate, and two Jigawatt middlewares. If the predicate function returns true, the first Jigawatt middleware is called. If false, the latter is called:
const fetchUserDetails = JW(
JW.branch(
isAdmin // Predicate
, showAllData // Called if predicate returns true
, showMinimalData // Called if predicate returns false
)
)
If you find issues with Jigawatt, we encourage you to open an issue ticket on the issues page. Please open a ticket on the issues page before submitting any pull requests!
MIT © Influential, Nathan Sculli
FAQs
Influential's Functional, Promise-based Express Middleware
We found that jigawatt demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 5 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Bun 1.2 enhances its JavaScript runtime with 90% Node.js compatibility, built-in S3 and Postgres support, HTML Imports, and faster, cloud-first performance.
Security News
Biden's executive order pushes for AI-driven cybersecurity, software supply chain transparency, and stronger protections for federal and open source systems.
Security News
Fluent Assertions is facing backlash after dropping the Apache license for a commercial model, leaving users blindsided and questioning contributor rights.