GraphQL API Contract
Relevant source files
The following files were used as context for generating this wiki page:
The GraphQL API Contract defines the communication interface between the ProductCompare frontend and its Phoenix-based backend. This contract adheres to Relay-first principles, utilizing global IDs, connections for pagination, and structured mutation payloads to ensure a consistent and scalable data architecture.
The API serves as the primary entry point for browser-based authentication, product catalog exploration, and comparison management. It is hosted at the /api/graphql endpoint and acts as the single source of truth for the system's data model and business logic transitions.
Sources: ARCHITECTURE.md:12, backend_starting_guide.md:7, AGENTS.md:14
Core Architectural Principles
The API is built using Elixir's Absinthe library and follows "Relay-modern" conventions. Key requirements include:
- Global IDs: All refetchable domain entities must implement the
Nodeinterface with a uniqueid. - Relay Mutations: Mutations use an
inputobject for parameters and return a payload containing the updated data and structured errors. - Deterministic Ordering: All list surfaces (connections) must provide a stable sort order.
- Context-Aware: GraphQL operations are executed within a context that handles authentication via Bearer tokens or session cookies.
Sources: backend_starting_guide.md:104-128, assets/schema.graphql:6-8
API Flow Diagram
The following diagram illustrates how the frontend interacts with the GraphQL endpoint, including the handling of SSR contexts and browser-based requests.
The diagram shows the request pipeline from the client through the Phoenix router and authentication layer to the Absinthe execution engine.
Sources: backend_starting_guide.md:94-102, ARCHITECTURE.md:12, assets/src/relay/fetch-graphql.ts:21-41
Authentication and Session Management
Authentication is handled via specific GraphQL mutations. Unlike legacy systems, the frontend does not use REST for login or registration; it relies entirely on the GraphQL schema. The backend remains the authority for cookie-backed sessions, which are set or cleared by these mutations.
Sources: AGENTS.md:14-17, ARCHITECTURE.md:12
Auth Mutations
| Mutation | Input Parameters | Return Type | Description |
|---|---|---|---|
login | email, password | AuthSessionPayload | Authenticates user and sets session cookie. |
register | email, password | AuthSessionPayload | Creates a new user and signs them in. |
logout | N/A | LogoutPayload | Clears the current session cookie. |
forgotPassword | email | AuthActionPayload | Triggers a password reset email. |
resetPassword | token, password | AuthActionPayload | Updates password using a reset token. |
verifyEmail | token | AuthActionPayload | Marks a user's email as verified. |
Sources: assets/schema.graphql:11-18, assets/src/routes/auth/actions.ts:25-83
Structured Mutation Errors
All mutations return an errors field containing a list of MutationError objects. This allows for fine-grained, field-level feedback in the UI.
type MutationError {
code: String!
message: String!
field: String
}
Sources: assets/schema.graphql:36-40, assets/src/routes/auth/actions.ts:4-8
Catalog and Product Schema
The catalog surface utilizes Relay Connections to handle product listings and merchant offers. The schema defines products, brands, and merchants with specific relationships.
Schema Relationships
The following diagram represents the core entities available through the GraphQL API and their relationships.
Key Query Endpoints
| Field | Arguments | Return Type | Description |
|---|---|---|---|
node | id: ID! | Node | Global ID lookup for refetching. |
viewer | N/A | User | Returns the currently authenticated user. |
products | first: Int, after: String | ProductConnection | Paginated list of products. |
product | slug: String! | Product | Retrieves a single product by its slug. |
merchantProducts | input: MerchantProductsInput! | MerchantProductConnection | Retrieves offers for a specific product. |
Sources: backend_starting_guide.md:106-110, assets/schema.graphql:10, assets/src/routes/products/api.ts:28-65, assets/src/routes/catalog/api.ts:13-28
Network Layer and Transport
The frontend communicates with the GraphQL API through a centralized fetchGraphQL helper. This helper manages:
- SSR Context Forwarding: During Server-Side Rendering (SSR), it forwards cookies and origin headers from the incoming browser request to the Phoenix backend.
- Credentials: In the browser, it sets
credentials: "include"to ensure session cookies are sent with every request. - Endpoint Resolution: It dynamically resolves the API URL based on environment variables (
VITE_API_BASE_URL) or local dev defaults.
Sources: assets/src/relay/fetch-graphql.ts:21-65
Sequence: Authentication Flow
Data Consistency and Performance
The API leverages several backend patterns to maintain performance:
- Dataloader: Used for request-level batching of catalog, pricing associations, and latest-price lookups to prevent N+1 query problems.
- Entropy IDs: While internal relations use
bigint, external-facing GraphQL IDs are typically derived fromentropy_id(UUIDv7) to prevent ID scraping. - Relay Modern Flavor: The schema uses
Absinthe.Relay.Schema, flavor: :modernto ensure compatibility with modern Relay features.
Sources: ARCHITECTURE.md:27-30, backend_starting_guide.md:10-15, 104
Summary
The GraphQL API Contract serves as the rigorous bridge between the Elixir backend and React frontend. By strictly adhering to Relay conventions and centralizing authentication within the schema, the project ensures that data fetching is predictable, type-safe, and performant across both client-side and server-side rendering environments.