Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.edgespark.dev/llms.txt

Use this file to discover all available pages before exploring further.

Consistent error handling makes your API predictable for clients and easier to debug. This guide shows patterns that fit the current EdgeSpark scaffold and runtime SDK.

Return a standard error shape

server/src/index.ts
import { db } from "edgespark";
import { auth } from "edgespark/http";
import { eq } from "drizzle-orm";
import { Hono } from "hono";
import { posts } from "@defs";

function err(message: string, details?: unknown) {
  return { error: message, ...(details ? { details } : {}) };
}

const app = new Hono().post("/api/posts", async (c) => {
  let body: { title?: string; content?: string };

  try {
    body = await c.req.json();
  } catch {
    return c.json(err("Request body must be valid JSON"), 400);
  }

  if (!body.title || body.title.trim().length === 0) {
    return c.json(err("Validation failed", { field: "title", issue: "required" }), 422);
  }

  const [post] = await db
    .insert(posts)
    .values({ title: body.title.trim(), content: body.content ?? null, authorId: auth.user.id })
    .returning();

  return c.json(post, 201);
});

export default app;

Handle not found

server/src/index.ts
import { db } from "edgespark";
import { eq } from "drizzle-orm";
import { Hono } from "hono";
import { posts } from "@defs";

const app = new Hono().get("/api/posts/:id", async (c) => {
  const id = Number(c.req.param("id"));

  if (Number.isNaN(id) || id < 1) {
    return c.json({ error: "Invalid ID" }, 400);
  }

  const [post] = await db.select().from(posts).where(eq(posts.id, id));

  if (!post) {
    return c.json({ error: "Post not found" }, 404);
  }

  return c.json(post);
});

export default app;

Catch unexpected errors

server/src/index.ts
import { db } from "edgespark";
import { eq } from "drizzle-orm";
import { Hono } from "hono";
import { posts } from "@defs";

const app = new Hono().get("/api/posts/:id", async (c) => {
  try {
    const id = Number(c.req.param("id"));
    const [post] = await db.select().from(posts).where(eq(posts.id, id));

    if (!post) return c.json({ error: "Not found" }, 404);
    return c.json(post);
  } catch (error) {
    console.error("Failed to fetch post", { id: c.req.param("id"), error });
    return c.json({ error: "Internal server error" }, 500);
  }
});

export default app;

Register global handlers

server/src/index.ts
import { Hono } from "hono";

const app = new Hono();

app.onError((error, c) => {
  console.error("Unhandled error", { path: c.req.path, error });
  return c.json({ error: "Internal server error" }, 500);
});

app.notFound((c) => {
  return c.json({ error: "Not found" }, 404);
});

export default app;

Standard status codes

SituationStatus
Request body invalid JSON400
Missing or invalid parameters422
Not authenticated401
Authenticated but not allowed403
Resource not found404
Success, returns body200
Created successfully201
Success, no body204
Server error500
The platform automatically returns 401 for unauthenticated requests to /api/* routes before your handler runs. You usually only need to handle 403 Forbidden yourself.

See also

Build a REST API

A complete example with validation, auth, uploads, and error handling.

View logs

Read runtime logs and exceptions from your deployed project.
Last modified on April 7, 2026