GraphQL fragments made simple ⚡️
npm install fraql graphql graphql-tools graphql-tagFraQL solves several things:
- ☀️ Isolation: fragments don't rely on name anymore
- ✨ Mocking: generate data & props from fragments
- 🤯 Collocation: put GraphQL in your components
importgqlfrom'fraql'// Create fragment without naming it.constfragment=gql` fragment _ on Article { title description }`// Just use it in your queries!constquery=gql` query Articles { articles { id${fragment} } }`⚡️ See live example on CodeSandbox
Putting data next to your component is a good practice. It is built-in Relay and Lee Byron explains the advantages into his talk about the IDEA architecture.
I tried to do it by myself, but relying on fragment names is not an easy task. FraQL solves this issue by bringing isolation, fragments do not rely on their names.
The second problem solved by FraQL is the mocking. Generating a set of data for complex components is a pain. FraQL solves it by generating data right from your fragments!
FraQL exports a default tag function that is a drop-in replacement for graphql-tag. By using it you can create reusable fragments easily.
FraQL is not a framework, but it comes with good practices. It is recommended to create a static property fragments on your components that contains a map of component properties. For each one, you specify the associated fragment.
You may have noticed that the name of the fragment is "_". FraQL transforms your fragment into an inline fragment. You can pick any name you want, because it will be dropped the transformation anyway.
importReactfrom'react'importgqlfrom'fraql'constArticleCard=({ article })=>(<div><h1>{article.title}</h1><p>{article.description}</p></div>)// Create a map of fragments and reference them on a static property "fragments".ArticleCard.fragments={article: gql` fragment _ on Article { title description } `,}exportdefaultArticleCardWith FraQL, using a fragment into a query is obvious, just put the fragment where you want to use it.
Importing gql from fraql is not required for queries. In this case this is just a pass-through to graphql-tag. The magic behind FraQL only happens when you use it on a fragment.
importReactfrom'react'importgqlfrom'fraql'import{Query}from'apollo-client'importArticleCardfrom'./ArticleCard'// Build your query by using your fragment.constARTICLES=gql` query Articles { articles { id${ArticleCard.fragments.article} } }`constArticleList=({ articles })=>(<div><Queryquery={ARTICLES}>{({ data })=>data.articles&&data.articles.map(article=>(<ArticleCardkey={article.id}article={article}/>))}</Query></div>)exportdefaultArticleList⚡️ See live example on CodeSandbox
⚡️ See React example in this repository
Tools like StoryBook allows you to develop your components into an isolated environment. But you still have to write a set of data for displaying your components. Each time you modify your component, you have to modify this set of data, it is a real pain to maintain!
If all your components have fragments, you get mocking for free!
Mocking data from a fragment requires knowing all schema types. That's why you have to generate a introspection result from your schema in order to use mocking.
FraQL exposes a method introspectSchema to simplify this operation. The only thing you have to do is create a script that dumps your introspection result into a JSON file.
// Example of script that generates an introspection result into "schema.json".const{ writeFileSync }=require('fs')const{ introspectSchema }=require('fraql/server')constschema=require('./myGraphQLSchema')// Your schema defined server-sideconstdata=introspectSchema(schema)fs.writeFileSync('schema.json',JSON.stringify(data))FraQL exposes a method createMockerFromIntrospection that creates a mocker from your schema.json.
It is recommended to create one mocker and to use it wherever you need to generate data.
// mocker.jsimport{createMockerFromIntrospection}from'fraql/mock'importintrospectionDatafrom'./schema.json'exportdefaultcreateMockerFromIntrospection(introspectionData)You can now mock fragments using mockFragment or mockFragments methods.
Single fragment
importgqlfrom'fraql'importmockerfrom'./mocker'constfragment=gql` fragment _ on Article { id title author { name } }`constdata=mocker.mockFragment(fragment)// {// id: '4b165f7d-2ee1-4f09-8fd7-fc90d38a238a',// title: 'Hello World',// author: {// name: 'Hello World',// },// }Multiple fragments (components)
importReactfrom'react'importgqlfrom'fraql'importmockerfrom'./mocker'importArticleCardfrom'./ArticleCard'// Generate all props directly from fragments.constprops=mocker.mockFragments(ArticleCard.fragments)// Create an element using generated props.constarticleCard=<ArticleCard{...props}/>⚡️ See StoryBook example in this repository
One of the principles of React is component composition. It is recommended to do the same with your GraphQL fragments.
// ArticleTitle.jsimportReactfrom'react'importgqlfrom'fraql'constArticleTitle=({ article })=><h2>{article.title}</h2>ArticleTitle.fragments={article: gql` fragment _ on Article { title } `,}exportdefaultArticleTitle// ArticleCard.jsimportReactfrom'react'importgqlfrom'fraql'importArticleTitlefrom'./ArticleTitle'constArticleCard=({ article })=>(<div><ArticleTitlearticle={article}/><div>{article.text}</div></div>)ArticleCard.fragments={article: gql` fragment _ on Article {${ArticleTitle.fragments.article} text } `,}exportdefaultArticleCardFraQL offers a drop-in replacement for graphql-tag but sometimes you don't use gql to define your fragments. As mentioned in graphql-tag documentation there are lots of other ways to do it (using Babel, Webpack, etc..).
FraQL exposes a function toInlineFragment that transforms a GraphQL fragment into an inline fragment.
import{toInlineFragment}from'fraql'importgqlfrom'graphql-tag'importfragmentfrom'./myFragment.gql'constinlineFragment=toInlineFragment(fragment)constquery=gql` query { articles {${inlineFragment} } }`Sometimes you may want to have the best of the two worlds, use a named fragment in one query and an inline fragment in another.
For this specific use-case FraQL exposes the original document:
importgqlfrom'fraql'constfragment=gql` fragment BaseArticleInfos on Article { title text }`constquery=gql` query Articles { articles { ...BaseArticleInfos } }${fragment.originalDocument}`Mocking feature of FraQL is build on top of graphql-tools, it means you can customize all your mocks.
You can define global mocks when you create the mocker:
importintrospectionDatafrom'./schema.json'constmocker=createMockerFromIntrospection(introspectionData,{mocks: {Article: ()=>({title: 'My article title',}),},})And you can override them into mockFragment and mockFragments:
importArticleCardfrom'./ArticleCard'constprops=mocker.mockFragments(ArticleCard.fragments,{mocks: {Article: ()=>({title: 'Another title',}),},})The default export of fraql is a drop-in replacement for graphql-tag that automatically converts fragments into inline fragments.
importgqlfrom'fraql'constinlineFragment=gql` fragment _ on Article { title }`constquery=gql` { articles { id${inlineFragment} } }`Converts a fragment into an inline fragment usable in requests.
importgqlfrom'graphql-tag'import{toInlineFragment}from'fraql'constfragment=gql` fragment ArticleTitle on Article { title }`constinlineFragment=toInlineFragment(fragment)constquery=gql` { articles { id${inlineFragment} } }`Generates introspection data from a schema.
import{introspectSchema}from'fraql/server'importschemafrom'./graphqlSchema'constintrospectionData=introspectSchema(schema)Generates a mocker from an introspection result generated using introspectSchema.
You can specify mocks, using the same format as graphql-tools.
import{createMockerFromIntrospection}from'fraql/mock'importintrospectionDatafrom'./schema.json'constmocker=createMockerFromIntrospection(introspectionData)Generates mock data from one fragment.
You can specify mocks, using the same format as graphql-tools.
constfragment=gql` fragment _ on Article { title }`constdata=fraqlMocker.mockFragment(fragment)// { title: 'Hello World' }Generates mock data from a map of fragments.
You can specify mocks, using the same format as graphql-tools.
constfragments={article: gql` fragment _ on Article { title author { name } } `,book: gql` fragment _ on Book { title } `,}constdata=fraqlMocker.mockFragment(fragments)// {// article: {// title: 'Hello World',// author: {// name: 'Hello World',// },// },// book: {// title: 'Hello World',// }// }- Thanks to Relay for bringing the collocation idea
- Thanks to Lee Byron for his awesome talk about IDEA Architecture
- Thanks to Apollo for the awesome tools they provide to the GraphQL community
MIT
