Skip to content
This repository has been archived by the owner on Mar 20, 2023. It is now read-only.

feat(server fragments): server predefined fragments #576

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

Ariel-Dayan
Copy link

added function addedServerFragments that concats relevent
fragments from the server to req query.
added function findFragments that return relevant fragments
for current request.
added function sliceFirstWord.
added serverFragments option.

resolves #575

added function addedServerFragments that concats relevent
fragments from the server to req query.
added function findFragments that return relevant fragments
for current request.
added function sliceFirstWord.
added serverFragments option.

resolves graphql#575
returned build to previous version.
added serverFragments to OptionsData in index.js.
added serverFragments to OptionsData in types/index.d.ts
@acao
Copy link
Member

acao commented Nov 28, 2019

nice! so we have a standard for this using graphql-config that is used pretty widely for loading additional fragments, for example with playground or graphiql. maybe we can build off of that so folks wont have to maintain duplicate configuration for this and the likely soon to be standard graphql-config?

*
* @returns { Set<string> } - relevant fragments for current request.
*/
function findFragments(serverFragments: string, query: string, fragmentsInUsed: Set<string>): Set<string> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be much simpler and more concise to use the graphql visit() function here I think

Copy link
Member

@acao acao Dec 10, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://graphql.org/graphql-js/language/#visit just parse the operation string to AST, and visit all the FragmentSpread node types i think it would be for this case.

import { visit, print } from 'graphql'

function (serverFragments: string[], query: string) {
   const operationAST = parse(query)
   const serverFragmentString  = ''
   visit(operationAst, {
     FragmentSpread(node) {
      if(serverFragments.includes(node.name.value)) {
          serverFragmentString += print(node)
       }
     }
   })
   return serverFragmentString 
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could be wrong though, about which option is more performant, as parsing each query and then re-printing the nodes entails another expense, though much of this could be memoized, eventually cached even?

})

return `${fragmentsInUsed}\n${query}`;
}
Copy link
Member

@acao acao Dec 10, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EOF issue

let fragmentsInUsed = '';

[...(findFragments(serverFragments, query, new Set()))].forEach(fullFragment => {
fragmentsInUsed += `fragment ${fullFragment} `;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

itll probably be easier and more perfomant to generate this from AST as well

@acao
Copy link
Member

acao commented Dec 10, 2019

also https://travis-ci.org/graphql/express-graphql/jobs/618258347#L236

this is looking good though! i'll gladly help you get it ready for @IvanGoncharov to give this a final pass

as per my earlier comment re: graphql-config, it will make sense as an abstraction around this interface.

@IvanGoncharov
Copy link
Member

@Ariel-Dayan @acao Don't want to be a party killer but I don't think it's the right feature for the reference implementation of GraphQL Server.
By supporting incomplete queries (in this case without fragments) we force the entire ecosystem (client tools, graphql gateways, etc.) to do the same.

  1. Shorten the query for every client by avoiding to repeat the fragment declaration.

This is solved by persistent queries

Allow the client to have dynamic fragments that are updated server side. For example, if a field was added to the schema server side, the fragment will update accordingly.

It brake assumption for client-side tools they since they were compiled/deployed with different sets of fields and this invalidates GraphQL type-safety/"predictable payload" principle.

At the same time if you want you can add this feature quite easily in your code without modifying express-graphql:

import { parse } from 'graphql';
// ...
app.use(
  '/graphql',
  graphqlHTTP({
    schema: MySessionAwareGraphQLSchema,
    graphiql: true,
    customParseFn(source) {
      const ast = parse(source);
      // code that inject fragments into ast
      const serverFragmentString  = ''
      visit(ast, {
        FragmentSpread(node) {
          const name = node.name.value;
          if(serverFragments.includes(name)) {
            serverFragmentString += serverFragments.get(name);
          }
        }
      });
      
      return concatAST(ast, parse(serverFragmentString));
    }
  }),
);

Base automatically changed from master to main February 10, 2021 15:09
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

feat: add fragments server side
3 participants