Athena.js Snippet Catalog
Exhaustive code-snippet catalog imported from athena-js source documentation.
This page aggregates snippet blocks from xylex-group/athena-js documentation sources so every example is available in one place.
Total snippets imported: 159.
Sources included: README.md, CONTRIBUTING.md, docs/**/*.md(x), and test-sdk/**/*.md(x) from the athena-js repository.
Context7 _autodocs
Source: Context7 library /xylex-group/athena-js (autogenerated reference corpus excerpts)
AthenaAuthSdkClient Interface
interface AthenaAuthSdkClient {
signIn: {
email(input: AthenaEmailSignInRequest): Promise<AthenaAuthResult<AthenaAuthSignInResponse>>
username(input: AthenaUsernameSignInRequest): Promise<AthenaAuthResult<AthenaAuthSignInResponse>>
social(input: AthenaSocialSignInRequest): Promise<AthenaAuthResult<AthenaAuthSignInResponse | AthenaAuthSocialRedirectResponse>>
}
signUp: {
email(input: AthenaEmailSignUpRequest): Promise<AthenaAuthResult<AthenaAuthSignInResponse>>
}
session: {
get(): Promise<AthenaAuthResult<AthenaAuthSessionResponse>>
list(): Promise<AthenaAuthResult<AthenaAuthSession[]>>
}
signOut(): Promise<AthenaAuthResult<AthenaAuthSignOutResponse>>
password: {
forget(input: AthenaForgetPasswordRequest): Promise<AthenaAuthResult<void>>
reset(input: AthenaResetPasswordRequest): Promise<AthenaAuthResult<void>>
}
email: {
verify(input: AthenaVerifyEmailRequest): Promise<AthenaAuthResult<void>>
change(input: AthenaChangeEmailRequest): Promise<AthenaAuthResult<void>>
sendVerification(input: AthenaSendVerificationEmailRequest): Promise<AthenaAuthResult<void>>
}
user: {
update(input: AthenaUpdateUserRequest): Promise<AthenaAuthResult<AthenaAuthUser>>
delete(input: AthenaDeleteUserRequest): Promise<AthenaAuthResult<void>>
}
account: {
link(input: AthenaLinkSocialRequest): Promise<AthenaAuthResult<AthenaAuthLinkedAccount>>
unlink(input: AthenaUnlinkAccountRequest): Promise<AthenaAuthResult<void>>
list(): Promise<AthenaAuthResult<AthenaAuthLinkedAccount[]>>
token(input: AthenaOAuthAccountTokenRequest): Promise<AthenaAuthResult<AthenaOAuthTokenBundle>>
}
refresh: {
token(): Promise<AthenaAuthResult<{ token: string }>>
}
}TypedAthenaClient Interface
interface TypedAthenaClient<TRegistry extends RegistryConstraint, TTenantMap extends TenantKeyMap = Record<never, string>> extends AthenaSdkClient {
readonly registry: TRegistry
readonly tenantKeyMap: Readonly<TTenantMap>
readonly tenantContext: TenantContext<TTenantMap>
fromModel<TDatabase extends keyof TRegistry & string, TSchema extends keyof TRegistry[TDatabase]['schemas'] & string, TModel extends keyof TRegistry[TDatabase]['schemas'][TSchema]['models'] & string>(database: TDatabase, schema: TSchema, model: TModel): TableQueryBuilder<RowOf<ModelAt<TRegistry, TDatabase, TSchema, TModel>>>
withTenantContext(context: TenantContext<TTenantMap>): TypedAthenaClient<TRegistry, TTenantMap>
}Query Runtime Exports
## Query Runtime Exports
### Description
Core components and hooks for client-side querying with Athena.
### Available Exports
- `AthenaQueryClient`
- `createAthenaQueryClient`
- `attachStateAdapter`
- `AthenaQueryClientProvider`
- `useAthenaQueryClient`
- `useQuery`
- `useMutation`CONTRIBUTING.md
Source: https://github.com/xylex-group/athena-js/blob/main/CONTRIBUTING.md
Development setup (1)
git clone https://github.com/xylex-group/athena-js
cd athena-js
npm install
npm run buildProject structure (2)
athena-js/
├── src/
│ ├── gateway/ # HTTP client, React hook, types
│ └── supabase.ts # Athena query builder
├── docs/
└── test/Validation checks (3)
pnpm typecheck
pnpm check:alldocs/api-reference.md
Source: https://github.com/xylex-group/athena-js/blob/main/docs/api-reference.md
Core result contract (1)
interface AthenaResult<T> {
data: T | null
error: string | null
errorDetails?: AthenaGatewayErrorDetails | null
status: number
count?: number | null
raw: unknown
}createClient(url, apiKey, options?) (2)
function createClient(
url: string,
apiKey: string,
options?: Pick<AthenaGatewayCallOptions, "client" | "headers" | "backend"> & {
auth?: AthenaAuthClientConfig
},
): AthenaSdkClientWithAuthAthenaClient.builder() (3)
interface AthenaClientBuilder {
url(url: string): AthenaClientBuilder
key(apiKey: string): AthenaClientBuilder
backend(backend: BackendConfig | BackendType): AthenaClientBuilder
client(clientName: string): AthenaClientBuilder
headers(headers: Record<string, string>): AthenaClientBuilder
healthTracking(enabled: boolean): AthenaClientBuilder
build(): AthenaSdkClientWithAuth
}Backend constants (4)
const Backend = {
Athena: { type: "athena" },
Postgrest: { type: "postgrest" },
PostgreSQL: { type: "postgresql" },
ScyllaDB: { type: "scylladb" },
} as const;Backend constants (5)
type BackendType = "athena" | "postgrest" | "postgresql" | "scylladb"AthenaSdkClient (6)
interface AthenaSdkClient {
from<Row = Record<string, AthenaJsonValue | undefined>, Insert = Partial<Row>, Update = Partial<Insert>>(
table: string,
): TableQueryBuilder<Row, Insert, Update>
rpc<Row = unknown, Args extends AthenaJsonObject = AthenaJsonObject>(
fn: string,
args?: Args,
options?: AthenaRpcCallOptions,
): RpcQueryBuilder<Row>
query<Row = unknown>(
query: string,
options?: AthenaGatewayCallOptions,
): Promise<AthenaResult<Row[]>>
}
interface AthenaSdkClientWithAuth extends AthenaSdkClient {
auth: AthenaAuthBindings
}TableQueryBuilder<Row, Insert, Update> (7)
interface TableQueryBuilder<Row, Insert = Partial<Row>, Update = Partial<Insert>> {
select<T = Row>(columns?: string | string[], options?: AthenaGatewayCallOptions): SelectChain<Row, T>
insert(values: Insert, options?: AthenaGatewayCallOptions): MutationQuery<Row>
insert(values: Insert[], options?: AthenaGatewayCallOptions): MutationQuery<Row[]>
upsert(
values: Insert,
options?: AthenaGatewayCallOptions & {
updateBody?: Update
onConflict?: string | string[]
},
): MutationQuery<Row>
upsert(
values: Insert[],
options?: AthenaGatewayCallOptions & {
updateBody?: Update
onConflict?: string | string[]
},
): MutationQuery<Row[]>
update(values: Update, options?: AthenaGatewayCallOptions): UpdateChain<Row>
delete(options?: AthenaGatewayCallOptions & { resourceId?: string }): MutationQuery<Row | null>
single<T = Row>(columns?: string | string[], options?: AthenaGatewayCallOptions): Promise<AthenaResult<T | null>>
maybeSingle<T = Row>(columns?: string | string[], options?: AthenaGatewayCallOptions): Promise<AthenaResult<T | null>>
reset(): TableQueryBuilder<Row, Insert, Update>
}SelectChain<Row, SelectedRow = Row> (8)
interface SelectChain<Row, SelectedRow = Row>
extends PromiseLike<AthenaResult<SelectedRow[]>> {
single<T = SelectedRow>(
columns?: string | string[],
options?: AthenaGatewayCallOptions,
): Promise<AthenaResult<T | null>>
maybeSingle<T = SelectedRow>(
columns?: string | string[],
options?: AthenaGatewayCallOptions,
): Promise<AthenaResult<T | null>>
}MutationQuery<Result> (9)
interface MutationQuery<Result> extends PromiseLike<AthenaResult<Result>> {
select(columns?: string | string[], options?: AthenaGatewayCallOptions): Promise<AthenaResult<Result>>
returning(columns?: string | string[], options?: AthenaGatewayCallOptions): Promise<AthenaResult<Result>>
single(
columns?: string | string[],
options?: AthenaGatewayCallOptions,
): Promise<AthenaResult<Result extends Array<infer Item> ? Item | null : Result | null>>
maybeSingle(
columns?: string | string[],
options?: AthenaGatewayCallOptions,
): Promise<AthenaResult<Result extends Array<infer Item> ? Item | null : Result | null>>
}UpdateChain<Row> (10)
interface UpdateChain<Row> extends MutationQuery<Row[]> {}RpcQueryBuilder<Row> (11)
interface RpcQueryBuilder<Row> extends PromiseLike<AthenaResult<Row[]>> {
eq(column: string, value: AthenaConditionValue): RpcQueryBuilder<Row>
neq(column: string, value: AthenaConditionValue): RpcQueryBuilder<Row>
gt(column: string, value: AthenaConditionValue): RpcQueryBuilder<Row>
gte(column: string, value: AthenaConditionValue): RpcQueryBuilder<Row>
lt(column: string, value: AthenaConditionValue): RpcQueryBuilder<Row>
lte(column: string, value: AthenaConditionValue): RpcQueryBuilder<Row>
like(column: string, value: AthenaConditionValue): RpcQueryBuilder<Row>
ilike(column: string, value: AthenaConditionValue): RpcQueryBuilder<Row>
is(column: string, value: AthenaConditionValue): RpcQueryBuilder<Row>
in(column: string, values: AthenaConditionArrayValue): RpcQueryBuilder<Row>
order(column: string, options?: { ascending?: boolean }): RpcQueryBuilder<Row>
limit(count: number): RpcQueryBuilder<Row>
offset(count: number): RpcQueryBuilder<Row>
range(from: number, to: number): RpcQueryBuilder<Row>
select(columns?: string | string[], options?: AthenaRpcCallOptions): Promise<AthenaResult<Row[]>>
single<T = Row>(columns?: string | string[], options?: AthenaRpcCallOptions): Promise<AthenaResult<T | null>>
maybeSingle<T = Row>(columns?: string | string[], options?: AthenaRpcCallOptions): Promise<AthenaResult<T | null>>
}JSON-safe primitives (12)
type AthenaJsonPrimitive = string | number | boolean | null
type AthenaJsonValue = AthenaJsonPrimitive | AthenaJsonObject | AthenaJsonArray
interface AthenaJsonObject {
[key: string]: AthenaJsonValue
}
type AthenaJsonArray = AthenaJsonValue[]Condition primitives (13)
type AthenaConditionValue = AthenaJsonPrimitive
type AthenaConditionArrayValue = Array<AthenaConditionValue>
type AthenaConditionCastType = stringAthenaGatewayCallOptions (14)
interface AthenaGatewayCallOptions {
baseUrl?: string
apiKey?: string
client?: string
backend?: BackendConfig | BackendType
publishEvent?: string
headers?: Record<string, string>
userId?: string | null
organizationId?: string | null
schema?: string
count?: "exact" | "planned" | "estimated"
head?: boolean
defaultToNull?: boolean
stripNulls?: boolean
onConflict?: string | string[]
updateBody?: AthenaJsonObject
}AthenaRpcCallOptions (15)
interface AthenaRpcCallOptions extends AthenaGatewayCallOptions {
count?: "exact" | "planned" | "estimated"
get?: boolean
}Fetch payload (16)
interface AthenaFetchPayload {
view_name?: string
table_name?: string
columns?: string[] | string
conditions?: AthenaGatewayCondition[]
limit?: number
offset?: number
current_page?: number
page_size?: number
total_pages?: number
strip_nulls?: boolean
group_by?: string
time_granularity?: "day" | "hour" | "minute"
aggregation_column?: string
aggregation_strategy?: "cumulative_sum"
aggregation_dedup?: boolean
sort_by?: AthenaSortBy
}Insert payload (17)
interface AthenaInsertPayload<TInsertBody = AthenaJsonObject, TUpdateBody = AthenaJsonObject> {
table_name: string
insert_body: TInsertBody | TInsertBody[]
update_body?: TUpdateBody
columns?: string[] | string
count?: "exact" | "planned" | "estimated"
head?: boolean
default_to_null?: boolean
on_conflict?: string | string[]
}Update payload (18)
interface AthenaUpdatePayload<TUpdateBody = AthenaJsonObject> extends AthenaFetchPayload {
set?: TUpdateBody
data?: TUpdateBody
}Delete payload (19)
interface AthenaDeletePayload {
table_name: string
resource_id?: string
columns?: string[] | string
conditions?: AthenaGatewayCondition[]
sort_by?: AthenaSortBy
current_page?: number
page_size?: number
total_pages?: number
}RPC payload (20)
interface AthenaRpcPayload<TArgs = AthenaJsonObject> {
function: string
function_name?: string
schema?: string
args?: TArgs
select?: string
filters?: AthenaRpcFilter[]
count?: "exact" | "planned" | "estimated"
head?: boolean
limit?: number
offset?: number
order?: AthenaRpcOrder
}Query payload (21)
interface AthenaQueryPayload {
query: string
}Gateway response/error contracts (22)
interface AthenaGatewayResponse<T = unknown> {
ok: boolean
status: number
data: T | null
count?: number | null
error?: string
errorDetails?: AthenaGatewayErrorDetails | null
raw: unknown
}
type AthenaGatewayErrorCode =
| "NETWORK_ERROR"
| "HTTP_ERROR"
| "INVALID_JSON"
| "UNKNOWN_ERROR"
interface AthenaGatewayErrorDetails {
code: AthenaGatewayErrorCode
message: string
status: number
endpoint?: "/gateway/fetch" | "/gateway/insert" | "/gateway/update" | "/gateway/delete" | "/gateway/rpc" | "/gateway/query" | `/rpc/${string}`
method?: "GET" | "POST" | "PUT" | "DELETE"
requestId?: string
hint?: string
cause?: string
}Error classification primitives (23)
type AthenaErrorKind = "unique_violation" | "not_found" | "validation" | "auth" | "rate_limit" | "transient" | "unknown"
type AthenaErrorCode =
| "UNIQUE_VIOLATION"
| "NOT_FOUND"
| "VALIDATION_FAILED"
| "AUTH_UNAUTHORIZED"
| "AUTH_FORBIDDEN"
| "RATE_LIMITED"
| "NETWORK_UNAVAILABLE"
| "TRANSIENT_FAILURE"
| "HTTP_FAILURE"
| "UNKNOWN"
type AthenaErrorCategory = "transport" | "client" | "server" | "database" | "unknown"Model declarations (24)
function defineModel<Row, Insert = Partial<Row>, Update = Partial<Insert>, Meta extends ModelMetadata<Row> = ModelMetadata<Row>>(
input: { meta: Meta },
): ModelDef<Row, Insert, Update, Meta>
function defineSchema<Models extends Record<string, AnyModelDef>>(models: Models): SchemaDef<Models>
function defineDatabase<Schemas extends Record<string, SchemaDef<Record<string, AnyModelDef>>>>(schemas: Schemas): DatabaseDef<Schemas>
function defineRegistry<Databases extends Record<string, DatabaseDef<Record<string, SchemaDef<Record<string, AnyModelDef>>>>>>(databases: Databases): RegistryDef<Databases>Typed client (25)
interface TypedClientOptions<TMap extends TenantKeyMap = TenantKeyMap>
extends Pick<AthenaGatewayCallOptions, "backend" | "client" | "headers"> {
tenantKeyMap?: TMap
tenantContext?: TenantContext<TMap>
}
interface TypedAthenaClient<TRegistry, TTenantMap> extends AthenaSdkClient {
readonly registry: TRegistry
readonly tenantKeyMap: Readonly<TTenantMap>
readonly tenantContext: TenantContext<TTenantMap>
withTenantContext(context: TenantContext<TTenantMap>): TypedAthenaClient<TRegistry, TTenantMap>
fromModel<
TDatabase extends keyof TRegistry & string,
TSchema extends keyof TRegistry[TDatabase]["schemas"] & string,
TModel extends keyof TRegistry[TDatabase]["schemas"][TSchema]["models"] & string,
>(
database: TDatabase,
schema: TSchema,
model: TModel,
): TableQueryBuilder<
RowOf<ModelAt<TRegistry, TDatabase, TSchema, TModel>>,
InsertOf<ModelAt<TRegistry, TDatabase, TSchema, TModel>>,
UpdateOf<ModelAt<TRegistry, TDatabase, TSchema, TModel>>
>
}
function createTypedClient(registry, url, apiKey, options?): TypedAthenaClientGenerator API (26)
interface AthenaGeneratorConfig {
provider: GeneratorProviderConfig
output: GeneratorOutputConfig
naming?: Partial<GeneratorNamingConfig>
features?: Partial<GeneratorFeatureFlags>
experimental?: Partial<GeneratorExperimentalFlags>
}Gateway hook (27)
useAthenaGateway(config?: AthenaGatewayHookConfig): AthenaGatewayHookResultValidation commands (28)
pnpm typecheck
pnpm check:allCommand line usage (29)
athena-js generate --dry-rundocs/auth-client-bindings.md
Source: https://github.com/xylex-group/athena-js/blob/main/docs/auth-client-bindings.md
Athena Auth Client Bindings (1)
type AthenaAuthResult<T> = {
ok: boolean
status: number
data: T | null
error: string | null
errorDetails?: AthenaAuthErrorDetails | null
raw: unknown
}Client setup (2)
import { createClient } from "@xylex-group/athena"
const client = createClient("http://localhost:3001", "gateway_api_key", {
auth: { baseUrl: "http://localhost:3001/api/auth" },
})React useSession parity (3)
import { useSession } from "@xylex-group/athena/react"
import { createClient } from "@xylex-group/athena"
const client = createClient("http://localhost:3001", "gateway_api_key", {
auth: { baseUrl: "http://localhost:3001/api/auth" },
})
function SessionPanel() {
const { data, isPending, isRefetching, error, refetch } = useSession(client)
return null
}docs/auth/admin.mdx
Source: https://github.com/xylex-group/athena-js/blob/main/docs/auth/admin.mdx
Examples (1)
import { client } from "./auth-client"
await client.auth.admin.role.set({
userId: "usr_1",
role: "admin",
})
await client.auth.admin.user.list()
await client.auth.admin.user.session.list({ userId: "usr_1" })
await client.auth.admin.user.create({
email: "new-user@example.com",
password: "temporary-password",
})
await client.auth.admin.user.unban({ userId: "usr_1" })
await client.auth.admin.user.ban({ userId: "usr_2", banReason: "policy violation" })
await client.auth.admin.user.impersonate({ userId: "usr_3" })
await client.auth.admin.user.stopImpersonating({ userId: "usr_3" })
// single payload -> /admin/revoke-user-session
await client.auth.admin.user.session.revoke({
userId: "usr_3",
sessionToken: "sess_token_1",
})
// multiple payloads -> /admin/revoke-user-sessions
await client.auth.admin.user.session.revoke([
{ userId: "usr_3", sessionToken: "sess_token_2" },
{ userId: "usr_3", sessionToken: "sess_token_3" },
])
// direct plural payload -> /admin/revoke-user-sessions
await client.auth.admin.user.session.revoke({
userId: "usr_3",
})
await client.auth.admin.user.remove({ userId: "usr_4" })
await client.auth.admin.user.setPassword({
userId: "usr_4",
newPassword: "new-password",
})
await client.auth.admin.hasPermission({
permissions: { users: ["manage"] },
})
await client.auth.admin.apiKey.create({
name: "admin-key",
expiresIn: 3600,
})
await client.auth.admin.athenaClient.create({
clientName: "dashboard-client",
})
await client.auth.admin.athenaClient.list()
await client.auth.admin.auditLog.list()
await client.auth.admin.email.get({
query: { id: "email_1" },
})
await client.auth.admin.email.create({
recipientEmail: "to@example.com",
subject: "Welcome",
fromAddress: "no-reply@example.com",
provider: "resend",
})
await client.auth.admin.email.update({
id: "email_1",
subject: "Welcome Updated",
})
await client.auth.admin.email.delete({
id: "email_1",
})
await client.auth.admin.email.failure.list()
await client.auth.admin.email.failure.get({
query: { id: "failure_1" },
})
await client.auth.admin.email.failure.create({
recipientEmail: "to@example.com",
flow: "transactional",
errorMessage: "bounce",
})
await client.auth.admin.email.failure.update({
id: "failure_1",
resolved: true,
})
await client.auth.admin.email.failure.delete({
id: "failure_1",
})
await client.auth.admin.email.list()
await client.auth.admin.email.template.create({
templateKey: "welcome",
subjectTemplate: "Welcome",
})
await client.auth.admin.email.template.get({
query: { id: "tmpl_1" },
})
await client.auth.admin.email.template.list()
await client.auth.admin.email.template.update({
id: "tmpl_1",
subjectTemplate: "Welcome Updated",
})
await client.auth.admin.email.template.delete({ id: "tmpl_1" })
// legacy alias
await client.auth.admin.emailTemplate.create({
templateKey: "legacy",
subjectTemplate: "Legacy",
})
await client.auth.admin.emailTemplate.get({
query: { id: "legacy_tmpl_1" },
})
await client.auth.admin.emailTemplate.list()
await client.auth.admin.emailTemplate.update({
id: "legacy_tmpl_1",
subjectTemplate: "Legacy Updated",
})
await client.auth.admin.emailTemplate.delete({ id: "legacy_tmpl_1" })docs/auth/api-key.mdx
Source: https://github.com/xylex-group/athena-js/blob/main/docs/auth/api-key.mdx
Examples (1)
import { client } from "./auth-client"
await client.auth.apiKey.create({
name: "mobile-client-key",
expiresIn: "3600",
remaining: "1000",
})
await client.auth.apiKey.get({
query: { id: "api_key_id" },
})
await client.auth.apiKey.update({
keyId: "api_key_id",
name: "mobile-client-key-updated",
expiresIn: "3600",
permissions: "{}",
})
await client.auth.apiKey.delete({ keyId: "api_key_id" })
await client.auth.apiKey.list()
await client.auth.apiKey.verify({
key: "prefix.secret",
})
await client.auth.apiKey.deleteAllExpired()docs/auth/callback.mdx
Source: https://github.com/xylex-group/athena-js/blob/main/docs/auth/callback.mdx
Example (1)
import { client } from "./auth-client"
await client.auth.callback.provider({
provider: "github",
code: "oauth_authorization_code",
state: "oauth_state_value",
})docs/auth/index.mdx
Source: https://github.com/xylex-group/athena-js/blob/main/docs/auth/index.mdx
Base setup (1)
import { createClient } from "@xylex-group/athena"
export const client = createClient("http://localhost:3001", "gateway_api_key", {
auth: { baseUrl: "http://localhost:3001/api/auth" },
})docs/auth/organization-invitations.mdx
Source: https://github.com/xylex-group/athena-js/blob/main/docs/auth/organization-invitations.mdx
Examples (1)
import { client } from "./auth-client"
await client.auth.organization.invitation.cancel({
invitationId: "inv_1",
})
await client.auth.organization.invitation.accept({
invitationId: "inv_1",
})
await client.auth.organization.invitation.get({
query: { id: "inv_1" },
})
await client.auth.organization.invitation.reject({
invitationId: "inv_1",
})
await client.auth.organization.invitation.list({
query: { organizationId: "org_1" },
})docs/auth/organization-members.mdx
Source: https://github.com/xylex-group/athena-js/blob/main/docs/auth/organization-members.mdx
Examples (1)
import { client } from "./auth-client"
await client.auth.organization.member.remove({
memberIdOrEmail: "member@example.com",
})
await client.auth.organization.member.updateRole({
memberId: "member_1",
role: "admin",
})
await client.auth.organization.member.invite({
email: "new-member@example.com",
role: "member",
})
await client.auth.organization.member.getActive()
await client.auth.organization.member.list({
query: {
organizationId: "org_1",
limit: 50,
sortBy: "createdAt",
sortDirection: "desc",
},
})docs/auth/organization.mdx
Source: https://github.com/xylex-group/athena-js/blob/main/docs/auth/organization.mdx
Examples (1)
import { client } from "./auth-client"
await client.auth.organization.create({
name: "Acme",
slug: "acme",
})
await client.auth.organization.update({
organizationId: "org_1",
data: { name: "Acme Updated" },
})
await client.auth.organization.delete({
organizationId: "org_1",
})
await client.auth.organization.setActive({
organizationId: "org_1",
})
await client.auth.organization.list()
await client.auth.organization.getFull({
query: { organizationId: "org_1" },
})
await client.auth.organization.checkSlug({
slug: "acme",
})
await client.auth.organization.leave({
organizationId: "org_1",
})
await client.auth.organization.listUserInvitations()
await client.auth.organization.hasPermission({
permissions: { project: ["create"] },
})docs/auth/passkey.mdx
Source: https://github.com/xylex-group/athena-js/blob/main/docs/auth/passkey.mdx
Examples (1)
import { client } from "./auth-client"
await client.auth.passkey.generateRegisterOptions()
await client.auth.passkey.generateAuthenticateOptions()
await client.auth.passkey.verifyRegistration({
response: "webauthn-registration-response",
})
await client.auth.passkey.verifyAuthentication({
response: "webauthn-authentication-response",
})
await client.auth.passkey.listUserPasskeys()
await client.auth.passkey.deletePasskey({ id: "passkey_id" })
await client.auth.passkey.updatePasskey({
id: "passkey_id",
name: "My Laptop Passkey",
})
await client.auth.passkey.getRelatedOrigins()docs/auth/session.mdx
Source: https://github.com/xylex-group/athena-js/blob/main/docs/auth/session.mdx
Examples (1)
import { client } from "./auth-client"
const session = await client.auth.getSession()
const signOut = await client.auth.signOut()
const sessions = await client.auth.session.list()
// single -> /revoke-session
await client.auth.session.revoke({ token: "session_token_1" })
// array length 1 -> /revoke-session
await client.auth.session.revoke([{ token: "session_token_2" }])
// array length > 1 -> /revoke-sessions
await client.auth.session.revoke([
{ token: "session_token_3" },
{ token: "session_token_4" },
])
// tokens list length > 1 -> /revoke-sessions
await client.auth.session.revoke({ tokens: ["session_token_5", "session_token_6"] })
const revokeOther = await client.auth.session.revokeOther()docs/auth/sign-in-up.mdx
Source: https://github.com/xylex-group/athena-js/blob/main/docs/auth/sign-in-up.mdx
Examples (1)
import { client } from "./auth-client"
await client.auth.signIn.social({
provider: "google",
callbackURL: "https://app.example.com/auth/callback",
})
await client.auth.signIn.email({
email: "user@example.com",
password: "password",
rememberMe: true,
})
await client.auth.signIn.username({
username: "user1",
password: "password",
rememberMe: true,
})
await client.auth.signUp.email({
name: "New User",
email: "new-user@example.com",
password: "password",
callbackURL: "https://app.example.com/onboarding",
})docs/auth/social-account.mdx
Source: https://github.com/xylex-group/athena-js/blob/main/docs/auth/social-account.mdx
Examples (1)
import { client } from "./auth-client"
await client.auth.social.link({
provider: "google",
callbackURL: "https://app.example.com/auth/callback",
})
await client.auth.account.list()
await client.auth.account.unlink({
providerId: "google",
accountId: "acc_123",
})
await client.auth.deleteUser.callback({
token: "delete_callback_token",
callbackURL: "https://app.example.com/delete-callback",
})
await client.auth.refreshToken({
providerId: "google",
accountId: "acc_123",
userId: "usr_123",
})
await client.auth.getAccessToken({
providerId: "google",
accountId: "acc_123",
userId: "usr_123",
})
await client.auth.health()
await client.auth.ok()
await client.auth.error()docs/auth/two-factor.mdx
Source: https://github.com/xylex-group/athena-js/blob/main/docs/auth/two-factor.mdx
Examples (1)
import { client } from "./auth-client"
await client.auth.twoFactor.getTotpUri({ password: "current-password" })
await client.auth.twoFactor.verifyTotp({ code: "123456" })
await client.auth.twoFactor.sendOtp()
await client.auth.twoFactor.verifyOtp({ code: "123456" })
await client.auth.twoFactor.verifyBackupCode({ code: "backup-code-value" })
await client.auth.twoFactor.generateBackupCodes({
password: "current-password",
})
await client.auth.twoFactor.enable({ password: "current-password" })
await client.auth.twoFactor.disable({ password: "current-password" })docs/auth/use-session.mdx
Source: https://github.com/xylex-group/athena-js/blob/main/docs/auth/use-session.mdx
Hook API (1)
import { useSession } from "@xylex-group/athena/react"
import { createClient } from "@xylex-group/athena"
const client = createClient("http://localhost:3001", "gateway_api_key", {
auth: { baseUrl: "http://localhost:3001/api/auth" },
})
const session = useSession(client, {
enabled: true,
refetchOnMount: true,
fetchInput: undefined,
callOptions: undefined,
})Example component (2)
import { createClient } from "@xylex-group/athena"
import { useSession } from "@xylex-group/athena/react"
const client = createClient("http://localhost:3001", "gateway_api_key", {
auth: { baseUrl: "http://localhost:3001/api/auth" },
})
export function SessionPanel() {
const { data, error, isPending, isRefetching, refetch } = useSession(client)
if (isPending) return <div>Loading session...</div>
if (error) return <div>Session error: {error.message}</div>
return (
<div>
<div>User: {data?.user.email ?? "none"}</div>
<button disabled={isRefetching} onClick={() => void refetch()}>
{isRefetching ? "Refreshing..." : "Refetch session"}
</button>
</div>
)
}docs/auth/user.mdx
Source: https://github.com/xylex-group/athena-js/blob/main/docs/auth/user.mdx
Examples (1)
import { client } from "./auth-client"
await client.auth.forgetPassword({
email: "user@example.com",
redirectTo: "https://app.example.com/reset-password",
})
await client.auth.resetPassword({
newPassword: "new-strong-password",
token: "reset_token",
})
await client.auth.resetPassword.token({
token: "reset_token",
callbackURL: "https://app.example.com/reset-password",
})
await client.auth.setPassword({ newPassword: "new-strong-password" })
await client.auth.verifyEmail({
token: "verify_token",
callbackURL: "https://app.example.com/verified",
})
await client.auth.sendVerificationEmail({
email: "user@example.com",
callbackURL: "https://app.example.com/verify",
})
await client.auth.changeEmail({
newEmail: "new-user@example.com",
callbackURL: "https://app.example.com/change-email",
})
await client.auth.changeEmailVerify({
query: { token: "change_email_token" },
})
await client.auth.deleteUserVerify({
query: { token: "delete_user_token" },
})
await client.auth.changePassword({
currentPassword: "old-password",
newPassword: "new-password",
revokeOtherSessions: true,
})
await client.auth.user.update({
name: "Updated Name",
image: "https://cdn.example.com/avatar.png",
})
await client.auth.user.delete({
password: "current-password",
})
await client.auth.user.email.list()docs/cli-command-reference.md
Source: https://github.com/xylex-group/athena-js/blob/main/docs/cli-command-reference.md
Root commands (1)
athena-js
athena-js --help
athena-js -h
athena-js help
athena-js help generateGenerate command (2)
athena-js generate
athena-js generate --dry-run
athena-js generate --config ./athena.config.ts
athena-js generate --config ./athena.config.ts --dry-run
athena-js generate --helpLocal vs global execution (3)
pnpm exec athena-js generate --dry-runLocal vs global execution (4)
pnpm add -g @xylex-group/athenaWindows global shim troubleshooting (5)
Get-Command athena-js | Format-List Source,DefinitionWindows global shim troubleshooting (6)
pnpm remove -g @xylex-group/athena
pnpm add -g @xylex-group/athenaWindows global shim troubleshooting (7)
athena-js --help
athena-js generate --helpERR_MODULE_NOT_FOUND for dist/cli/index.js (8)
Cannot find module .../@xylex-group/athena/dist/cli/index.jsPostgres 3D000 (database "<name>" does not exist) (9)
PostgreSQL database "app_db" does not exist (code 3D000).Debugging mode (10)
$env:ATHENA_JS_DEBUG="1"
athena-js generate --config ./athena.config.ts --dry-rundocs/generator-cicd.md
Source: https://github.com/xylex-group/athena-js/blob/main/docs/generator-cicd.md
Athena JS Generator CI/CD Guide (1)
athena-js generate [--config <path>] [--dry-run]Pipeline pattern 1: direct pg_url introspection (2)
export default {
provider: {
kind: "postgres",
mode: "direct",
connectionString: process.env.PG_URL!,
database: "app_db",
schemas: ["public", "athena"],
},
output: {
targets: {
model: "athena/models/{schema_kebab}/{model_kebab}.ts",
schema: "athena/schemas/{schema_kebab}.ts",
database: "athena/relations.ts",
registry: "athena/config.ts",
},
placeholderMap: {},
},
};Pipeline pattern 1: direct pg_url introspection (3)
name: generator-direct
on:
pull_request:
branches: [main]
jobs:
generate:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: pnpm install --frozen-lockfile
# dry-run verification (no files written)
- name: Verify generator (dry-run)
env:
PG_URL: ${{ secrets.GENERATOR_PG_URL }}
run: pnpm exec athena-js generate --config ./athena.config.ts --dry-run
# optional write + diff gate for PRs that include generated artifacts
- name: Enforce generated artifacts are committed
env:
PG_URL: ${{ secrets.GENERATOR_PG_URL }}
run: |
pnpm exec athena-js generate --config ./athena.config.ts
git diff --exit-codePipeline pattern 2: gateway-only introspection via Athena /gateway/query (4)
export default {
provider: {
kind: "postgres",
mode: "gateway",
gatewayUrl: process.env.ATHENA_URL!,
apiKey: process.env.ATHENA_API_KEY!,
database: "app_db",
schemas: ["public", "athena"],
backend: "postgresql",
},
output: {
targets: {
model: "athena/models/{schema_kebab}/{model_kebab}.ts",
schema: "athena/schemas/{schema_kebab}.ts",
database: "athena/relations.ts",
registry: "athena/config.ts",
},
placeholderMap: {},
},
};Pipeline pattern 2: gateway-only introspection via Athena /gateway/query (5)
name: generator-gateway
on:
pull_request:
branches: [main]
jobs:
generate:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: pnpm install --frozen-lockfile
# dry-run verification (no files written)
- name: Verify generator (dry-run)
env:
ATHENA_URL: ${{ secrets.ATHENA_GENERATOR_URL }}
ATHENA_API_KEY: ${{ secrets.ATHENA_GENERATOR_API_KEY }}
run: pnpm exec athena-js generate --config ./athena.config.ts --dry-run
# optional write + diff gate for PRs that include generated artifacts
- name: Enforce generated artifacts are committed
env:
ATHENA_URL: ${{ secrets.ATHENA_GENERATOR_URL }}
ATHENA_API_KEY: ${{ secrets.ATHENA_GENERATOR_API_KEY }}
run: |
pnpm exec athena-js generate --config ./athena.config.ts
git diff --exit-codeDry-run verification strategy (6)
pnpm exec athena-js generate --config ./athena.config.ts --dry-runArtifact diff strategy for PR checks (7)
pnpm exec athena-js generate --config ./athena.config.ts
git diff --exit-codeFailure handling and retry guidance (8)
#!/usr/bin/env bash
set -euo pipefail
for attempt in 1 2 3; do
if pnpm exec athena-js generate --config ./athena.config.ts --dry-run; then
exit 0
fi
# deterministic failures should fail immediately
if [[ $attempt -eq 1 ]]; then
# If your CI captures stderr, pattern-match known deterministic messages here.
# Keep this conservative to avoid masking real config problems.
:
fi
if [[ $attempt -lt 3 ]]; then
sleep $((attempt * 5))
fi
done
exit 1docs/generator-codex-handoff-prompt-pack.md
Source: https://github.com/xylex-group/athena-js/blob/main/docs/generator-codex-handoff-prompt-pack.md
1) Prompt: Canonical Handoff (1)
You are documenting the Athena JS typed schema generator. Treat this as a production docs task.
Goals:
1. Keep docs accurate with current implementation.
2. Explain both PostgreSQL introspection modes:
- direct pg_url (`provider.mode = "direct"`)
- gateway-only query path (`provider.mode = "gateway"`, using Athena `/gateway/query`)
3. Preserve backward compatibility messaging for existing createClient/from<T> users.
4. Document naming, placeholders, feature flags, and edge cases.
5. Include migration and troubleshooting sections.
6. Keep all examples runnable and consistent with current exports.
Current implementation facts (must reflect):
- Generator config discovery supports:
athena.config.ts/js, athena-js.config.ts/js, .athena.config.ts/js
- CLI command is:
athena-js generate [--config <path>] [--dry-run]
- Direct mode uses pg connection string and catalog introspection.
- Gateway mode introspects by executing SQL catalog queries through Athena query endpoint.
- Output artifacts are model/schema/database/registry.
- Output registry emission can be disabled with features.emitRegistry=false.
- Relation metadata emission can be disabled with features.emitRelations=false.
- Postgres type mapping handles numeric/text/boolean/binary/uuid/json/jsonb/temporal/network/geometric/bit/xml/full-text/enum/domain/range/multirange/composite/arrays.
- Unsafe/reserved identifiers are emitted safely in generated TS property keys.
Deliverables:
- Update docs/generator-config.md as the primary reference.
- Add or update a quickstart section in README linking to generator docs.
- Add a troubleshooting section with concrete failure patterns and fixes.
- Add a migration section for teams moving from manual model typing to generated registry files.
- Add a "CI usage" section with direct and gateway-only examples.
- Keep wording explicit, concise, and implementation-accurate.
Validation requirements:
- No contradictions with actual config keys or exported APIs.
- Every code snippet should be self-consistent.
- Mention known limitations clearly (Scylla contract placeholder, custom SQL deferred).2) Prompt: Troubleshooting-Focused Expansion (2)
Expand Athena JS generator troubleshooting docs only.
Required sections:
1. Config discovery failures (file name and location mismatch)
2. Gateway auth/header/backend mismatches
3. Empty snapshot / missing schema results
4. Duplicate output path collisions from placeholder templates
5. Unsafe identifier rendering expectations
6. Type mapping surprises (e.g. bigint as string)
For each section include:
- Symptom
- Likely cause
- How to confirm
- Exact fix
Keep examples aligned with:
- provider.mode direct (pg_url)
- provider.mode gateway (Athena /gateway/query)3) Prompt: CI/Automation-Focused Expansion (3)
Write CI/CD documentation for Athena JS generator.
Requirements:
- Include two pipeline patterns:
1) direct pg_url introspection
2) gateway-only introspection via Athena query endpoint
- Include secure secret mapping examples.
- Include dry-run verification step.
- Include artifact diff strategy for PR checks.
- Include failure handling strategy and retry guidance.
- Include branch policy advice for generated files.
Keep it implementation-accurate and avoid introducing non-existent CLI flags.docs/generator-config.md
Source: https://github.com/xylex-group/athena-js/blob/main/docs/generator-config.md
CLI entrypoint (1)
athena-js generate
athena-js generate --dry-run
athena-js generate --config ./athena.config.ts
athena-js generate --help
athena-js help generateConfig surface at a glance (2)
export interface AthenaGeneratorConfig {
provider: GeneratorProviderConfig
output: GeneratorOutputConfig
naming?: Partial<GeneratorNamingConfig>
features?: Partial<GeneratorFeatureFlags>
experimental?: Partial<GeneratorExperimentalFlags>
}defineGeneratorConfig helper (3)
import { defineGeneratorConfig } from "@xylex-group/athena";
export default defineGeneratorConfig({
provider: {
kind: "postgres",
mode: "direct",
connectionString: process.env.DATABASE_URL!,
database: "app_db",
schemas: (process.env.ATHENA_GENERATOR_SCHEMAS ?? "public,athena").split(","),
},
output: {
targets: {
model: "athena/models/{schema_kebab}/{model_kebab}.ts",
schema: "athena/schemas/{schema_kebab}.ts",
database: "athena/relations.ts",
registry: "athena/config.ts",
},
},
});Postgres direct mode (4)
{
kind: "postgres",
mode: "direct",
connectionString: "postgres://user:pass@host:5432/db",
database: "app_db",
schemas: ["public", "athena"],
}Postgres gateway mode (5)
{
kind: "postgres",
mode: "gateway",
gatewayUrl: "https://athena.example.com",
apiKey: process.env.ATHENA_API_KEY!,
database: "app_db",
schemas: ["public", "athena"],
backend: "athena",
}Scylla mode (contract placeholder) (6)
{
kind: "scylla",
mode: "direct",
contactPoints: ["127.0.0.1:9042"],
keyspace: "app",
datacenter: "eu-west-1",
}Output contract (7)
interface GeneratorOutputConfig {
targets: GeneratorOutputTargets;
placeholderMap: Record<string, string>;
}
interface GeneratorOutputTargets {
model: string;
schema: string;
database: string;
registry: string;
}Schema selection (8)
schemas: ["public", "athena"]
// or
schemas: process.env.ATHENA_GENERATOR_SCHEMAS ?? "public,athena"placeholderMap (9)
output: {
targets: {
model: "src/{namespace}/{schema_snake}/{model_snake}.ts",
},
placeholderMap: {
namespace: "{database_kebab}/{schema_kebab}",
},
}Naming controls (10)
interface GeneratorNamingConfig {
modelType: "preserve" | "camel" | "pascal" | "snake" | "kebab";
modelConst: "preserve" | "camel" | "pascal" | "snake" | "kebab";
schemaConst: "preserve" | "camel" | "pascal" | "snake" | "kebab";
databaseConst: "preserve" | "camel" | "pascal" | "snake" | "kebab";
registryConst: "preserve" | "camel" | "pascal" | "snake" | "kebab";
}Feature flags (11)
interface GeneratorFeatureFlags {
emitRelations: boolean;
emitRegistry: boolean;
}Feature flags (12)
features: {
emitRegistry: false,
},Experimental flags (13)
interface GeneratorExperimentalFlags {
postgresGatewayIntrospection: boolean;
scyllaProviderContracts: boolean;
}Local development (direct DB) (14)
import { defineGeneratorConfig } from "@xylex-group/athena";
export default defineGeneratorConfig({
provider: {
kind: "postgres",
mode: "direct",
connectionString: process.env.DATABASE_URL!,
database: "app_db",
schemas: ["public", "athena"],
},
output: {
targets: {
model: "athena/models/{schema_kebab}/{model_kebab}.ts",
schema: "athena/schemas/{schema_kebab}.ts",
database: "athena/relations.ts",
registry: "src/generated/registry.ts",
},
placeholderMap: {
namespace: "{database_kebab}/{schema_kebab}",
},
},
naming: {
modelType: "pascal",
modelConst: "camel",
schemaConst: "snake",
},
});Gateway-only environment (15)
export default {
provider: {
kind: "postgres",
mode: "gateway",
gatewayUrl: process.env.ATHENA_URL!,
apiKey: process.env.ATHENA_API_KEY!,
database: "app_db",
schemas: ["public", "athena"],
backend: "athena",
},
output: {
targets: {
model: "athena/models/{schema_kebab}/{model_kebab}.ts",
schema: "athena/schemas/{schema_kebab}.ts",
database: "athena/relations.ts",
registry: "athena/config.ts",
},
placeholderMap: {},
},
};Pipeline integration tips (16)
athena-js generate --config ./athena.config.ts --dry-rundocs/getting-started.md
Source: https://github.com/xylex-group/athena-js/blob/main/docs/getting-started.md
2) Install (1)
pnpm add @xylex-group/athenacreateClient (fastest) (2)
import { createClient } from "@xylex-group/athena";
const athena = createClient(process.env.ATHENA_URL!, process.env.ATHENA_API_KEY!, {
client: "web-dashboard",
backend: { type: "athena" },
});AthenaClient.builder() (explicit configuration) (3)
import { AthenaClient, Backend } from "@xylex-group/athena";
const athena = AthenaClient.builder()
.url(process.env.ATHENA_URL!)
.key(process.env.ATHENA_API_KEY!)
.backend(Backend.Athena)
.client("web-dashboard")
.headers({ "X-App-Region": "eu" })
.build();AthenaClient.fromEnvironment() (ops-friendly) (4)
import { AthenaClient } from "@xylex-group/athena";
const athena = AthenaClient.fromEnvironment();4) Read data with table builders (5)
type UserRow = {
id: string;
email: string;
active: boolean;
created_at: string;
};
const result = await athena
.from<UserRow>("users")
.select("id, email, active")
.eq("active", true)
.order("created_at", { ascending: false })
.limit(25);Important chain behavior (6)
const one = await athena.from<UserRow>("users").eq("id", "u-1").single("id, email");5) Filter, paging, and schema-qualified calls (7)
const page = await athena
.from<UserRow>("users")
.select("id, email")
.currentPage(2)
.pageSize(50)
.order("created_at", { ascending: false });
const usersInPublic = await athena
.from<UserRow>("users")
.select("id, email", { schema: "public" });Insert (8)
await athena
.from<{ id: string; email: string }, { email: string }>("users")
.insert({ email: "user@example.com" })
.select("id, email");Update (9)
await athena
.from<{ id: string; email: string }, { email: string }, { email?: string }>("users")
.eq("id", "u-1")
.update({ email: "new@example.com" })
.select("id, email");Upsert (10)
await athena
.from<{ id: string; email: string }, { id: string; email: string }, { email?: string }>("users")
.upsert(
{ id: "u-1", email: "user@example.com" },
{
onConflict: "id",
updateBody: { email: "user@example.com" },
},
)
.select("id, email");RPC (11)
const rpcResult = await athena
.rpc<{ count: number }, { active_only: boolean }>("list_users", { active_only: true })
.single("count");Raw query (12)
const rows = await athena.query<{ id: string; email: string }>(
"select id, email from users where active = true",
);8) Handle responses safely (13)
import { isOk, unwrapRows, unwrapOne, requireAffected } from "@xylex-group/athena";
const list = await athena.from<{ id: string }>("users").select("id");
if (!isOk(list)) throw new Error(list.error ?? "Unknown error");
const rows = unwrapRows(list);
const single = await athena.from<{ id: string }>("users").eq("id", "u-1").single("id");
const user = unwrapOne(single, { allowNull: true });
const inserted = await athena.from<{ id: string }, { email: string }>("users").insert({ email: "a@b.com" });
requireAffected(inserted, { min: 1 });9) Move to typed model registry (14)
import {
createTypedClient,
defineDatabase,
defineModel,
defineRegistry,
defineSchema,
} from "@xylex-group/athena";
const users = defineModel<
{ id: string; email: string; created_at: string | null },
{ email: string },
{ email?: string }
>({
meta: {
primaryKey: ["id"],
nullable: { id: false, email: false, created_at: true },
},
});
const registry = defineRegistry({
app: defineDatabase({
public: defineSchema({ users }),
}),
});
const typed = createTypedClient(registry, process.env.ATHENA_URL!, process.env.ATHENA_API_KEY!);
await typed
.fromModel("app", "public", "users")
.select("id, email")
.eq("email", "user@example.com");10) Tenant header propagation (15)
const scopedClient = createTypedClient(registry, process.env.ATHENA_URL!, process.env.ATHENA_API_KEY!, {
tenantKeyMap: {
organizationId: "X-Organization-Id",
workspaceId: "X-Workspace-Id",
},
});
const tenantBound = scopedClient.withTenantContext({ organizationId: "org-1", workspaceId: "ws-4" });
await tenantBound.fromModel("app", "public", "users").select("id, email");11) Form contracts with Zod and React Hook Form (16)
import { z } from "zod";
import type { InsertOf, UpdateOf } from "@xylex-group/athena";
const userCreateSchema = z.object({
email: z.string().email(),
active: z.boolean().default(true),
});
type UserModel = typeof registry.app.schemas.public.models.users;
type UserInsert = InsertOf<UserModel>;
type UserUpdate = UpdateOf<UserModel>;
function toInsert(input: unknown): UserInsert {
return userCreateSchema.parse(input);
}
function toUpdate(input: unknown): UserUpdate {
return userCreateSchema.partial().parse(input);
}12) Generate registry code from PostgreSQL (17)
athena-js generate
athena-js generate --dry-run
athena-js generate --config ./athena.config.ts
athena-js generate --help12) Generate registry code from PostgreSQL (18)
import { defineGeneratorConfig } from "@xylex-group/athena";
export default defineGeneratorConfig({
provider: {
kind: "postgres",
mode: "direct",
connectionString: process.env.DATABASE_URL!,
database: "app_db",
schemas: ["public", "athena"],
},
output: {
targets: {
model: "athena/models/{schema_kebab}/{model_kebab}.ts",
schema: "athena/schemas/{schema_kebab}.ts",
database: "athena/relations.ts",
registry: "athena/config.ts",
},
},
});13) Production checklist (19)
provider: {
kind: "postgres",
mode: "gateway",
gatewayUrl: process.env.ATHENA_URL!,
apiKey: process.env.ATHENA_API_KEY!,
database: "app_db",
schemas: ["public", "athena"],
}docs/index.md
Source: https://github.com/xylex-group/athena-js/blob/main/docs/index.md
Concept map (1)
Runtime client (createClient / AthenaClient.builder)
-> Optional typed client (createTypedClient)
-> Registry contracts (defineModel / defineSchema / defineDatabase / defineRegistry)
-> Optional generator pipeline (athena-js generate)
-> Generated model/schema/database/registry files
-> Runtime query builders + app/domain form surfacesdocs/type-safety-playbook.md
Source: https://github.com/xylex-group/athena-js/blob/main/docs/type-safety-playbook.md
3) Tri-generic model pattern (1)
const invoices = defineModel<
{
id: string;
organization_id: string;
amount_cents: number;
status: "draft" | "sent" | "paid";
due_at: string | null;
},
{
organization_id: string;
amount_cents: number;
status?: "draft" | "sent" | "paid";
due_at?: string | null;
},
{
amount_cents?: number;
status?: "draft" | "sent" | "paid";
due_at?: string | null;
}
>({
meta: {
primaryKey: ["id"],
nullable: {
id: false,
organization_id: false,
amount_cents: false,
status: false,
due_at: true,
},
},
});4) Typed query-builder behavior to leverage (2)
await typed
.fromModel("billing", "public", "invoices")
.eq("organization_id", "org-1")
.order("due_at", { ascending: true })
.select("id, amount_cents, status");5) Zod alignment pattern (3)
import { z } from "zod";
import type { InsertOf, UpdateOf } from "@xylex-group/athena";
type InvoiceModel = typeof registry.billing.schemas.public.models.invoices;
type InvoiceInsert = InsertOf<InvoiceModel>;
type InvoiceUpdate = UpdateOf<InvoiceModel>;
const invoiceCreateSchema = z.object({
organization_id: z.string().min(1),
amount_cents: z.number().int().nonnegative(),
status: z.enum(["draft", "sent", "paid"]).default("draft"),
due_at: z.string().datetime().nullable().optional(),
});
const invoicePatchSchema = invoiceCreateSchema
.pick({ amount_cents: true, status: true, due_at: true })
.partial();
function parseCreate(input: unknown): InvoiceInsert {
return invoiceCreateSchema.parse(input);
}
function parsePatch(input: unknown): InvoiceUpdate {
return invoicePatchSchema.parse(input);
}6) React Hook Form alignment pattern (4)
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
const form = useForm<z.input<typeof invoiceCreateSchema>>({
resolver: zodResolver(invoiceCreateSchema),
defaultValues: {
status: "draft",
},
});
async function onSubmit(raw: z.input<typeof invoiceCreateSchema>) {
const payload: InvoiceInsert = invoiceCreateSchema.parse(raw);
await typed.fromModel("billing", "public", "invoices").insert(payload);
}Typed create helper (5)
function createUser(payload: InsertOf<typeof registry.app.schemas.public.models.users>) {
return typed.fromModel("app", "public", "users").insert(payload).single("id, email");
}Typed patch helper (6)
function updateUser(id: string, patch: UpdateOf<typeof registry.app.schemas.public.models.users>) {
return typed.fromModel("app", "public", "users").eq("id", id).update(patch).single("id, email");
}Typed list helper (7)
function listUsers() {
return typed
.fromModel("app", "public", "users")
.select("id, email, created_at")
.order("created_at", { ascending: false });
}docs/typed-schema-registry.md
Source: https://github.com/xylex-group/athena-js/blob/main/docs/typed-schema-registry.md
1) Core contracts (1)
import {
defineModel,
defineSchema,
defineDatabase,
defineRegistry,
createTypedClient,
} from "@xylex-group/athena";
const users = defineModel<
{ id: string; email: string; createdAt: string | null },
{ id?: string; email: string },
{ email?: string }
>({
meta: {
primaryKey: ["id"],
nullable: { id: false, email: false, createdAt: true },
},
});
const primarySchema = defineSchema({ users });
const primaryDb = defineDatabase({ public: primarySchema });
const registry = defineRegistry({ app: primaryDb });
const client = createTypedClient(registry, process.env.ATHENA_URL!, process.env.ATHENA_API_KEY!);3) Client behavior and type coupling (2)
const typed = createTypedClient(registry, "https://athena-db.com", "secret", {
tenantKeyMap: {
organizationId: "X-Organization-Id",
workspaceId: "X-Workspace-Id",
},
});
await typed
.withTenantContext({ organizationId: "org-1" })
.fromModel("app", "public", "users")
.select("id, email")
.eq("active", true);Tenant context behavior (3)
const scoped = typed.withTenantContext({ organizationId: "org-1" });
const scopedAgain = scoped.withTenantContext({ workspaceId: "ws-2" });
// scopedAgain sends both tenant headers5) Relation metadata (4)
type Kind = "one-to-one" | "many-to-one" | "one-to-many" | "many-to-many";
{
kind: Kind;
sourceColumns: string[];
targetSchema: string;
targetModel: string;
targetColumns: string[];
targetDatabase?: string;
through?: {
schema: string;
model: string;
sourceColumns: string[];
targetColumns: string[];
};
}10) Model-to-form adapter (React Hook Form + Zod) (5)
import { defineModel, createModelFormAdapter } from "@xylex-group/athena";
const profiles = defineModel<{
id: string;
display_name: string | null;
age: number | null;
}>({
meta: {
primaryKey: ["id"],
nullable: { id: false, display_name: true, age: true },
},
});
const formAdapter = createModelFormAdapter(profiles);
// Edit defaults for RHF (null -> "")
const defaultValues = formAdapter.toDefaults(existingRow);
// Submit payload ("" -> null on nullable fields)
const insertPayload = formAdapter.toInsert(formValues);README.md
Source: https://github.com/xylex-group/athena-js/blob/main/README.md
Install (1)
npm install @xylex-group/athena
# or
pnpm add @xylex-group/athena
# or
yarn add @xylex-group/athenaInstall (2)
npm install react # React >=17 required for the hookQuick start (3)
import { createClient } from "@xylex-group/athena";
const athenaClient = createClient(ATHENA_URL, ATHENA_API_KEY, {
client: "CLIENT_NAME",
backend: { type: "athena" },
});
const { data, error } = await athenaClient.from("characters").select(`
id,
name,
from:sender_id(name),
to:receiver_id(name)
`);
if (error) {
console.error("gateway error", error);
} else {
console.table(data);
}Auth client (Athena Auth server) (4)
import { createClient } from "@xylex-group/athena";
const athena = createClient(ATHENA_URL, ATHENA_API_KEY, {
client: "CLIENT_NAME",
auth: {
baseUrl: "http://localhost:3001/api/auth",
// optional: bearer token if you are not using cookie-based sessions
bearerToken: process.env.AUTH_BEARER_TOKEN,
},
});
const login = await athena.auth.signIn.email({
email: "demo@example.com",
password: "super-secret",
rememberMe: true,
});
const session = await athena.auth.getSession();
const sessions = await athena.auth.session.list();
// clear one session
await athena.auth.session.revoke({ token: "session_token_here" });
// or clear all sessions
await athena.auth.session.revoke([{ token: "session_token_here" }, { token: "session_token_2" }]);
await athena.auth.signOut();
// additional core flows
await athena.auth.forgetPassword({ email: "demo@example.com", redirectTo: "https://app/reset-password" });
await athena.auth.resetPassword({ newPassword: "new-secret", token: "reset_token" });
await athena.auth.verifyEmail({ token: "verify_token", callbackURL: "https://app/verified" });
await athena.auth.changePassword({ currentPassword: "old-secret", newPassword: "new-secret" });
await athena.auth.user.update({ name: "Demo User" });Typed schema registry (model-first) (5)
import {
createTypedClient,
defineDatabase,
defineModel,
defineRegistry,
defineSchema,
} from "@xylex-group/athena";
const registry = defineRegistry({
primary: defineDatabase({
public: defineSchema({
users: defineModel<{ id: string; email: string }>({
meta: {
primaryKey: ["id"],
nullable: { id: false, email: false },
},
}),
}),
}),
});
const typed = createTypedClient(registry, ATHENA_URL, ATHENA_API_KEY, {
tenantKeyMap: {
organizationId: "X-Organization-Id",
},
});
await typed
.withTenantContext({ organizationId: "org_1" })
.fromModel("primary", "public", "users")
.select("*");Typed schema generator (6)
athena-js generate
athena-js generate --dry-run
athena-js generate --config ./athena.config.ts
athena-js generate --help
athena-js help generateResult unwrapping and success guards (7)
import {
isOk,
unwrap,
unwrapRows,
unwrapOne,
requireSuccess,
requireAffected,
} from "@xylex-group/athena";
const result = await athena.from("users").select("id,name");
if (isOk(result)) {
const rows = unwrapRows(result); // typed User[]
console.log(rows.length);
}
const one = await athena.from("users").eq("id", 1).single("id,name");
const user = unwrapOne(one, { allowNull: true });
const inserted = await athena
.from("users")
.insert({ name: "Alice" })
.select("id", { count: "exact" });
requireSuccess(inserted, { table: "users", operation: "insert" });
requireAffected(inserted, { min: 1 }, { table: "users", operation: "insert" });Error normalization (8)
import { normalizeAthenaError } from "@xylex-group/athena";
const result = await athena.from("users").insert({ id: 1 }).select();
if (result.error) {
const err = normalizeAthenaError(result, {
table: "users",
operation: "insert",
});
if (err.kind === "unique_violation") {
// deterministic conflict handling
}
}Numeric coercion (9)
import { coerceInt, assertInt } from "@xylex-group/athena";
const maybeCaseId = coerceInt(req.query.case_id, { min: 1 });
if (maybeCaseId == null) throw new Error("Invalid case id");
const caseId = assertInt(req.query.case_id, "case_id", { min: 1 });Retry helper (10)
import { withRetry } from "@xylex-group/athena";
const result = await withRetry(
{
retries: 3,
backoff: "exponential",
baseDelayMs: 100,
jitter: true,
},
() => athena.from("users").select("id,name"),
);Reading rows (11)
// select all columns
const { data } = await athena.from("users").select();
// select specific columns
const { data } = await athena.from("users").select("id, name, email");
// select with type annotation
const { data } = await athena.from<User>("users").select("id, name");Filters (12)
const { data } = await athena
.from("characters")
.select("id, name")
.eq("active", true) // column = value
.eqUuid("session_id", "550e8400-e29b-41d4-a716-446655440000") // explicit UUID cast
.eqCast("session_id", "550e8400-e29b-41d4-a716-446655440000", "uuid") // explicit cast type
.neq("role", "guest") // column != value
.gt("level", 5) // column > value
.gte("score", 100) // column >= value
.lt("age", 30) // column < value
.lte("created_at", "2024-01-01") // column <= value
.like("name", "Ali%") // SQL LIKE (case-sensitive)
.ilike("email", "%@example%") // SQL ILIKE (case-insensitive)
.is("deleted_at", null) // IS NULL / IS TRUE etc.
.in("status", ["active", "pending"]) // IN (…)
.contains("tags", ["hero"]) // array contains value
.containedBy("tags", ["hero", "villain"]) // array is subset of value
.match({ role: "admin", active: true }) // multiple eq filters at once
.not("role", "eq", "banned") // NOT col op val
.or("status.eq.active,status.eq.pending"); // OR expressionFilters (13)
const { data } = await athena
.from("instruments")
.select("name, section_id")
.eq("name", "violin");Pagination (14)
// 1. offset / limit — contiguous windows
const { data } = await athena.from("users").select().limit(25).offset(50);
// range shorthand: offset = from, limit = to - from + 1
const { data: firstTwentyFive } = await athena.from("users").select().range(0, 24);
// 2. page based — maps to current_page / page_size / total_pages
const { data: page2 } = await athena
.from("orders")
.select("id, total")
.currentPage(2)
.pageSize(25);
// .totalPages() is an optional hint some backends use in the response envelope
const { data: hinted } = await athena
.from("orders")
.select("id, total")
.currentPage(1)
.pageSize(25)
.totalPages(10);Ordering (15)
// descending + limit
// SELECT * FROM rsf_messages WHERE room_id = $1 ORDER BY created_at DESC LIMIT 100
const { data } = await athena
.from("rsf_messages")
.eq("room_id", roomId)
.select("*", { stripNulls: false })
.order("created_at", { ascending: false })
.limit(100);
// ascending (default) + page-based pagination
const { data: page } = await athena
.from("orders")
.select("id, total, created_at")
.order("created_at")
.currentPage(1)
.pageSize(25);
// combine with .single() to grab the newest / oldest row
const { data: latest } = await athena
.from("messages")
.eq("room_id", roomId)
.select("*")
.order("created_at", { ascending: false })
.single();Single row (16)
// returns the first row or null instead of an array
const { data: user } = await athena
.from("users")
.select("id, name")
.eq("id", 42)
.single();Table schema targeting (17)
const { data } = await athena
.from("users")
.select("id,email", { schema: "public" });RPC (18)
const { data, count } = await athena
.rpc("list_users", { role: "admin" }, { count: "exact", schema: "public" })
.eq("active", true)
.order("created_at", { ascending: false })
.range(0, 24)
.select(["id", "email"]);
const { data: firstUser } = await athena
.rpc<{ id: number; email: string }>("list_users", { role: "admin" })
.single("id,email");
const { data: readOnlyUser } = await athena
.rpc<{
id: number;
email: string;
}>(
"list_users",
{ role: "admin" },
{ get: true, count: "planned", head: true },
)
.eq("id", 1)
.single("id,email");Options (19)
const { data } = await athena
.from("orders")
.select("id", { count: "exact", stripNulls: false });Insert (20)
const { data: inserted } = await athena
.from("countries")
.insert({ name: "Mordor" })
.select("id, name");
// insert multiple rows
const { data } = await athena
.from("characters")
.insert([{ name: "Frodo" }, { name: "Sam" }])
.select();
// Type inference differs by payload shape:
// - insert(one) => AthenaResult<Row>
// - insert(many) => AthenaResult<Row[]>Update (21)
const { data: updated } = await athena
.from("countries")
.update({ name: "Gondor" })
.eq("id", 1)
.select();Upsert (22)
const { data } = await athena
.from("countries")
.upsert(
{ id: 2, name: "Rohan" },
{ updateBody: { name: "Rohan" }, onConflict: "id" },
)
.select();
// Type inference differs by payload shape:
// - upsert(one) => AthenaResult<Row>
// - upsert(many) => AthenaResult<Row[]>Delete (23)
// delete by id filter
await athena.from("countries").eq("id", 1).delete();
// delete with explicit resourceId option
await athena.from("countries").delete({ resourceId: "abc-123" });
// chain .select() to get the deleted row back
const { data: deleted } = await athena
.from("countries")
.eq("resource_id", "abc-123")
.delete()
.select("id, name");MutationQuery chaining (24)
const mutation = athena.from("users").insert({ name: "Alice" });
await mutation.select("id, name"); // fire request, return rows
await mutation.returning("id"); // alias for .select()
await mutation.single("id"); // return first row or null
await mutation.maybeSingle("id"); // same as .single()
await mutation; // fire request, return default columns
mutation.then(({ data }) => …); // thenable
mutation.catch(err => …);
mutation.finally(() => …);React hooks (25)
"use client";
import {
AthenaQueryClientProvider,
createAthenaQueryClient,
useAthenaGateway,
useMutation,
useQuery,
} from "@xylex-group/athena/react";
import { createClient } from "@xylex-group/athena";
const queryClient = createAthenaQueryClient({
cache: { mode: "none" }, // default: no persistent data cache, inflight dedupe only
});
const athena = createClient(
process.env.NEXT_PUBLIC_ATHENA_URL!,
process.env.NEXT_PUBLIC_ATHENA_API_KEY!,
);
type Product = {
id: string;
name: string;
price: number;
};
type CreateProductInput = {
name: string;
price: number;
};
function ProductsInner() {
const products = useQuery<Product[]>({
queryKey: ["products"],
queryFn: () =>
athena.from("products").select("id,name,price").limit(50),
});
const createProduct = useMutation<CreateProductInput, Product>({
mutationFn: (input) =>
athena.from("products").insert(input).select("id,name,price").single(),
onSuccess: () => {
void products.refetch();
},
});
if (products.isLoading) return <div>Loading...</div>;
if (products.error) return <div>{products.error.message}</div>;
return (
<div>
<button
onClick={() => {
createProduct.mutate({ name: "New product", price: 99 });
}}
>
Add Product
</button>
{products.data?.map((product) => (
<div key={product.id}>
{product.name} - {product.price}
</div>
))}
</div>
);
}
export function Products() {
return (
<AthenaQueryClientProvider client={queryClient}>
<ProductsInner />
</AthenaQueryClientProvider>
);
}React hooks (26)
import { useAthenaGateway } from "@xylex-group/athena/react";
import { useEffect } from "react";
export function UsersPanel() {
const { fetchGateway, lastResponse, isLoading, error } = useAthenaGateway({
baseUrl: "https://athena-db.com",
apiKey: process.env.NEXT_PUBLIC_ATHENA_API_KEY,
});
useEffect(() => {
void fetchGateway({
table_name: "users",
columns: ["id", "email"],
limit: 25,
});
}, [fetchGateway]);
if (error) return <div>Error: {error}</div>;
if (isLoading) return <div>Loading…</div>;
return <pre>{JSON.stringify(lastResponse?.data, null, 2)}</pre>;
}User context headers (27)
const athena = createClient(
"https://athena-db.com",
process.env.ATHENA_API_KEY,
{
headers: {
"X-User-Id": currentUser.id,
"X-Organization-Id": currentUser.organizationId ?? "",
},
},
);Custom headers (28)
const athena = createClient(
"https://athena-db.com",
process.env.ATHENA_API_KEY,
{
headers: {
"X-Custom-Header": "value",
},
},
);TypeScript (29)
interface User {
id: number;
name: string;
email: string;
active: boolean;
}
const { data } = await athena
.from<User>("users")
.select("id, name")
.eq("active", true);
// data is User[] | nullDevelopment Validation (30)
pnpm typecheck # compile-time type compatibility checks
pnpm check:all # lint + typecheck + test + buildtest-sdk/examples/generator/README.md
Source: https://github.com/xylex-group/athena-js/blob/main/test-sdk/examples/generator/README.md
Validation (1)
pnpm test:e2etest-sdk/examples/react-hooks/README.md
Source: https://github.com/xylex-group/athena-js/blob/main/test-sdk/examples/react-hooks/README.md
Athena credentials (1)
import { createExampleAthenaClient } from './shared'
const athena = createExampleAthenaClient({
athenaUrl: process.env.NEXT_PUBLIC_ATHENA_URL!,
apiKey: process.env.NEXT_PUBLIC_ATHENA_API_KEY!,
client: process.env.NEXT_PUBLIC_ATHENA_CLIENT,
})Athena credentials (2)
import { DemoProductsPanel } from './products-panel'
export default function Page() {
return <DemoProductsPanel athena={athena} />
}Direct Athena hook pattern (3)
const products = useQuery({
queryKey: ['products', organizationId],
enabled: Boolean(organizationId),
queryFn: async () => {
const result = await athena
.from('products')
.select('id,name,price')
.eq('organization_id', organizationId)
.limit(50)
if (result.error) throw new Error(result.error)
return result.data ?? []
},
})
const createProduct = useMutation({
mutationFn: async (input) => {
const result = await athena
.from('products')
.insert(input)
.select('id,name,price')
.single()
if (result.error || !result.data) throw new Error(result.error ?? 'insert failed')
return result.data
},
onSuccess: () => void products.refetch(),
})test-sdk/README.md
Source: https://github.com/xylex-group/athena-js/blob/main/test-sdk/README.md
Setup (1)
pnpm build
cd test-sdk
pnpm installRun (2)
pnpm start
# or
pnpm devRun E2E tests (3)
pnpm test:e2eRun E2E tests (4)
node --import tsx --test test/react-test-sdk-hooks-integration.test.tsGenerator full-utilization examples (5)
pnpm test:e2e:generatorError response shape (6)
{
"error": {
"code": "VALIDATION_ERROR",
"message": "limit must be a non-negative integer",
"details": {
"field": "limit",
"received": "abc"
}
},
"responseTimeMs": 2
}