pipio
About
Pipio helps developers compose complex business logic from reusable blocks of code (functions) in sync and async manner.
Installation
To install this package, run the command below.
# npm
npm install pipio
# yarn
yarn add pipio
# pnpm
pnpm add pipio
Usage
CommonJS
const { pipio } = require('./pipio');
const handler = pipio(async () => {
// bootstrap application
return { serviceName: 'S1', logger: console.log };
})
// step 1
.use(async ({ serviceName, logger }) => {
logger(`${serviceName} is called`);
return { serviceName, logger, result: { id: 123, title: 'Todo 1' } };
})
// step 2
.use(async ({ serviceName, logger, result }) => {
logger(`${serviceName} is called`);
return { status: 200, body: result }
})
.build({
onError: (err) => {
if (err instanceof BadRequestError) {
return {
status: 400,
body: 'ValidationFailed'
}
}
return {
status: 500,
body: 'Failed'
}
},
});
ESModule (TypeScript/ES6+)
import { pipio } from './pipio';
const handler = pipio(async () => {
// bootstrap application
return { serviceName: 'S1', logger: console.log };
})
// step 1
.use(async ({ serviceName, logger }) => {
logger(`${serviceName} is called`);
return { serviceName, logger, result: { id: 123, title: 'Todo 1' } };
})
// step 2
.use(async ({ serviceName, logger, result }) => {
logger(`${serviceName} is called`);
return { status: 200, body: result }
})
.build({
onError: (err) => {
if (err instanceof BadRequestError) {
return {
status: 400,
body: 'ValidationFailed'
}
}
return {
status: 500,
body: 'Failed'
}
},
});
Examples
Example 1
Without pipio, your handler would be like this.
- Unnecessary variable declaration
- Complicated error handling (e.g. multiple early returns, not accessing to variables due to variable scope in a try/catch block)
- Not testable
- Hard to maintain
const handler = async (event, context) => {
// initialize application (1-time operation)
const { validator, database, notification } = await bootstrap();
// parse JSON
const parsedResponse = JSON.parse(event.body);
// validate input
if (validator.isValid(parsedResponse) === false) {
return {
status: 400,
};
}
// save in database
const savedItem = await database.save(parsedResponse);
// notify others
await notification.send({ event: 'item.created', item: savedItem });
// map response
return {
status: 200,
body: savedItem,
};
};
With pipio, your handler would be like this
const handler = pipio(lambdaWrapper())
// bootstrap
.use(async (opts) => {
// destructure the opts parameter
const { event, context } = opts;
// initialize application (1-time operation)
const { validator, database, notification } = await bootstrap();
return { event, context, validator, database, notification };
})
// parse JSON
.use(async (opts) => {
try {
// parse JSON
const parsedResponse = JSON.parse(event.body);
} catch (err) {
throw new Error('MalformedJSON');
}
return { ...opts, parsedResponse };
})
// validate input
.use(async (opts) => {
// destructure the opts parameter
const { event, parsedResponse, validator } = opts;
if (validator.isValid(parsedResponse) === false) {
throw new Error('BadRequest');
}
return opts;
})
// save in database
.use(async (opts) => {
// destructure the opts parameter
const { database, parsedResponse } = opts;
// save in database
const savedItem = await database.save(parsedResponse);
return { ...opts, savedItem };
})
// notify others
.use(async (opts) => {
// destructure the opts parameter
const { notification, savedItem } = opts;
// notify others
await notification.send({ event: 'item.created', item: savedItem });
return opts;
})
// map response
.use(async (opts) => {
// destructure the opts parameter
const { savedItem } = opts;
return {
status: 200,
body: savedItem,
};
})
// convert to handler
.build({
onError: (err) => {},
});
And you're good to go!
License
MIT