Skip to main content
Wrap an external API by injecting credentials from secrets. The client calls your function, and your function calls the real API with the correct keys. Pattern: route with parameters + authentication injection + response transformation.
index.ts
import { define, z } from "@jelou/functions";

export default define({
  name: "api-proxy",
  description: "Proxy to an external API with injected authentication",
  input: z.object({
    fields: z.array(z.string()).optional().describe("Fields to include in the response"),
  }),
  config: {
    path: "/users/:id",
    methods: ["GET"],
    cors: {
      origin: "https://app.example.com",
      credentials: true,
    },
  },
  handler: async (input, ctx) => {
    const apiKey = ctx.env.get("INTERNAL_API_KEY");
    const baseUrl = ctx.env.get("API_BASE_URL");

    ctx.log("Proxy request", {
      userId: ctx.params.id,
      fields: input.fields,
    });

    const res = await fetch(
      `${baseUrl}/api/users/${ctx.params.id}`,
      { headers: { Authorization: `Bearer ${apiKey}` } },
    );

    if (!res.ok) {
      ctx.log("Upstream error", { status: res.status, userId: ctx.params.id });
      return { error: "not_found", status: res.status };
    }

    const user = await res.json();
    const result: Record<string, unknown> = {
      id: user.id,
      name: user.name,
      email: user.email,
    };

    if (input.fields?.includes("address")) {
      result.address = user.address;
    }
    if (input.fields?.includes("orders")) {
      result.recentOrders = user.orders?.slice(0, 5);
    }

    return result;
  },
});

Local testing

curl "http://localhost:3000/users/42?fields=address,orders"

Why it works this way

  • config.path: "/users/:id" — the :id is captured in ctx.params.id.
  • Credentials are never exposed to the client; they live in secrets.
  • cors.origin restricts which domains can call the proxy from the browser.
  • You can transform or filter the response before returning it.