GraphQL Integration
This guide covers integrating client applications with Lana's GraphQL APIs.
API Endpoints
Lana exposes three GraphQL APIs, each with its own identity and authorization context:
| API | Purpose | Typical URL |
|---|---|---|
| Admin API | Administrative operations — customers, credit, accounting | https://admin.your-instance.com/graphql |
| Customer API | Customer-facing operations — account info, facility status | https://app.your-instance.com/graphql |
| Partner API | Partner-scoped operations | https://partner.your-instance.com/graphql |
Making Requests
With curl
curl -X POST \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"query": "{ customers(first: 10) { edges { node { customerId status level } } } }"}' \
https://admin.your-instance.com/graphql
With JavaScript (Apollo Client)
npm install @apollo/client graphql
import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
const httpLink = createHttpLink({
uri: 'https://admin.your-instance.com/graphql',
});
const authLink = setContext((_, { headers }) => ({
headers: {
...headers,
authorization: `Bearer ${getAccessToken()}`,
},
}));
const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
});
With Python
import requests
url = "https://admin.your-instance.com/graphql"
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json",
}
query = """
query {
customers(first: 10) {
edges {
node {
customerId
status
level
}
}
}
}
"""
response = requests.post(url, json={"query": query}, headers=headers)
data = response.json()
Pagination
Lana APIs use cursor-based pagination following the Relay specification:
query GetCustomers($first: Int!, $after: String) {
customers(first: $first, after: $after) {
edges {
cursor
node {
customerId
status
level
}
}
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
}
}
To fetch the next page, pass the endCursor value as the after parameter.
Error Handling
Check both the top-level errors array and the operation's payload. Typed mutations return expected business rejections as a union under data, not as top-level GraphQL errors:
mutation FreezeCustomer($input: CustomerFreezeInput!) {
customerFreeze(input: $input) {
__typename
... on CustomerFreezePayloadSuccess {
customer {
customerId
status
}
}
... on CustomerFreezePayloadError {
code
}
}
}
The typed fault boundary uses these top-level error extensions:
extensions.code | extensions.retryable | Meaning |
|---|---|---|
UNAUTHORIZED | false | The subject is not authorized |
UNAVAILABLE | true | A transient dependency failure |
INTERNAL | false | An internal failure |
Query domain rejections and validated scalars can return operation-specific codes with retryable: false. Some operations still use legacy error paths, so do not assume every error has these extensions. Invalid or expired tokens can also be rejected at the HTTP authentication layer before GraphQL execution.
Inspect the current operation's payload types in the API reference, handle HTTP failures separately, and do not blindly retry mutations: reconcile their result or use the operation's idempotency contract before retrying.
Required Headers
Authorization: Bearer <access-token>
Content-Type: application/json
API References
- Admin API Reference — Complete admin operations and types
- Customer API Reference — Complete customer operations