Part of running a reliable API Gateway is ensuring that the service is healthy
and available. With Zuplo, it is easy to setup a health check endpoint that can
be used to ensure the health of both your Zuplo Gateway and your backend (as
well as the connectivity between them).
A typical health check endpoint on your API Gateway will excersise some of the
most important policies (such as authentication) and then make a simple request
to your backend to ensure that it is reachable and functioning.
Setting up a Health Check Handler
To set up a health check handler, you will need to create a new route in your
Zuplo Gateway. This route will be used to handle health check requests.
You'll create a new route with the path /health and the method GET. In your
OpenAPI file this would look like:
config/routes.oas.json
{ "paths": { "/health": { "get": { "summary": "Health Check", "description": "Checks the health of the API Gateway and backend.", "responses": { "200": { "description": "OK" }, "503": { "description": "Service Unavailable" } }, "x-zuplo-route": { "corsPolicy": "anything-goes", "handler": { "export": "default", "module": "$import(./modules/health)" }, "policies": { "inbound": ["my-auth-policy"] } } } } }}
Next, you'll create a custom handler module that will be used to process the
health check requests. This module will make a simple request to your backend to
ensure that it is reachable and functioning. If you have multiple backend
services you can check each of them.
modules/health.ts
import { ZuploContext, ZuploRequest, ZuploResponse } from "@zuplo/runtime";export default async function handler( request: ZuploRequest, context: ZuploContext,): Promise<ZuploResponse> { try { // Make a simple request to the backend to check its health const responses = await Promise.allSettled([ fetch("https://backend-1.example.com/health", { method: "GET", headers: { "Content-Type": "application/json", }, }), fetch("https://backend-2.example.com/health", { method: "GET", headers: { "Content-Type": "application/json", }, }), ]); const backendResponse = responses.find( (res) => res.status === "fulfilled", ) as PromiseFulfilledResult<Response> | undefined; if (!backendResponse) { context.log.error("All backend health checks failed", { responses }); return new ZuploResponse("Service Unavailable", { status: 503 }); } else { const failedResponses = responses.filter( (res) => res.status === "rejected", ) as PromiseRejectedResult[]; context.log.error("Backend health check failed", { statuses: failedResponses.map((res) => res.reason.status), statusText: failedResponses.map((res) => res.reason.statusText), }); return new ZuploResponse("Service Unavailable", { status: 503 }); } } catch (error) { context.log.error("Error during health check", { error }); return new ZuploResponse("Service Unavailable", { status: 503 }); }}