API Library Overview
The @elcto/api package provides unified REST, GraphQL, and WebSocket clients for Heimdall platform applications.
Installation
The library is already included in both platform/id and platform/backend. For new Next.js apps in the monorepo:
- Add the dependency to
package.json:
{
"dependencies": {
"@elcto/api": "workspace:*"
}
}
- Add to
next.config.ts:
const nextConfig: NextConfig = {
transpilePackages: ['@elcto/api', '@elcto/ui'],
};
- Run
pnpm install
Config-first design
@elcto/api is config-first: the library itself reads no environment variables.
Every transport (apiRequest, graphqlRequest, gql, createWebSocket, useWebSocket)
and every route helper takes an ApiClientConfig as its
first argument:
interface ApiClientConfig {
baseUrl: string; // e.g. "http://localhost:3000" (NO trailing /v1)
wsUrl?: string; // e.g. "ws://localhost:3000/v1/ws" (derived from baseUrl if absent)
systemApiKey?: string; // service-to-service / SSR auth
}
Each app builds this from its own src/lib/config.ts (which resolves
HEIMDALL_* → NEXT_PUBLIC_* → localhost default) and exposes a getApiConfig()
helper via @/lib/api. Pass getApiConfig() as the first argument everywhere:
import { getApiConfig, gql } from "@/lib/api";
const data = await gql<{ user: User }>(getApiConfig(), query, { id: "123" });
Quick Start
REST API
import { getApiConfig, apiRequest } from "@/lib/api";
// GET request
const response = await apiRequest<User>(getApiConfig(), "/v1/users/123");
if (response.data) {
console.log(response.data);
}
// POST request
const result = await apiRequest<void>(getApiConfig(), "/v1/users", {
method: "POST",
body: { name: "John" },
});
GraphQL
import { getApiConfig, gql, graphqlRequest } from "@/lib/api";
// Using gql helper (throws on error)
const data = await gql<{ user: User }>(getApiConfig(), `
query GetUser($id: ID!) {
user(id: $id) {
id
name
}
}
`, { id: "123" });
// Using raw request (returns errors)
const response = await graphqlRequest<{ user: User }>(getApiConfig(), query, variables);
if (response.errors) {
console.error(response.errors);
}
WebSocket (Client-side only)
import { getApiConfig, createWebSocket } from "@/lib/api";
const ws = createWebSocket(getApiConfig(), {
accessToken: session.accessToken,
autoReconnect: true,
});
ws.onMessage((data) => {
if (data.type === "accountBanned") {
// Handle ban
}
});
ws.send({ type: "ping" });
ws.close();
React Hook
import { getApiConfig, useWebSocket } from "@/lib/api";
function LiveUpdates() {
const { isConnected, lastMessage, send } = useWebSocket(getApiConfig(), {
accessToken: session.accessToken,
});
return (
<div>
<p>Status: {isConnected ? "Connected" : "Disconnected"}</p>
<button onClick={() => send({ type: "ping" })}>Ping</button>
</div>
);
}
Authentication
Server-side (REST & GraphQL)
When no accessToken is supplied, requests fall back to config.systemApiKey
(the app populates this from its SYSTEM_API_KEY env var via getApiConfig()):
// Falls back to config.systemApiKey if set
const response = await apiRequest<User>(getApiConfig(), "/v1/users/123");
// Or provide a specific access token
const response = await apiRequest<User>(getApiConfig(), "/v1/users/123", {
accessToken: userAccessToken,
});
Priority: accessToken option > config.systemApiKey > no auth
Client-side (WebSocket)
WebSocket connections derive their URL from the config (config.wsUrl, falling back
to config.baseUrl + /v1/ws) and take the access token via options:
const ws = createWebSocket(getApiConfig(), {
accessToken: session.accessToken,
});
Environment Variables
The library reads no environment variables itself. Each app's src/lib/config.ts
resolves these and feeds them into getApiConfig():
| Variable | Maps to | Description | Default |
|---|---|---|---|
HEIMDALL_API_URL → NEXT_PUBLIC_API_URL | config.baseUrl | API base URL | http://localhost:3000 |
HEIMDALL_WS_URL → NEXT_PUBLIC_WS_URL | config.wsUrl | WebSocket URL | ws://localhost:3000/v1/ws |
SYSTEM_API_KEY | config.systemApiKey | Server-side API key (no public fallback) | - |
Package Structure
shared/api/
├── src/
│ ├── index.ts # Main exports
│ ├── client.ts # ApiClientConfig type
│ ├── clients/
│ │ ├── rest.ts # REST client
│ │ ├── graphql.ts # GraphQL client
│ │ ├── storage.ts # Storage client
│ │ └── websocket.ts # WebSocket client
│ ├── types/
│ │ ├── audit.ts # Audit types & constants
│ │ ├── common.ts # Common types (pagination, config)
│ │ ├── discord.ts # Discord integration types
│ │ ├── graphql.ts # GraphQL types
│ │ ├── integration.ts # Integration types
│ │ ├── platform.ts # Platform types
│ │ ├── rest.ts # REST types
│ │ ├── role.ts # Role types
│ │ ├── session.ts # Session types
│ │ ├── storage.ts # Storage types
│ │ ├── system-setting.ts # System setting types
│ │ ├── user.ts # User types
│ │ └── websocket.ts # WebSocket types & messages
│ ├── routes/
│ │ ├── audit.ts # Audit log routes
│ │ ├── permissions.ts # Permission management routes
│ │ ├── platforms.ts # Platform routes
│ │ ├── roles.ts # Role management routes
│ │ ├── sessions.ts # Session management routes
│ │ ├── settings.ts # System settings routes
│ │ └── users.ts # User API routes
│ ├── hooks/
│ │ └── useWebSocket.ts # React WebSocket hook
│ ├── constants/
│ │ ├── index.ts # Constants barrel
│ │ └── roles.ts # Role constants & helpers
│ └── errors/
│ └── index.ts # Error handler & codes
Next Steps
- REST Client - Complete REST API reference
- GraphQL Client - GraphQL client reference
- WebSocket Client - WebSocket client reference
- Error Handling - Consistent error responses
- Pre-built Routes - User, session, and audit routes
- Types - Type definitions reference