Athena

Queries and results

How Athena.js reads work across .select() chains, eager .findMany(...) calls, aliases, and typed result inference.

Athena.js exposes two read styles:

  • .select(...) for deferred column-string chains
  • .findMany(...) for eager object-tree reads

Both start from .from(tableName). Use the string surface when a flat select is enough. Use findMany(...) when you want nested relations or clearer typed intent at the callsite.

.select(...) chains

const result = await athena.from("users").select("id, name, email").eq("active", true).limit(25);

if (result.error) {
  console.error(result.error.message);
} else {
  console.table(result.data);
}

select() returns a SelectChain, not a promise. The request executes when you await the chain or terminate it with .single(...) / .maybeSingle(...).

Column lists

Omit the argument for all columns (*). Otherwise pass a comma-separated string or an array:

const { data } = await athena.from("users").select("id, name, email");
const { data: rows } = await athena.from("users").select(["id", "name"]);

Column strings can rename response keys:

const { data } = await athena
  .from("users")
  .select("user_id:id, user_email:email, createdAt:created_at");

See Select column aliases for the full alias rules.

.findMany(...) reads

findMany(...) is the canonical eager read surface for object-based selection trees:

const sections = await athena.from("orchestral_sections").findMany({
  select: {
    name: true,
    instruments: {
      select: {
        name: true,
      },
    },
  },
  where: {
    active: true,
  },
  orderBy: {
    name: "asc",
  },
  limit: 10,
});

Important behavior:

  • findMany(...) executes immediately
  • select, where, orderBy, and limit are compiled into the existing gateway transport by default
  • typed relation inference is strongest on fromModel(...)
  • the current Athena server contract expects the compiled transport, not top-level AST fetch bodies

See findMany AST and server contract for transport and compatibility details.

Single-row helpers

.single(...) and .maybeSingle(...) are read terminators that return one row or null.

const { data: user } = await athena
  .from("users")
  .select("id, name")
  .eq("id", 42)
  .single();

if (user) {
  console.log(user.name);
}

Combine them with sorting when you want the latest or oldest row.

Typing rows and relations

Annotate the row type for inference on data:

interface User {
  id: number;
  name: string;
  email: string;
  active: boolean;
}

const { data } = await athena.from<User>("users").select("id, name");
// data is User[] | null

When you use the typed registry, fromModel(...) can also infer relation output for findMany(...). On plain from<Row>(...) calls, unresolved relation leaves fall back to unknown.

Result shape

All awaitable reads resolve to AthenaResult<T>. The fields you usually care about are:

  • data
  • error
  • status
  • statusText
  • count
  • raw

error is a structured object on failed results, so service code should read result.error.message instead of assuming a plain string.