Reputation: 606
I'm working on an app that allows users to upload large datasets. The uploader has a "draft" version that they can edit in the UI, then they publish snapshots of the draft, which all users can see. The draft files should only be queried if the uploader is logged in.
While non-edit privilege users don't see the draft, the app still queries its entire fileset, which makes fetching the dataset page really slow.
The query has a fragment for the "drafts" data, including the conditionally unwanted files. If I omit the files from the fragment, the component works for all users...but then the uploader can't get their draft files. So it's this damned if I do, damned if I don't quandary and my lack of experience with graphql and apollo really shines here.
Here's the query and accompanying hook:
export const getDatasetPage = gql`
query dataset($datasetId: ID!) {
dataset(id: $datasetId) {
id
created
public
following
starred
...DatasetDraft
...DatasetPermissions
...DatasetSnapshots
...DatasetIssues
...DatasetMetadata
...DatasetComments
uploader {
id
name
email
}
analytics {
downloads
views
}
onBrainlife
}
}
${DatasetQueryFragments.DRAFT_FRAGMENT}
${DatasetQueryFragments.PERMISSION_FRAGMENT}
${DatasetQueryFragments.DATASET_SNAPSHOTS}
${DatasetQueryFragments.DATASET_ISSUES}
${DatasetQueryFragments.DATASET_METADATA}
${DATASET_COMMENTS}
`
export const DatasetQueryHook = ({ datasetId }) => {
const {
data: { dataset },
loading,
error,
} = useQuery(getDatasetPage, {
variables: { datasetId },
})
if (loading) {
return <Spinner text="Loading Dataset" active />
} else {
if (error) Sentry.captureException(error)
return (
<ErrorBoundary error={error} subject={'error in dataset page'}>
<DatasetQueryContext.Provider
value={{
datasetId,
}}>
<DatasetPage dataset={dataset} />
</DatasetQueryContext.Provider>
</ErrorBoundary>
)
}
}
And this is the fragment:
export const DRAFT_FRAGMENT = gql`
fragment DatasetDraft on Dataset {
id
draft {
id
modified
readme
partial
description {
Name
Authors
DatasetDOI
License
Acknowledgements
HowToAcknowledge
Funding
ReferencesAndLinks
}
files { //when this is removed, it works...except for users with edit privileges
id
filename
size
}
summary {
modalities
sessions
subjects
subjectMetadata {
participantId
age
sex
group
}
tasks
size
totalFiles
dataProcessed
}
}
}
`
Long story short: The query should omit draft files if the user doesn't have edit privileges. I know the logic for hasEdit, but I don't know how I can implement it using a directive. Can I even use @skip
on a fragment, and even if so, can I pass its variable conditionally? I can't find anything in the docs about this and at a loss for a solution.
Upvotes: 3
Views: 5740
Reputation: 84777
Yes, directives can be used inside fragments and, yes, you can use a variable for this.
Here's a simple example using the SWAPI API:
query MyQuery ($showDate: Boolean!){
allFilms {
films {
...FilmFields
}
}
}
fragment FilmFields on Film {
id
title
releaseDate @include(if: $showDate)
}
One thing to note here is that the variable is still defined as part of the operation. That means if you put your fragments in separate files and then import them inside your queries, you need to make sure A) the operation actually defines the variable and B) the name of the variable is correct.
Also worth noting is that there is experimental support for defining variables as part of fragments. This has to be explicitly enabled server-side. If you're using graphql-tag, it also has to be explicitly enabled there as well. If you're using makeExecutableSchema
or Apollo Server, you can pass in a parseOptions
parameter to enable this feature server side:
makeExecutableSchema({
typeDefs,
resolvers,
parseOptions: {
experimentalFragmentVariables: true,
},
})
Then you can create fragments like:
fragment FilmFields on Film ($showDate: Boolean!) {
id
title
releaseDate @include(if: $showDate)
}
Upvotes: 12