
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
An express middleware for better data and error handling per route.
Each pond corresponds with a way of handling data and errors for a set of your endpoints, and are supposed to handle the response for two scenarios: success and error.
Let's define a couple ponds for two different API response formats:
File: ponds-setup.js
import ponds from 'ponds';
ponds.set('api_1', {
data(data, req, res) {
res.json({
status: 'success',
data: data
})
},
error(err, req, res) {
res.json({
status: 'error',
error: err
})
}
});
ponds.set('api_2', {
data(data, req, res) {
res.json({
data: data,
error: null
})
},
error(err, req, res) {
res.json({
data: null,
error: err
})
}
});
Once defined, we can use them in our routes:
File: app.js
import express from 'express';
import ponds from 'ponds';
import routes from './routes';
import './ponds-setup';
const app = express();
// By default, ponds includes a 404 handler that ships a
// NotFound PublicError (see errors and PublicError)
app.use(routes, ponds.get('api_2'));
// In this case, as we're not doing app.use() for a set of routes,
// we can't have the NotFound error, so we passe `false`as a second param.
app.get('/myRoute', (req, res, next) => {
// Instead of sending the response,
// we send our data to next().
// If it's an error, it'll be handled by our error handler;
// otherwise it'll go through our data handler
next({ some: 'data', foo: 'else' });
}, ponds.get('api_1', false));
Hence, if we send a get request to /myRoute, as it has a next pond api_1, we'd get: { error: null, data: { some: 'data', foo: 'else' } }.
File: routes.js
import { Router } from 'express';
const router = Router();
router.get('/routes/foo', (req, res, next) => {
// Instead of sending the response,
// we send our data to next().
// If it's an error, it'll be handled by our error handler;
// otherwise it'll go through our data handler
next({ other: 'data', foo: 'else' });
})
export default router;
Hence, as all routes in routes.js have a next pond api_2 middleware, if we send a get request to /routes/foo, we'd get: { status: 'success', data: { other: 'data', foo: 'else' } }.
ponds.set(name, handler)Sets a pond handler.
name: string, the name of the handler.handler: object, with keys:
data: function, with signature (data, req, res):
data: any, the data sent to next by the controller.req: object, an express request object.res: object, an express response object.error: function, with signature (error, req, res):
error: Error, an error sent to next by the controller.req: object, an express request object.res: object, an express response object.import ponds from 'ponds';
ponds.set('api_1', {
data(data, req, res) {
res.json({
status: 'success',
data: data
})
},
error(err, req, res) {
res.json({
status: 'error',
error: err
})
}
});
ponds.get(name, notFound?)Returns a previously set handler as an express middleware.
name: string, the name of the handler.notFound: boolean, optional. If true, ponds.get() will return an array, its first element being a handler that will next() a NotFound PublicError; if false, it will return a single final handler for the next()'ed data/error. Default: true.import ponds from 'ponds';
app.get('/myRoute', (req, res, next) => {
next({ some: 'data', foo: 'else' });
}, ponds.get('api_1', false));
ponds.exists(name)Returns true if a pond has been set, false otherwise.
name: string, the name of the handler.import ponds from 'ponds';
ponds.exists('api_1'); // true
ponds.exists('foo_pond'); // false
ponds.transform(transform)Sets a data/error transform that will execute before the next()'ed data is received by any pond handler. It's particularly useful to reformat errors from different libraries to a PublicError in order for them to be handled by the pond error handler.
transform: object, with keys:
data: function, optional, receives and should return any data.error: function, optional, receives and should return an error.import ponds, { PublicError, errors } from 'ponds';
ponds.transform({
error(err) {
if (err instanceof SomeDbLibError) {
return new PublicError(errors.Database, { err });
}
return err;
}
});
dispatch(cb)Wraps a middleware function sending to next() any returned data and catching any thrown errors (also sent to next()).
cb: function, with signature (req, res):
req: object, an express request object.res: object, an express response object.import ponds, { dispatch, PublicError, errors } from 'ponds';
const controller = dispatch((req, res) => {
if (!req.body.somethingRequired) {
throw new PublicError(
errors.RequestValidation,
{ info: `Request didn't have "somethingRequired".` }
);
}
return {
some: 'data',
foo: 'else'
};
});
app.get('/myRoute', controller, ponds.get('api_1', false));
dispatch.all(obj)Same as dispatch() for an object of functions.
obj: object, with any number of keys and values of functions, with signature (req, res):
req: object, an express request object.res: object, an express response object.import ponds, { dispatch, PublicError, errors } from 'ponds';
const controllers = dispatch.all({
myRoute(req, res) {
if (!req.body.somethingRequired) {
throw new PublicError(
errors.RequestValidation,
{ info: `Request didn't have "somethingRequired".` }
);
}
return {
some: 'data',
foo: 'more data'
};
},
otherRoute(req, res) {
return { some: 'other', foo: 'else' };
}
});
app.get('/myRoute', controller.myRoute, ponds.get('api_1', false));
app.get('/otherRoute', controllers.otherRoute, ponds.get('api_1', false));
This library includes an Error type to handle additional information regarding the status code, information, and stack.
PublicErrornew PublicError(type, additional?)
type: object, with keys (see the predefined errors types below):
id: string.message: string.status: number.additional: object, optional, with keys:
info: any, optional, provides any additional information to be accessed via the instance.info property.err: Error, optional, provides the original error, of any kind, a PublicError had as a cause.Properties
id: string.pascalId: string, same as id but replacing any letter following _ for it's uppercase variant. Example: for some_id, it's pascalId would be someId.message: string.status: number.info: any.child: Error, the error passed as additional.err to the PublicError instance, if any.first: PublicError, the first (bottom) error that is an instance of PublicError when following the child chain.Because you can store the origin errors a PublicError had as a cause (which can be another PublicError), it'd be possible to throw several PublicErrors in a chain, and access the first via the last thrown.
import { dispatch, PublicError, errors } from 'ponds';
async function service() {
// let's assume something happened
throw Error('Something happened');
}
async function dbQuery() {
try {
return service();
} catch(err) {
throw new PublicError(errors.Database, { err });
}
}
async function controller() {
try {
dbQuery()
} catch(err) {
throw new PublicError(errors.Server, { err });
}
}
try {
controller();
} catch(err) {
// `err` would be a Server PublicError
// `err.first` would be a Database PublicError
// `err.first.child` would be an Error with message "Something happened"
}
errorsponds exports an object with some predefined error types:
Server: { id: 'server', message: 'Server error', status: 500 },NotFound: { id: 'not_found', message: 'Server Not Found', status: 404 },Unauthorized: { id: 'unauthorized', message: "You don't have access to this resource", status: 401 },RequestValidation: { id: 'request_validation', message: 'Invalid request', status: 400 },Database: { id: 'database', message: 'Database error', status: 500 },DatabaseValidation: { id: 'database_validation', message: 'Invalid database request', status: 500 },DatabaseNotFound: { id: 'database_not_found', message: 'Item not found in database', status: 500 }import { PublicError, errors } from 'ponds';
new PublicError(errors.Server, { info: 'Some additional information' });
FAQs
An express middleware for better data and error handling per route
The npm package ponds receives a total of 1 weekly downloads. As such, ponds popularity was classified as not popular.
We found that ponds demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer 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
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.