Handler: app/api/github/graphql/route.ts
Registry: lib/github/operations/registry.ts
Request contract
POST /api/github/graphql
Authorization: via gh_session cookie or Bearer header
{
"operationName": "ViewerProfile", // REQUIRED — must be in allowlist
"query": "...", // optional — server may substitute its own
"variables": {} // optional
}Allowlist
Exactly 8 operations are exposed to clients (USER_GITHUB_GRAPHQL_OPERATIONS in lib/github/operations/registry.ts):
| # | Operation | Purpose |
|---|---|---|
| 1 | ViewerProfile | User profile + org memberships |
| 2 | SearchUsers | User search (debounced, 300 ms) |
| 3 | OrganizationSummary | Org metadata / counts |
| 4 | OrgRepositories | Org repos paginated |
| 5 | RepoScoringData | Commits / PRs / issues for scoring |
| 6 | OrgTeamsData | Teams + members |
| 7 | OrgScoringData | Org-level scoring data |
| 8 | OrganizationRepositories | Repos with metadata |
Sending any other operationName returns 403.
Query resolution
The registry holds a OPERATION_QUERY_FALLBACKS map with server-owned query text for each operation.
Resolution order:
- If a server fallback exists for the operation → use it (client-supplied query text is ignored)
- If no fallback exists → use client-supplied query text
- If neither exists → 400
In practice, always send query in your request. The server may substitute its own version, but the field is not forbidden.
Authentication
The proxy uses the user OAuth token from the session — not an installation token.
getRequestSession() → decrypt token → createUserOctokit(accessToken)This means only operations valid for a user token are supported here. Server-side ingest queries (see below) use installation token auth via lib/github/fetch-graphql.ts.
Error responses
| Status | Condition |
|---|---|
| 400 | Missing or empty operationName |
| 400 | Missing query and no server fallback |
| 401 | No session or session expired |
| 403 | operationName not in allowlist |
| 500 | GitHub GraphQL returned an error |
Caching
Cache implementation: lib/github/graphql-response-cache.ts
| Layer | Strategy | TTL |
|---|---|---|
| In-memory | LRU(64) | 90 s |
| Redis | Standard key | 90 s |
Redis (OrgRepositories) | Separate key | 5 min |
Server-only queries (not exposed to clients)
The ingest pipeline uses 5 additional queries that are never in the public allowlist:
repoScoringDataCorerepoScoringDataPrMetarepoScoringDataReviewsrepoScoringDataIssueLinks
These live under lib/github/queries/ and run with installation token auth in lib/github/fetch-graphql.ts. They cannot be called through POST /api/github/graphql.
Adding a new operation
- Add the name to
USER_GITHUB_GRAPHQL_OPERATIONSinlib/github/operations/registry.ts - Add query text to
OPERATION_QUERY_FALLBACKSin the same file - Create a frontend hook in
hooks/queries/
Full guide: /docs/contributing/adding-graphql-operations