Skip to content

Handler Context

Every handler function receives two parameters: the Hono context (c) for request/response handling, and the Crude Functions context (ctx) with route metadata and utilities.

export default async function (c, ctx) {
// c = Hono context (request, response helpers)
// ctx = Function context (params, query, secrets, etc.)
return c.json({ message: "Hello" });
}

The ctx parameter provides Crude Functions-specific information and utilities.

Type: RouteInfo

Metadata about the matched route.

PropertyTypeDescription
namestringFunction name from route configuration
descriptionstring | undefinedOptional description
handlerstringPath to the handler file
routestringRoute pattern (e.g., /users/:id)
methodsstring[]Allowed HTTP methods
keysnumber[] | undefinedRequired API key group IDs
export default async function (c, ctx) {
console.log(ctx.route.name); // "get-user"
console.log(ctx.route.handler); // "user.ts"
console.log(ctx.route.methods); // ["GET", "PUT"]
console.log(ctx.route.route); // "/users/:id"
return c.json({ route: ctx.route.name });
}

Type: Record<string, string>

Path parameters extracted from the route pattern.

// Route: /users/:userId/posts/:postId
// URL: /run/users/42/posts/99
export default async function (c, ctx) {
const userId = ctx.params.userId; // "42"
const postId = ctx.params.postId; // "99"
return c.json({ userId, postId });
}

Type: Record<string, string>

Query parameters parsed from the URL.

// URL: /run/search?q=test&page=2
export default async function (c, ctx) {
const searchQuery = ctx.query.q; // "test"
const page = ctx.query.page; // "2"
return c.json({ q: searchQuery, page });
}

Type: string | undefined

The name of the API key group used for authentication. Only set when the route requires API keys and the request was successfully authenticated.

export default async function (c, ctx) {
if (ctx.authenticatedKeyGroup) {
console.log(`Authenticated via: ${ctx.authenticatedKeyGroup}`);
}
return c.json({ group: ctx.authenticatedKeyGroup || "public" });
}

Type: Date

Timestamp when the request was received.

export default async function (c, ctx) {
return c.json({
timestamp: ctx.requestedAt.toISOString()
});
}

Type: string

Unique UUID for request tracing. Useful for logging and debugging.

export default async function (c, ctx) {
console.log(`Processing request ${ctx.requestId}`);
return c.json({ requestId: ctx.requestId });
}

Signature:

getSecret(
name: string,
scope?: "global" | "function" | "group" | "key"
): Promise<string | undefined>

Retrieves a secret value. When called without a scope, secrets are resolved hierarchically from most specific to least specific:

  1. Key - Secret for the authenticated API key
  2. Group - Secret for the authenticated key group
  3. Function - Secret for the current function
  4. Global - Application-wide secret

Returns the first match found, or undefined if no secret exists with that name.

export default async function (c, ctx) {
// Hierarchical resolution (key > group > function > global)
const apiToken = await ctx.getSecret("api_token");
if (!apiToken) {
return c.json({ error: "api_token not configured" }, 500);
}
const response = await fetch("https://api.example.com/data", {
headers: { Authorization: `Bearer ${apiToken}` }
});
return c.json(await response.json());
}

Explicit scope:

export default async function (c, ctx) {
// Only retrieve from a specific scope
const globalConfig = await ctx.getSecret("config", "global");
const functionKey = await ctx.getSecret("api_key", "function");
const groupPassword = await ctx.getSecret("db_password", "group");
const keyToken = await ctx.getSecret("token", "key");
return c.json({ globalConfig, functionKey, groupPassword, keyToken });
}

Signature:

getCompleteSecret(name: string): Promise<{
global?: string;
function?: string;
group?: { value: string; groupId: number; groupName: string };
key?: { value: string; groupId: number; groupName: string; keyId: number; keyName: string };
} | undefined>

Retrieves a secret’s value from all accessible scopes at once, with metadata. Useful for debugging or when you need to know which scope a secret came from.

export default async function (c, ctx) {
const complete = await ctx.getCompleteSecret("db_password");
if (!complete) {
return c.json({ error: "db_password not found in any scope" }, 400);
}
// See which scopes have this secret
return c.json({
hasGlobal: !!complete.global,
hasFunction: !!complete.function,
hasGroup: !!complete.group,
hasKey: !!complete.key,
});
}

