astro
Version:
Astro is a modern site builder with web best practices, performance, and DX front-of-mind.
89 lines (88 loc) • 2.96 kB
JavaScript
import { AstroError, AstroErrorData } from "../errors/index.js";
/**
* Utility function that is in charge of calling the middleware.
*
* It accepts a `R` generic, which usually is the `Response` returned.
* It is a generic because endpoints can return a different payload.
*
* When calling a middleware, we provide a `next` function, this function might or
* might not be called.
*
* A middleware, to behave correctly, can:
* - return a `Response`;
* - call `next`;
*
* Failing doing so will result an error. A middleware can call `next` and do not return a
* response. A middleware cannot call `next` and return a new `Response` from scratch (maybe with a redirect).
*
* ```js
* const onRequest = async (context, next) => {
* const response = await next(context);
* return response;
* }
* ```
*
* ```js
* const onRequest = async (context, next) => {
* context.locals = "foo";
* next();
* }
* ```
*
* @param onRequest The function called which accepts a `context` and a `resolve` function
* @param apiContext The API context
* @param responseFunction A callback function that should return a promise with the response
*/
export async function callMiddleware(onRequest, apiContext, responseFunction) {
let nextCalled = false;
let responseFunctionPromise = undefined;
const next = async (payload) => {
nextCalled = true;
responseFunctionPromise = responseFunction(apiContext, payload);
// We need to pass the APIContext pass to `callMiddleware` because it can be mutated across middleware functions
return responseFunctionPromise;
};
const middlewarePromise = onRequest(apiContext, next);
return await Promise.resolve(middlewarePromise).then(async (value) => {
// first we check if `next` was called
if (nextCalled) {
/**
* Then we check if a value is returned. If so, we need to return the value returned by the
* middleware.
* e.g.
* ```js
* const response = await next();
* const new Response(null, { status: 500, headers: response.headers });
* ```
*/
if (typeof value !== "undefined") {
if (value instanceof Response === false) {
throw new AstroError(AstroErrorData.MiddlewareNotAResponse);
}
return value;
} else {
/**
* Here we handle the case where `next` was called and returned nothing.
*/
if (responseFunctionPromise) {
return responseFunctionPromise;
} else {
throw new AstroError(AstroErrorData.MiddlewareNotAResponse);
}
}
} else if (typeof value === "undefined") {
/**
* There might be cases where `next` isn't called and the middleware **must** return
* something.
*
* If not thing is returned, then we raise an Astro error.
*/
throw new AstroError(AstroErrorData.MiddlewareNoDataOrNextCalled);
} else if (value instanceof Response === false) {
throw new AstroError(AstroErrorData.MiddlewareNotAResponse);
} else {
// Middleware did not call resolve and returned a value
return value;
}
});
}