Runtime Extensions
While most configuration in your Zuplo gateway is set on a per-route or
per-policy basis, there are times when behaviors need to be modified globally.
To plug into the global initialization of your gateway, create a file called
zuplo.runtime.ts
in the modules
folder with the following code.
Any error thrown in the runtimeInit
method will prevent the gateway from
starting and yield a 500 error for all requests. Be sure to add only reliable
code here and use try/catch
as appropriate to handle any recoverable
exceptions.
import { RuntimeExtensions } from "@zuplo/runtime";
export function runtimeInit(runtime: RuntimeExtensions) {
// Extensions go here
}
The name of the export must be runtimeInit
and must conform to the above
function signature.
The following configurations are available.
Custom Problem (Error) Response Formatter#
Zuplo includes built-in error handling that returns errors in the format of the Problem Details for HTTP APIs proposed standard. This means that HTTP errors (or other exceptions) will return responses that look like the following.
{
"type": "https://httpproblems.com/http-status/404",
"title": "Not Found",
"status": 404,
"detail": "Not Found",
"instance": "/not-a-path",
"trace": {
"timestamp": "2023-03-14T15:49:38.581Z",
"requestId": "05968b6d-6f82-4ae3-8e13-f92e0d0499c5",
"buildId": "a9b200a3-734c-413a-a1ae-ce171d53e5a7",
"rayId": "7a7daaf3bac2f325"
}
}
If you want to customize this format, you can configure the
problemResponseFormat
function and return a Response
in the format of your
choice.
import { RuntimeExtensions } from "@zuplo/runtime";
export function runtimeInit(runtime: RuntimeExtensions) {
runtime.problemResponseFormat = (
{ problem, statusText, additionalHeaders },
request,
context,
) => {
// Build the response body
const body = JSON.stringify(problem, null, 2);
// Send the response with headers and status
return new Response(body, {
status: problem.status,
statusText,
headers: {
...additionalHeaders,
"content-type": "application/problem+json",
},
});
};
}
Hook: OnResponseSending#
The OnResponseSending
hook allows modification of the Response
immediately
before it is sent to the client. The hook provides the Request
and Response
and returns a Response
. To modify the outgoing response create and return a
new Response()
.
The example below shows modifying the response for a specific path.
import { RuntimeExtensions } from "@zuplo/runtime";
export function runtimeInit(runtime: RuntimeExtensions) {
runtime.addResponseSendingHook(async (response, latestRequest, context) => {
const url = new URL(request.url);
if (url.pathname === "/path-to-change-response") {
return new Response("New Response", response);
}
return response;
});
}
Hook: Context OnResponseSendingFinal#
The OnResponseSendingFinal
hook on ZuploContext
fires immediately after the
response is sent to the client. The Response
in this hook is immutable and the
body has been used. This hook is useful for custom performing various tasks like
logging or analytics.
import { ZuploContext, ZuploRequest } from "@zuplo/runtime";
export async function pluginWithHook(
request: ZuploRequest,
context: ZuploContext,
policyName: string,
) {
const cloned = request.clone();
context.addResponseSendingFinalHook(
async (response, latestRequest, context) => {
const body = await cloned.text();
await fetch("https://example.com", {
method: "GET",
body,
});
},
);
return request;
}
Plugin and Handler Extensions#
Built-in and custom plugins and handlers can expose their own extensibility. The AWS Lambda handler exposes the ability to customize the event that is sent when invoking the Lambda function.
The example below shows how to use a route's custom property to set the path on the outgoing event to a custom value.
import { AwsLambdaHandlerExtensions, RuntimeExtensions } from "@zuplo/runtime";
export function runtimeInit(runtime: RuntimeExtensions) {
AwsLambdaHandlerExtensions.addSendingAwsLambdaEventHook(
async (request, context, event: AwsLambdaEventV1) => {
event.path = context.custom.lambdaPath ?? event.path;
return event;
},
);
}