@apollion-dsi/relay
v0.29.0
Published
Frontend services regarding Relay Environment
Readme
@apollion-dsi/relay
Configure and use Relay (react-relay / relay-runtime) the Apollion DS
way — Environment creation with auth, multipart uploads, WebSocket
subscriptions, and httpOnly-cookie sessions, without the boilerplate.
- Environment in one call.
CreateRelayEnvironmentwires the network layer, auth, and error handling from a single config object. - Two auth models.
Bearertokens or secure httpOnly cookies — switch with one flag (see below). - Persisted queries. Ship only build-time hashes over the wire — the server executes nothing outside its allowlist (see below).
- Batteries included. Multipart file uploads,
graphql-wssubscriptions, and pluggable network retry / refresh ship with the package. - Typed. Ships its own types for every option.
Installation
react is a peer dependency (^19.0.0); the Relay stack (react-relay /
relay-runtime 21.x, graphql 16.x, graphql-ws, fetch-multipart-graphql,
js-cookie) ships with the package:
yarn add @apollion-dsi/relay reactTo generate Relay artifacts, also add
relay-compileras a dev dependency and arelay.config.js.
Documentation
- Docs & API reference: https://www.apollion.com.br
- Changelog: https://www.apollion.com.br/changelog
Quick start
Create the Environment and provide it to your tree with EnvironmentProvider
— the single Relay provider, backing both the DS useEnvironment() hook and
every react-relay store hook (useLazyLoadQuery, usePreloadedQuery,
useFragment, useSubscription). Do not also mount react-relay's
RelayEnvironmentProvider yourself — EnvironmentProvider already does:
import { CreateRelayEnvironment, EnvironmentProvider } from '@apollion-dsi/relay';
import { App } from './app';
const { Environment } = new CreateRelayEnvironment({
url: 'https://api.example.com/graphql',
});
<EnvironmentProvider environment={Environment}>
<App />
</EnvironmentProvider>;Authentication: Bearer vs. httpOnly cookie
The Environment supports two auth models via authMode.
authMode: 'bearer' (default)
Reads the sessionToken from storage (localStorage or a JS-readable cookie)
and injects Authorization: Bearer <token> into every request; session
verification uses the ${authUrl}user/me probe.
new CreateRelayEnvironment({
url: 'https://api.example.com/graphql/',
authUrl: 'https://api.example.com/auth/',
useAuthorization: true,
storageType: 'cookie', // JS-readable cookie, or 'localStorage'
});authMode: 'cookie' (httpOnly session — recommended for SPAs)
The session token lives in an httpOnly + SameSite cookie, invisible to JS
and immune to XSS. The Environment never reads the token or sets
Authorization — credentials: 'include' makes the browser attach the cookie
automatically. On an auth error (e.g. 401), a refresh POST is fired to
authUrl with credentials, without probing user/me.
new CreateRelayEnvironment({
url: 'https://api.example.com/graphql/',
authUrl: 'https://api.example.com/auth/refresh/',
authMode: 'cookie',
redirectOnError: true,
loginRoute: '/login',
});| Option | Default | What it does |
| ----------------- | ---------------------------------- | ----------------------------------------------------------------------------------------- |
| authMode | 'bearer' | 'bearer' (token + header) or 'cookie' (httpOnly). |
| credentials | cookie→'include'; bearer→omitted | RequestCredentials forwarded to the GraphQL and refresh fetches. Works in both modes. |
| sessionCheckUrl | mode-dependent | Overrides the probe URL; false disables it (auth error → direct logout, no extra call). |
Persisted queries
With usePersistedQueries: true, every operation ships as a build-time
hash instead of GraphQL text. The server keeps the matching query map and
executes only hashes it knows — a tampered client query is refused, and the
server can cache aggressively by hash.
- Enable
persistConfigin yourrelay.config.jsonsorelay-compilerwrites the query map and stamps each artifact'sid:
{
"src": "./src",
"schema": "./schema.graphql",
"language": "typescript",
"persistConfig": { "file": "./persisted/queryMap.json", "algorithm": "MD5" }
}Ship
queryMap.jsonto your GraphQL server as the allowlist.Turn the flag on:
new CreateRelayEnvironment({
url: 'https://api.example.com/graphql/',
usePersistedQueries: true,
});The request body becomes { name, doc_id, variables } — no query key.
Uploads carry the hash inside the multipart operations field, and
subscriptions send it in payload.extensions.doc_id over graphql-ws.
| Option | Default | What it does |
| ------------------------- | ---------- | ------------------------------------------------------------------------------ |
| usePersistedQueries | false | Sends only the persisted hash; the GraphQL text never leaves the build. |
| persistedOperationField | 'doc_id' | Renames the hash field to whatever your server's allowlist middleware expects. |
Server contract: resolve the hash against the query map; refuse raw text and unknown hashes replying
200+{ "errors": [...] }withContent-Type: application/json— a4xxis treated as a transport failure by the client's retry layer instead of surfacing the GraphQL error.
License
MIT.