The c parameter is Hono’s standard Context object. Here are the most commonly used methods.

MethodDescription
c.json(data, status?)Send JSON response
c.text(text, status?)Send plain text
c.html(html, status?)Send HTML
c.body(body, status?)Send raw body
c.redirect(url, status?)Redirect to another URL (default 302)
// JSON response
return c.json({ message: "success" });
return c.json({ error: "not found" }, 404);
// Text response
return c.text("OK");
return c.text("Not found", 404);
// HTML response
return c.html("<h1>Hello</h1>");
// Redirect
return c.redirect("/other-page");
return c.redirect("https://example.com", 301); // permanent redirect
PropertyTypeDescription
c.req.methodstringHTTP method (GET, POST, etc.)
c.req.urlstringFull request URL
c.req.pathstringRequest path
c.req.rawRequestRaw Request object
c.req.raw.headersHeadersRequest headers
export default async function (c, ctx) {
return c.json({
method: c.req.method, // "GET"
url: c.req.url, // "http://localhost/run/example?foo=bar"
path: c.req.path, // "/run/example"
});
}
MethodReturnsDescription
c.req.param()Record<string, string>All path parameters
c.req.param(key)string | undefinedSingle path parameter
c.req.header(name)string | undefinedHeader value
c.req.json()Promise<any>Parse JSON body
c.req.text()Promise<string>Body as text
c.req.formData()Promise<FormData>Parse form data
export default async function (c, ctx) {
// Headers
const contentType = c.req.header("content-type");
const authHeader = c.req.header("authorization");
// Parse JSON body
const body = await c.req.json();
return c.json({ received: body });
}
ItemTypeDescription
ctx.routeRouteInfoRoute metadata
ctx.paramsRecord<string, string>Path parameters
ctx.queryRecord<string, string>Query parameters
ctx.authenticatedKeyGroupstring | undefinedAuthenticated key group
ctx.requestedAtDateRequest timestamp
ctx.requestIdstringUnique request UUID
ctx.getSecret(name, scope?)Promise<string | undefined>Get secret hierarchically
ctx.getCompleteSecret(name)Promise<object | undefined>Get secret from all scopes
c.json(data, status?)ResponseSend JSON
c.text(text, status?)ResponseSend text
c.html(html, status?)ResponseSend HTML
c.redirect(url, status?)ResponseRedirect to URL
c.req.json()Promise<any>Parse JSON body
c.req.header(name)string | undefinedGet header
export default async function (c, ctx) {
return c.json({
message: "Hello from Crude Functions",
route: ctx.route.name,
requestId: ctx.requestId,
});
}
export default async function (c, ctx) {
try {
const body = await c.req.json();
if (!body.name) {
return c.json({ error: "name is required" }, 400);
}
return c.json({
success: true,
name: body.name,
requestId: ctx.requestId,
});
} catch {
return c.json({
error: "Invalid JSON body",
requestId: ctx.requestId,
}, 400);
}
}
// Route: /users/:id
// URL: /run/users/42?include=email
export default async function (c, ctx) {
const userId = ctx.params.id;
const includeEmail = ctx.query.include === "email";
const user = { id: userId, name: "John" };
if (includeEmail) {
user.email = "john@example.com";
}
return c.json(user);
}
export default async function (c, ctx) {
const apiKey = await ctx.getSecret("external_api_key");
if (!apiKey) {
console.error(`[${ctx.requestId}] Missing external_api_key secret`);
return c.json({ error: "API not configured" }, 500);
}
const response = await fetch("https://api.example.com/data", {
headers: {
"Authorization": `Bearer ${apiKey}`,
"X-Request-ID": ctx.requestId,
},
});
if (!response.ok) {
console.error(`[${ctx.requestId}] External API error: ${response.status}`);
return c.json({ error: "External API error" }, 502);
}
return c.json(await response.json());
}
export default async function (c, ctx) {
// Route is configured to require API keys
// ctx.authenticatedKeyGroup is set if auth succeeded
if (!ctx.authenticatedKeyGroup) {
return c.json({
error: "Authentication required",
requestId: ctx.requestId,
}, 401);
}
// Use group-specific secret
const dbPassword = await ctx.getSecret("db_password", "group");
return c.json({
message: "Authenticated",
group: ctx.authenticatedKeyGroup,
hasDbAccess: !!dbPassword,
});
}