Axon 911 Frontend Docs
    Loading navigation…
U
User
Switch to light mode
  1. Working With Data
  2. Graphql Backend Responses

GraphQL Backend Responses

What a mutation or subscription response needs so the frontend cache updates without a refetch, and how to ask for a change

Every screen in a shell reads one normalized Apollo cache. When a mutation or subscription returns an entity with its id and the fields that changed, Apollo merges it into the cache and every component that shows it updates, with no extra request. When the response leaves that out, the frontend has to guess with cache.modify or refetch a query, and a refetch puts every component that reads the query into a refetching state. This page is for developers porting code that reuses the existing Prepared911 data. The schema comes from the graphql-ruby types in the Prepared911 repository, composed by AxonMundi and vendored here as packages/data-gql/src/schema.graphql. You can't fix a response shape from this repository, but you can ask for it.
What a response needs
  • A mutation returns the entities it changed. Return each changed entity with its id and changed fields, and the parent that owns a list or count it changed. A delete returns the deleted id (and the parent). ok: true is enough only when no cached object changed.
  • Every type has a stable id: ID!. It stays the same for the life of the record and whichever operation returns it, so a chatroom from a list, a subscription and a mutation all land on one cache entry. A type without an id is stored inside its parent and can't be shared between queries.
  • A subscription carries the entity and what happened. Send the changed entity, plus a kind (CREATED, UPDATED, REMOVED) or a separate event per kind, so the client knows whether to add it to a list, update it, or drop it. Prefer an enum to a string. A subscription that only says "something changed" makes every subscriber refetch.
  • Filters are arguments on the list. Put search and filter arguments on the list or connection field itself, so each filter set has its own cached list, rather than on a wrapper object above it.
The client chooses how much of a response it selects, so returning the entity doesn't make payloads bigger. Don't replace an entity on a payload with scalars: chatroomLock returns { id, locked } on its own payload type, so Chatroom.locked stays stale everywhere until something refetches it. chatroomBookmarkDelete is the pattern to copy: it returns the deleted id and the parent chatroom. Apollo recommends the same shape in Include modified objects in mutation responses.
When porting
If an operation forces a refetchQueries, a refetch from a subscription handler, or a cache.modify that only exists because the payload is missing data, consider asking the backend to improve its shape. Port the operation with the workaround first, so the port isn't blocked, and say in a comment which request would remove it. See Mutation order for the workaround and Freshness and Loading States for how a refetch affects other components.
Request checklist
Work through this before you write the request:
  1. Name the operation and the screen. Which mutation or subscription, and which components show the data it changes.
  2. List what changes on the server after it runs. Every entity whose fields change, every list one joins or leaves, every count that moves.
  3. Check the payload in schema.graphql. If each changed entity is reachable from the payload, directly or through a back-reference such as chatroomAnalysis.chatroom, select it: you don't need the backend.
  4. Check identity. Does each changed type have a stable id: ID!, or is it only keyed by cache-key-fields.json?
  5. For a delete, check that the removed id and the parent come back.
  6. For a subscription, check that the event carries the entity and says whether it was created, removed or left a list.
  7. Say what you do today (refetchQueries, cache.modify, polling) and what it costs (a refetch on every component that reads the query, a skeleton flash, an extra request).
  8. Ask for the smallest change. Usually that is one more field on the payload. Don't ask for a new query when a payload field will do.
Request template
Paste this into a ticket or a message to the schema's owners:
Example
chatroomLock and chatroomUnlock return ChatroomLockPayload { chatroom, id, locked } (schema.graphql:10694). The chatroom field is deprecated with "Use id and locked instead", and id and locked sit on the payload, so Apollo stores them as a ChatroomLockPayload record and Chatroom.locked never changes.
  • What the frontend can do today: write locked onto the chatroom with cache.modify after the mutation, or refetch the chatroom queries. Both exist only because of the payload.
  • Requested change: keep chatroom and drop its deprecation, so a document selects chatroom { id locked } and every component that shows the chatroom updates on its own.
Related documentation
  • GraphQL Cache - Updating the cache, and the order to reach for
  • Freshness and Loading States - What a refetch does to other components
  • Pub/Sub - Subscriptions and live updates
Previous
Working with data / GraphQL Freshness and Loading States
Next
Working with data / Local Storage
On this page
What a response needs
When porting
Request checklist
Request template
Example
Related documentation