Skip to content

Latest commit

 

History

History
83 lines (59 loc) · 5 KB

graphql.md

File metadata and controls

83 lines (59 loc) · 5 KB

GraphQL Worker Architecture

The GraphQL worker provides a GraphQL API surfacing the PeeringDB dataset using graphql-yoga. It is driven by the schema extraction pipeline.

Request Flow

Client → wrapHandler (error trap + telemetry headers)
       → extractApiKey / extractSessionId → authenticated
       → isRateLimited (isolate-level, per-IP or per-identity)
       → validateRequest (method checks, URL validation)
       → env.PDB.withSession("first-unconstrained") → db (global connection)
       → Router
           → GET / → GraphiQL UI (handlers/static.js)
           → POST/GET → graphql-yoga pipeline (handlers/query.js)
               → L1 Read + SWR (SHA-256 keyed)
               → yoga.fetch() → Resolver Map → D1

Module Layout

graphql/
├── index.js              Router, rate limiter init
├── cache.js              LRU cache, TTLs, withGqlSWR(), graphqlCacheKey()
└── handlers/
    ├── query.js          handleGraphQLQuery (yoga pipeline + SWR)
    └── static.js         serveGraphiQL (custom-branded explorer UI)

Dependencies flow downward: handlers → cache.js → core/. No cross-worker imports — shared logic lives in core/.

Schema & Resolvers

Unlike traditional GraphQL implementations where schemas are authored by hand, this API is generated by scripts/gen_graphql_schema.py.

  • Type Definitions (extracted/graphql-typedefs.js): Emits SDL strings establishing GraphQL object types, Connection configurations (for Relay specification capabilities), and input filter objects.
  • Resolvers (extracted/graphql-resolvers.js): An exported map dictating how queries map into the shared API query builder.

Resolver Patterns

  1. List Queries: Support offset/limit offset pagination (limit, skip) mapped directly to SQL limits via buildJsonQuery.
  2. Detail Queries: Fetch a single entity by ID.
  3. Forward Foreign Keys (Relationships): e.g. Network.organization. Resolves the parent Organization using the stored org_id.
  4. Reverse Edge Navigation: e.g. Network.facilities. Plural endpoints nested inside an entity definition mapped to an implicit Foreign Key filter.
  5. Connections (Relay Pagination): Extends list queries to emit Relay-compliant pagination (nodes, edges, pageInfo, cursor) leveraging base 64 encoded offsets.

PDB+ Naming Aliases

The codebase aims for drop-in compatibility with older downstream client expectations like PeeringDB Plus, generating backward compatible aliases (e.g., querying internetExchanges which maps natively to the ix table). These overrides are defined via the naming meta block inside ENTITY_NAMING in parse_django_models.py.

Restricted Entities

Entities marked _restricted: true in the entity registry (currently only poc) are restricted by the shared WHERE builder (buildWherePagination in api/query.js), not by the resolvers. Every resolver passes ctx.authenticated through opts.authenticated; for anonymous callers the builder pins visible = 'Public' and ignores any caller-supplied visible filter. The same rule applies to the API and REST workers.

Resolver Anonymous Behaviour
listResolver (e.g. pocs) Public contacts only, with or without other filters.
detailResolver (e.g. poc(id: N)) The contact if it is public, else null (matches upstream /api/poc/{id}).
reverseEdgeResolver (e.g. Network.pointsOfContact) Public contacts only, matching the API worker's depth expansion.
connectionResolver (e.g. pocsConnection) Public contacts only, in both totalCount and the page.

Authenticated callers receive all visibility levels without restriction.

Caching Strategy

SHA-256 Query Hashing

Because GraphQL requests generally employ query bodies over POST, cache keys are formulated by generating a SHA-256 digest of the raw POST string.

  • Keys are partitioned by authentication scope prefixed with either auth: or anon:.
  • This ensures authenticated queries (which can retrieve private points of contact) never poison the cache for anonymous users attempting the same query body.

L1 Cache

  1. L1 LRU: A 2000-slot LRU cache in cache.js. Due to the high combinatorial possibility of uniquely shaped GraphQL queries, a GraphQL request retains a ~10% capacity isolation ratio.

SWR (Stale-While-Revalidate)

withGqlSWR() in cache.js wraps core/swr.js, injecting the GraphQL-specific cache, TTL, and sentinel. The flow matches the API worker pattern: L1 hit → stale-serve + background refresh → miss.

Interactive UI (GraphiQL)

The interactive API shell natively bundled inside Yoga is disabled (graphiql: false), replaced with a custom-branded interface served by handlers/static.js.

The custom page inside frontend/api/graphql.html establishes the interactive API explorer. It bypasses pdbfe.dev Cloudflare Access restrictions by inlining standard CSS and directly importing Inter font assets from the Google Web Fonts CDN.