Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Dynamic GraphQL Queries #2293

Closed
kevinschaich opened this issue Sep 30, 2017 · 18 comments
Closed

Support Dynamic GraphQL Queries #2293

kevinschaich opened this issue Sep 30, 2017 · 18 comments

Comments

@kevinschaich
Copy link

kevinschaich commented Sep 30, 2017

My app has a component GridItem which currently has an img prop to generate an image. To use gatsby-image, I would have to use a GraphQL query similar to the following:

export const query = graphql
  query IndexQuery {
    gridImage: imageSharp(id: { regex: "/${this.props.img}/" }) {
      responsiveSizes(maxWidth: 960) {
        base64
        aspectRatio
        src
        srcSet
        sizes
        originalImg
        originalName
      }
    }
  }

This fails with the following error:

BabelPluginGraphQL: Substitutions are not allowed in graphql. fragments. Included fragments should be referenced as ...MyModule_foo.

Is this supported and I'm just not using the right syntax, or is it not possible at the moment?

In general, IMO handling images through a GraphQL query AND provided component is not intuitive. I like how gatsby-remark-images works. I propose either we have a plugin that automatically optimizes any images in your app, including those in the static folder, or at least have a prop in gatsby-image's Img component that allows you to explicitly specify the path of the image.

@sebastienfi
Copy link
Contributor

Maybe with something like this ?

export const query = graphql`
  query IndexQuery($id: String!) {
    gridImage: imageSharp(id: { eq: $id ){
      responsiveSizes(maxWidth: 960) {
        base64
        aspectRatio
        src
        srcSet
        sizes
        originalImg
        originalName
      }
    }
  }`

@KyleAMathews
Copy link
Contributor

GraphQL queries are run at build time not run time so you can't use React data. Instead you pass arguments to queries when creating pages & layouts.

I suggest going through part 4 of the tutorial as it discusses this topic https://www.gatsbyjs.org/tutorial/part-four/

@wiesson
Copy link

wiesson commented Oct 6, 2017

Any suggestions how to make use of imageSharp and e.g. a yml/json file with an array of images included without creating the route "programmatically"?

@mattgreenfield
Copy link

I'm struggling to achieve the exact same thing. I can get all images in a directory using graphQL but I'm not able to run them all through gatsby-images to make them responsive.
@kevinschaich - did you come up with a solution?

@AlahmadiQ8
Copy link

AlahmadiQ8 commented Sep 4, 2018

I tried to find someway around this but no luck

one way is to evaluate expressions at build time using
babel-plugin-preval babel plugin.

for example, it would be something like this

<StaticQuery
      query={graphql(preval`module.exports = require('../utils/getQuery')('my-photo.jpg')`)}
      render={data => <div>{data}</div>}
  />

//./utils/getQuery.js
module.exports = (str) => {
  return `
query {
  allImageSharp(
    filter: { fluid: { originalName: { regex: "/${str}/" } } }
  ) {
    edges {
      node {
        fluid {
          ...GatsbyImageSharpFluid
        }
      }
    }
  }
}
`
}

I took a stab on this, edited the following files

  • gatsby/src/internal-plugins/query-runner/file-parser.js
  • gatsby/packages/babel-plugin-remove-graphql-queries/src/index.js

to also process CallExpression instead of just TaggedTemplateExpression. This would allow us to do

  • query={graphql(query etc...)}

in addition to

  • query={graphqlquery etc...}

Unfortunately, this still doesn't work because I think the babel transformers are incoked after the query parsing.

@ewagstaff
Copy link

ewagstaff commented Jan 8, 2019

GraphQL queries are run at build time not run time so you can't use React data. Instead you pass arguments to queries when creating pages & layouts.

I suggest going through part 4 of the tutorial as it discusses this topic https://www.gatsbyjs.org/tutorial/part-four/

I've taken a couple stabs at this but without luck. From the example in the tutorial, it seems like for each new optimized image URL, you need a separate component. I couldn't find an example of a gatsby-image optimized component being passed an image URL from a file or elsewhere.

I understand it can't be done at run time, but I'm not sure how it can be done at build time. I'm very new to graphQL queries, so there could be something I'm missing.

@ewagstaff
Copy link

For all who arrive at this issue later, here is what eventually worked for me:

import React from 'react'
import { StaticQuery, graphql } from 'gatsby'
import Img from 'gatsby-image'

/*
 * This component is built using `gatsby-image` to automatically serve optimized
 * images with lazy loading and reduced file sizes. The image is loaded using a
 * `StaticQuery`, which allows us to load the image from directly within this
 * component, rather than having to pass the image data down from pages.
 *
 * For more information, see the docs:
 * - `gatsby-image`: https://gatsby.app/gatsby-image
 * - `StaticQuery`: https://gatsby.app/staticquery
 */

export default class Image extends React.Component {
  render(){
    return (
      <StaticQuery
        query={graphql`
          query {
            allImageSharp {
              edges {
                node {
                  fluid(maxWidth: 1200) {
                    ...GatsbyImageSharpFluid
                  }
                }
              }
            }
          }
        `}
        render={data => {
          return (
            <Img fluid={data.allImageSharp.edges.find((element) => {
              // Match string after final slash
              return (element.node.fluid.src.split('/').pop() === this.props.imgsrc);
            }).node.fluid} />
          )
        }}
      />
    )
  }
}

