2

Problem definition

In the project I'm currently working on we're using React with Apollo Client.

On all our mutations, we have the following fields in our response:

ok
errors {
  field
  messages
}

The back-end extends all mutations with these fields, hence it would be nice to have a good, short way to include these fields on all mutations in the front-end, as well as be able to change this "fragment" in the future.

Hence I'm interested in shortening these same 4 lines into 1 across all my mutations.

What I've tried so far:

I've tried looking into Apollo fragments, however they seem to require a type that the fields are on or "relating to", eg.

fragment NameParts on Person {
  firstName
  lastName
}

Here, the frament NameParts is created using Person. However, I'm interested in extending ALL mutations.

It would be nice if I could make a generic fragment like so:

fragment OkAndErrors {
  ok
  errors {
    field
    messages
  }
}

This does not seem to be possible.

I've also tried making a string, and importing it into my mutation like so:

export const OK_AND_ERRORS: string = `
  ok
  errors {
    field
    messages
  }
`;
import { gql } from "apollo-boost";
import { OK_AND_ERRORS } from "./OK_AND_ERRORS";

export const CREATE_API = gql`
  mutation CreateApi($newApi: ApiCreateGenericType!) {
    createDrugapi(newDrugapi: $newDrugapi) {
      ${OK_AND_ERRORS}
      (...rest of mutation is omitted for brevity)
    }
  }
`;

Again, it did not work.

I'm not sure if I can use gql function in a smart way with strings or JSON?

There's also inline fragments but I'm in doubt if it can be used for what I need, and the documentation of inline-fragments in Apollo is scarce.

In essence: Is there a smart way to extend Apollo mutations? Does generic fragments exist?

PeterOeClausen
  • 370
  • 3
  • 15
  • smart? I saw somwhere (on SO?) that link can be used to remove all `__typename` props in variables for mutations ... you can try to use link to add your fields to queried props in mutations (AST) – xadm May 19 '21 at 11:49
  • What do you mean by "on SO"? And what do you mean by "link"? The Apollo docs [references a way to link](https://www.apollographql.com/docs/react/api/link/introduction/) is this what you're referring to? Could be really nice if you could provide a link to the article where you've read about it :-) I hope my problem and question is understandable. – PeterOeClausen May 19 '21 at 16:37
  • 1
    yes, apollo links ... subscription config directs subscriptions queries to socket link https://www.apollographql.com/docs/react/data/subscriptions/#3-split-communication-by-operation-recommended ... remove typename? not hard to find https://stackoverflow.com/a/51380645/6124657 – xadm May 19 '21 at 18:29
  • Didn't know Apollo had middleware - perfect! Thanks a lot! I'll try it out and I'll post an answer to my question very soon :) – PeterOeClausen May 20 '21 at 07:34
  • Hi Peter @PeterOeClausen It seems like I am facing the same demand as yours, did you finally solve your question? I would like to hear any good news updated from you, could you please share your method? Thanks in advance – RH-st Jul 30 '21 at 13:41

1 Answers1

1

First of all, fragments aren't limited to Apollo but are part of just regular GraphQL queries. The GraphQL site itself actually has a nice explanation of them: https://graphql.org/learn/queries/#fragments

Essentially we can put fragments onto any query to extract data dependencies, but they're also useful for matching types using their on X type conditions.

In your case, you're saying that each mutation returns a kind of result type that has a common errors field. This tells me that you may have a MutationError kind of type already. However these MutationResult types that all have an errors fields should all implement an interface, if they don't already.

Interfaces are a great tool in the schema language to define explicitly that a type implementing it must always include a certain set of fields, in this case the errors fields. This means that we'd write our results like so:

interface MutationResult {
  errors: [MutationError!]
}

type ExampleMutationResult implements MutationResult {
  ok: Boolean
  errors: [MutationError!]
}

type UserMutationResult implements MutationResult {
  user: User
  errors: [MutationError!]
}

As you can see above, the MutationResult interface is now implemented by several results, which allows me to write a reusable fragment that can be applied to any type that implements it, e.g.

fragment MutationResultErrors on MutationResult {
  errors {
    field
    messages
  }
}

Which I can then start using for all mutation queries that I'm defining. This is more predictable and expected in GraphQL rather than doing some client-side document transformations, string interpolations in a query, or something similar, as it'll be baked into your schema.

Side note: What I'd also say is, I've found that it's commonly seen that people have started to split their mutations into "errors" and "results" and made some kind of union or interface to differentiate between the two. But often they then implement generic errors with messages. It's important to say that errors that don't carry any relational data are actually already baked into GraphQL: https://spec.graphql.org/June2018/#sec-Errors

Phil Plückthun
  • 1,381
  • 9
  • 14