Popsicle Status Middleware
Popsicle Status is a middleware for the `popsicle` HTTP client library, designed to automatically reject HTTP responses with undesirable status codes. It provides a simple function, `status(min?: number, max?: number)`, which returns a middleware that validates a response's status code against a specified inclusive `min` and exclusive `max` range. By default, it rejects any status outside the 200-399 range (i.e., anything not a 2xx or 3xx success/redirection code). The current stable version is `3.0.0`. This library is actively maintained, with releases primarily aligning with major updates to its peer dependencies, `popsicle` and `servie`, to ensure compatibility. Its key differentiator is its seamless integration into the `popsicle` middleware chain, simplifying error handling by transforming invalid HTTP responses into thrown errors, making control flow clearer than manual status checks.
Common errors
-
TypeError: Cannot read properties of undefined (reading 'url') or The middleware function signature is incorrect.
cause Using `popsicle-status@2.x` or higher with an incompatible version of `popsicle` (e.g., `popsicle@5.x`), leading to a mismatch in the middleware function signature.fixUpdate `popsicle` to `^6.0.0` or downgrade `popsicle-status` to `1.x` to match your `popsicle` version. -
Error: Peer dependency 'servie' is not met. Required: '^4.0.0'
cause `popsicle-status@3.x` requires `servie@^4.0.0` as a peer dependency, but an older or incompatible version is installed or missing.fixInstall or update `servie` to a compatible version: `npm install servie@^4.0.0` or `yarn add servie@^4.0.0`. -
UnhandledPromiseRejectionWarning: HttpError: Bad Request (400)
cause Code written for `popsicle-status` versions prior to `0.2.1` that expected rejected promises for bad statuses, but `0.2.1` and later throw errors directly, leading to unhandled rejections if not caught.fixWrap your `popsicle` requests using `popsicle-status` middleware in `try/catch` blocks to explicitly handle the thrown `HttpError`.
Warnings
- breaking Version 3.0.0 introduced a refactor for 'Servie 4' compatibility. This means users of `popsicle-status` will need to ensure their `servie` peer dependency is at version 4.x.
- breaking Version 2.0.0 updated the middleware signature to align with `popsicle@6.x`. Middleware functions now expect a different signature, which will break compatibility with older `popsicle` versions (e.g., `5.x` or earlier).
- breaking Version 0.2.1 changed the error handling mechanism from resolving with a `Promise` to explicitly throwing an error. Code that previously awaited a rejected promise might now require `try/catch` blocks.
- gotcha Since version 2.0.1, the error object thrown by `popsicle-status` now includes a `res` property, providing direct access to the `Response` object that caused the error. This is not a breaking change but alters the error object's structure.
Install
-
npm install popsicle-status -
yarn add popsicle-status -
pnpm add popsicle-status
Imports
- status
const { status } = require('popsicle-status');import { status } from 'popsicle-status'; - Middleware
import { Middleware } from 'popsicle-status';import type { Middleware } from 'popsicle';
Quickstart
import { status } from 'popsicle-status';
import { Request, Response } from 'popsicle'; // Assuming popsicle types are available
async function main() {
const statusMiddleware = status(200, 300); // Configure to only allow 2xx responses
// Mock popsicle's internal `send` function for demonstration
const mockSend = async (req: Request): Promise<Response> => {
if (req.url === 'https://example.com/success') {
return { url: req.url, status: 200, statusText: 'OK', headers: {}, body: 'Success!', clone: () => ({ ...this }) } as Response;
}
if (req.url === 'https://example.com/bad-request') {
return { url: req.url, status: 400, statusText: 'Bad Request', headers: {}, body: 'Bad', clone: () => ({ ...this }) } as Response;
}
throw new Error('Unexpected request URL');
};
// Create a dummy request object
const createRequest = (url: string): Request => ({
url,
method: 'GET',
headers: {},
body: null,
clone: () => ({ ...this })
} as Request);
console.log('--- Testing a successful request (200 OK) ---');
try {
const reqSuccess = createRequest('https://example.com/success');
const resSuccess = await statusMiddleware(reqSuccess, mockSend);
console.log(`Success! Status: ${resSuccess.status}, Body: ${await resSuccess.body}`);
} catch (err: any) {
console.error(`Unexpected error for success: ${err.message}`);
}
console.log('\n--- Testing a failing request (400 Bad Request) ---');
try {
const reqFail = createRequest('https://example.com/bad-request');
const resFail = await statusMiddleware(reqFail, mockSend); // This should throw due to status filter
console.log(`Unexpected success for failure: Status: ${resFail.status}`);
} catch (err: any) {
console.error(`Caught expected error: ${err.message}`);
if (err.res) {
console.log(`Error includes response object. Status: ${err.res.status}`);
}
}
}
main();