Then images can be used anywhere like this:
<Image imgsrc="yourimage.jpg" />

(Please let me know if there's a flaw in my implementation here.)

@0x6e6562
Copy link

0x6e6562 commented Jan 12, 2019

@ewagstaff I'd like to do the same thing as you.

I like your pragmatism. This approach might be expensive for massive builds, since you are querying a lot of data and searching through for each <Image/> instance you create. But I guess this is not too bad in practice for you?

It seems like gatsby-image is only useful for components that can define their image sources statically. It feels like the data driven approach breaks down at this point - you can create nodes from any data source, but if you want to leverage the image processing APIs, this can only be achieved from hardcoded components. Feels like invoking a runtime API from the gatsby-image module would be more flexible than the GraphQL binding.

@filipetedim
Copy link

GraphQL queries are run at build time not run time so you can't use React data. Instead you pass arguments to queries when creating pages & layouts.

I suggest going through part 4 of the tutorial as it discusses this topic https://www.gatsbyjs.org/tutorial/part-four/

I've been through it a couple of times @KyleAMathews , but no where in that tutorial it mentions arguments passing in to queries. Am I missing something?

I'm currently trying to get this going:

import Config from '../utils/config';
// ...
<StaticQuery
  query={graphql`
    query {
      allApiFields(filter: { _pageId: { eq: "${Config.pages.jobs.pageId}" } }) {
        edges {
          node {
            name
            content
          }
        }
      }
    }
  `}
/>

@jonniebigodes
Copy link

jonniebigodes commented Jan 23, 2019

@filipetedim sorry to be the bringer of bad news, but in a nutshell, no you can't do it. There's some discussion in here on that matter, but as of now, StaticQuery does not allow what you're trying to do for instance, it can't accept variables. More on the subject can be found here and here.

@0x6e6562
Copy link

@filipetedim I ended solving this issue by pushing the dynamic lookup into the node creation step, so that I could query statically. There is a discussion here which is mainly me talking about an example that I created to illustrate my point.

@filipetedim
Copy link

Will take a look!

@purecopy
Copy link

purecopy commented Aug 21, 2019

any news on this limitation?

@develerltd
Copy link

So, I wanted to create a reusable component that displayed banners given a page name. In order to do this, I edited gatsby-node to add in the pageName to the context of the page. Now the pageName comes from a file that is whatever the component file is +.context. Its an optional file so if it doesn't exist - it doesn't add it. If it exists, then it adds it into context. The code is as below:

exports.onCreatePage = ({ page, actions }) => {
  const { createPage, deletePage } = actions;

  try {
    const componentModuleContext = require(`${page.component}.context`);

    deletePage(page);
    createPage({
      ...page,
      context: {
        ...page.context,
        ...componentModuleContext,
      },
    });
  } catch (ex) {
    // dummy
  }
};

@dandv
Copy link
Contributor

dandv commented Nov 12, 2019

I'm still new to Gatsby so I hope I'm missing something. Otherwise, I don't understand all the hype about GraphQL, when Gatsby can't pass variables for rather simple requirements, like the one below.

Our CMS has blog posts with a special widget tag, which embeds content by id, say headlines:

This is a blog post about the headlines below. We think this and that.

<headline-widget id="set-of-headlines-at-some-point-in-time">

I'm creating pages per the documented pattern, then in post.html I want to replace <headline-widget id="set-of-headlines"> with the actual headlines, fetched from a GraphQL source. (in the GraphQL source the headlines are being decorated with analytics data, so copy/pasting them in the blog post isn't a way out of this.)

Since set-of-headlines-at-some-point-in-time is variable, it seems I can't use StaticQuery. Is that correct? Is there a way to fulfill my requirement with Gatsby?

@saschwarz
Copy link
Contributor

@dandv I had exactly the same problem. I had to make the mental shift to 'the gatsby way" to solve the same issue. The solution, for me, was to create a React component with a static query that returned all the possibly matching data. Then it is filtered in the component to the data that matches the prop passed into the component.

@borisdiakur
Copy link

borisdiakur commented Jan 22, 2020

@sidharthachatterjee Can you give an update / estimate here on when the suspense based renderer and useQuery API that you prototyped recently will be available in Gatsby?

Update Just seen it’s actually tracked here: #10482

Please give us an update here or there. Thanks! 🙏🙏🙏

@douglasjunior
Copy link

douglasjunior commented Oct 8, 2020

How about dynamic fields? I'm trying to implement i18n with Gastby and Strapi, so I need to return only the fields specific to the user language.

let lang; // pt, es, en, etc...

const query = useMemo(() => {
    return graphql`
        query MyQuerySecoes {
            allStrapiSecoes {
                edges {
                  node {
                    id
                    pagina
                    categoria_${lang}
                    nome_${lang}
                    titulo_${lang}
                    caracteristicas {
                        id
                        descricao_${lang}
                    }
                  }
                }
              }
            }
        }) 
    `;
}, [lang]);

But I got an error:

String interpolations are not allowed in graphql fragments

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests