How to load image in Gatsby GraphQL query - graphql

I have small SQLite database with products, that have relative paths to images.
I'd like to include these images to my page. I'm trying to use this query:
query {
allContent {
nodes {
Category
Description
Price
ProductID
Title
ImageURI {
childImageSharp {
gatsbyImageData(width: 200)
}
}
}
}
}
But I have this error:
Field "ImageURI" must not have a selection since type "String" has no subfields.
This can happen if you e.g. accidentally added { } to the field "ImageURI". If you didn't expect "ImageURI" to be of type "String" make sure that your input source and/or plugin
is correct.
However, if you expect "value" to exist, the field might be accessible in another subfield. Please try your query in GraphiQL and use the GraphiQL explorer to see which fields
you can query and what shape they have.
It is recommended to explicitly type your GraphQL schema if you want to use optional fields. This way you don't have to add the mentioned
"dummy content". Visit our docs to learn how you can define the schema for "undefined":
https://www.gatsbyjs.com/docs/reference/graphql-data-layer/schema-customization#creating-type-definitions
As I understand, I should convert String to File in my query. Is it right? If yes, how to do that?
Full code & repo to reproduce: https://github.com/vladd11/gatsby-test/blob/master/src/pages/index.js

Just query allFiles outside of allContent nodes. Like:
allFile {
edges {
node {
relativePath
childImageSharp {
gatsbyImageData(width: 200)
}
}
}
}
Don't forget to add new source of data to Gatsby Config:
{
resolve: "gatsby-source-filesystem",
options: {
path: `${__dirname}/static`, // Directory with your images
name: "images",
},
}
Find image in this directory using relative path, then load image dynamically:
getImage(data.allFile.edges.find(value => value.node.relativePath === product.ImageURI).node)
Use GatsbyImage to show this image:
<GatsbyImage alt={product.Title} image={Image}/>

Related

Can I create two different graphql query for two different folders separately in my Gatsby project

I'm a newbie programmer and Recently I want to add a blog page to my website. I already have a projects page using Graphql and .md files, now my problem is, every time I try to create a blog post with .md, it appears also in my projects page, could you please advice me with some of your kind experience on solving this issue and do I have to create a separate query? even I don't know how?
this is my current query.
export const query = graphql`
query ProjectsPage {
allMarkdownRemark(sort: { fields: frontmatter___date, order: DESC }) {
nodes {
frontmatter {
slug
stack
title
thumb {
childImageSharp {
gatsbyImageData(placeholder: BLURRED, layout: FULL_WIDTH)
}
}
}
id
}
}
}
One easy thing you can do is to add a key field on your markdown files to distinguish between posts and projects. This will allow you to filter GraphQL queries. In the same way, you can also place the markdowns in separate folders and filter them using the relative/absolute path.
export const query = graphql`
query ProjectsPage {
allMarkdownRemark(filter: { frontmatter: {key: { eq: "project" }}, sort: { fields: frontmatter___date, order: DESC }) {
nodes {
frontmatter {
slug
stack
title
thumb {
childImageSharp {
gatsbyImageData(placeholder: BLURRED, layout: FULL_WIDTH)
}
}
}
id
}
}
}
As I said, this approach assumes that you have created a key: project field in your markdown files. You can do the same using key: article.
Alternatively, placing the markdowns in different folders using fileAbsolutePath/relativePath if needed in the same way. This approach will rely on your filesystem structure to determine the available paths. Both approaches are based on a filtered query, choose whatever suits you better.
Use the GraphiQL playground to test all queries, paths and filters at localhost:8000/___graphql

Graphql, return result only if record not null

Totally new in Gatsby (my app is build in here). Also new in Graphql but I go this project from a colleague and now I need to find a way to sort it out.
I have a query in graphql that looks as follow:
query MediasQuery {
allStrapiMedias(sort: { fields: [date], order: DESC }) {
edges {
node {
id
name
description
date(formatString: "Do MMMM, YYYY")
link
poster {
childImageSharp {
fluid {
src
}
}
}
file {
url
}
}
}
}
}
The issue I am facing is that the file record can exist or not, to be more accurate, if link record is empty, then I should find a file (this is how it's been build in Strapi).
If I have at least one record with file then the query run normally, but as soon as I deleted this record from database all the app crash.
How can I make that query to only return file if url is empty or null?
You are querying an object that will have one property (file) filled or not with an url. You just need to add that logic to your JavaScript and React code. When you will print the file, add the new logic, something like:
if (data.allStrapiMedias.edges.node.file) return <ComponentA/>
else return <ComponentB/>
I think that what you are looking for is something that the previous snippet. However, answering your question, you achieve it by adding filters to your query:
query MediasQuery {
allStrapiMedias(
sort: { fields: [date], order: DESC }
filter: { file: {url: {ne: null }}}
) {
edges {
node {
id
name
description
date(formatString: "Do MMMM, YYYY")
link
poster {
childImageSharp {
fluid {
src
}
}
}
file {
url
}
}
}
}
}
More details in GraphQL Query Options Reference.

Using frontmatter in a markdownfile to query for images in a page query

I'm using GraphQL from within a Gatsby project. I have a set of markdown files for a blog-like section of the site. In the frontmatter of each markdown file, there's an image property.
What I'd like to do is use Gatsby's fine image api to load the image in the frontmatter. When viewing an individual post (the ones created via createPage api), this works just fine because I can provide the frontmatter.image in the context. Here's what that query looks like.
export const pageQuery = graphql`
query($slug: String!, $image: String) {
markdownRemark(frontmatter: { slug: { eq: $slug } }) {
html
frontmatter {
date(formatString: "MMMM DD, YYYY")
slug
title
image
}
}
coverImage: file(relativePath: { eq: $image }) {
childImageSharp {
fluid(maxWidth: 1440) {
...GatsbyImageSharpFluid
}
}
}
}
`
On my index page where I'm displaying all these posts though, I want to display a smaller version of this image. I can get the image from the front matter easy enough, but I'm not sure how to integrate that into the query.
export const pageQuery = graphql`
query {
allMarkdownRemark(sort: { order: DESC, fields: [frontmatter___date] }) {
edges {
node {
id
excerpt(pruneLength: 250)
frontmatter {
date(formatString: "MMMM DD, YYYY")
slug
title
image # <--- want to use this in a file query
}
}
}
}
}
`
As far as I understand, I can't use string interpolation in a static query in the component where the image is actually used, so I need to get it here in the page query. Is what I'm trying to do possible? Is there a better way to handle this?
This "link" between your frontmatter's image string and an actual image file node (processed with Sharp) is called a foreign-key relationship.
Creating foreign-key relationships in Gatsby
There are two ways of doing this:
Using mappings in gatsby-config.js
Using a GraphQL #link directive through Gatsby's schema customization (from v2.2)
I recommend the second option, since it's a more GraphQL way of doing things, and happens in gatsby-node.js where most node operations are taking place. However, if you're starting out with Gatsby and GraphQL, the first option might be easier to set up.
Implementing the #link directive in gatsby-node.js
In your case, using the #link GraphQL directive, you would probably end up with something like this in your gatsby-node.js:
exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions
const typeDefs = [
`type MarkdownRemark implements Node { frontmatter: Frontmatter }`,
`type Frontmatter {
# you may need to adapt this line depending on the node type and key
# that you want to create the relationship for
image: File #link(by: "relativePath")
}`
]
createTypes(typeDefs)
}
If you want to see an example in the wild, check out gatsby-node.js in robinmetral/eaudepoisson.com.
Query processed images via your frontmatter
Finally, you'll be able to query like this:
{
allMarkdownRemark {
edges {
node {
frontmatter {
date
slug
title
# image now points to the image file node
image {
childImageSharp {
fluid(maxWidth: 1024) {
...GatsbyImageSharpFluid
}
}
}
}
}
}
}
}

How to define an optional field in graphQL fragment for query

Tha database for that queries comes up to a set of YAML files in a folder.
Using GatsbyJS theses files are collected and are available by graphQL requests.
Not all source files defines a label field.
Is it possible to return a default or empty value for that field when missing?
Actually I get following error:
Cannot query field "label" on type "EngagementsYamlTo".
This is my query:
{
stable: allEngagementsYaml(filter: {stable: {eq: true}, to: {}}) {
edges {
...engagementNode
}
}
unstable: allEngagementsYaml(filter: {stable: {eq: false}}) {
edges {
...engagementNode
}
}
}
fragment engagementNode on EngagementsYamlEdge {
node {
heading
description
iconClass
stable
to {
href
label
}
}
}
Hm ... that was not a problem of the query.
None of the source files had a label field, so that schema was never touched. Having at least 1 file with label field will return a valid response.
Clarified for this case!

Use absolute path for featured image in markdown post with Gatsby

I've followed Gatsby tutorial for Working With Images in Markdown Posts and Pages which is working well but what I want to achieve is to fetch image from a static location instead of using a relative path for the image.
Would like to reference image like this (in frontmatter)
featuredImage: img/IMG_20190621_112048_2.jpg
Where IMG_20190621_112048_2.jpg is in /src/data/img instead of same directory as markdown file under /src/posts
I've tried to setup gatsby-source-filesystem like this :
{
resolve: `gatsby-source-filesystem`,
options: {
name: `posts`,
path: `${__dirname}/src/posts`,
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `data`,
path: `${__dirname}/src/data/`,
},
},
but graphQL query in post template fails :
export const query = graphql`
query($slug: String!) {
markdownRemark(fields: { slug: { eq: $slug } }) {
html
frontmatter {
title
featuredImage {
childImageSharp {
fluid(maxWidth: 800) {
...GatsbyImageSharpFluid
}
}
}
}
}
}
GraphQL Error Field "featuredImage" must not have a selection since
type "String" has no subfields.
Any idea how I could fetch image from a location distinct to the post markdown directory ?
Achieving this in Gatsby used to be pretty troublesome, but thanks to the new createSchemaCustomization Node API docs (since Gatsby 2.5) it's relatively easy.
Here's a demo where I replicate your repo structure: github
Here's where the relevant code lives: github
Here's the code to make it work:
// gatsby-node.js
const path = require('path')
exports.createSchemaCustomization = ({ actions }) => {
const { createFieldExtension, createTypes } = actions
createFieldExtension({
name: 'fileByDataPath',
extend: () => ({
resolve: function (src, args, context, info) {
const partialPath = src.featureImage
if (!partialPath) {
return null
}
const filePath = path.join(__dirname, 'src/data', partialPath)
const fileNode = context.nodeModel.runQuery({
firstOnly: true,
type: 'File',
query: {
filter: {
absolutePath: {
eq: filePath
}
}
}
})
if (!fileNode) {
return null
}
return fileNode
}
})
})
const typeDefs = `
type Frontmatter #infer {
featureImage: File #fileByDataPath
}
type MarkdownRemark implements Node #infer {
frontmatter: Frontmatter
}
`
createTypes(typeDefs)
}
How it works:
There are 2 parts to this:
Extend markdownRemark.frontmatter.featureImage so graphql resolves to a File node instead of a string via createTypes
Create a new field extension #fileByDataPath via createFieldExtension
createTypes
Right now Gatsby's inferring frontmatter.featureImage as a string. We'll ask Gatsby to read featureImage as a string instead, by modifying its parent type:
type Frontmatter {
featureImage: File
}
This is not enough however, we'll also need to pass this Frontmatter type to its parent as well:
type Frontmatter {
featureImage: File
}
type MarkdownRemark implements Node {
frontmatter: Frontmatter
}
We'll also add the #infer tag, which lets Gatsby know that it can infer other fields of these types, i.e frontmatter.title, markdownRemark.html, etc.
Then pass these custom type to createTypes:
exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions
const typeDefs = `
type Frontmatter #infer {
featureImage: File
}
type MarkdownRemark implements Node #infer {
frontmatter: Frontmatter
}
`
createTypes(typeDefs)
}
Now, we can fire up localhost:8000/___graphql and try to query the image
query Post {
markdownRemark {
frontmatter {
featureImage {
id
}
}
}
}
and we get...
Error: Cannot return null for non-nullable field File.id.
That is because while Gatsby now understands featureImage should be a File node, it has no idea where to get that file.
At this point, we can either use createResolvers to manually resolve the field to a File node, or createFileExtension to do the same thing. I choose createFileExtension because it allows more code reuse (you can extend any fields), while createResolvers, in this case, is more useful for a specific field. Seeing that all you want is to resolve a file from the src/data directory, I'll call this extension fieldByDataPath.
createFileExtension
Let's just look at the resolve attribute. It is a function that takes in the following:
source: The data of the parent field (in this case, frontmatter)
args: The arguments passed to featureImage in a query. We won't need this
context: contains nodeModel, which we'll use to get nodes from Gatsby node store
info: metadata about this field + the whole schema
We will find the original path (img/photo.jpg) from src.featureImage, then glue it to src/data to get a complete absolute path. Next, we query the nodeModel to find a File node with the matching absolute path. Since you have already pointed gatsby-source-filesystem to src/data, the image (photo.jpg) will be in Gatsby node store.
In case we can't find a path or a matching node, return null.
resolve: async function (src, args, context) {
// look up original string, i.e img/photo.jpg
const partialPath = src.featureImage
if (!partialPath) {
return null
}
// get the absolute path of the image file in the filesystem
const filePath = path.join(__dirname, 'src/data', partialPath)
// look for a node with matching path
const fileNode = await context.nodeModel.runQuery({
firstOnly: true,
type: 'File',
query: {
filter: {
absolutePath: {
eq: filePath
}
}
}
})
// no node? return
if (!fileNode) {
return null
}
// else return the node
return fileNode
}
We've done 99% of the work. The last thing to do is to move this to pass this resolve function to createFieldExtension; as well as add the new extension to createTypes
createFieldExtension({
name: 'fileByDataPath' // we'll use it in createTypes as `#fileByDataPath`
extend: () => ({
resolve, // the resolve function above
})
})
const typeDef = `
type Frontmatter #infer {
featureImage: File #fileByDataPath // <---
}
...
`
With that, you can now use relative path from src/data/ in frontmatter.
Extra
The way fileByDataPath implemented, it'll only work with fields named featureImage. That's not too useful, so we should modify it so that it'll work on any field that, say, whose name ended in _data; or at the very least accept a list of field names to work on.
Edit had a bit of time on my hand, so I wrote a plugin that does this & also wrote a blog on it.
Edit 2 Gatsby has since made runQuery asynchronous (Jul 2020), updated the answer to reflect this.
In addition to Derek Answer which allow assets of any type to be use anywhere (sound, video, gpx, ...), if looking for a solution only for images, one can use :
https://www.gatsbyjs.org/packages/gatsby-remark-relative-images/
The reason in your server schema you may have declared the featuredImage variable as string and in your client graphql query you are trying to call subobjects of the featuredImage variable and that subobjects is not existing.
You may have to check the graphql schema definition and align the query with the schema definition
you current schema might be like this
featuredImage: String
and you need to change it by declaring the proper types based on the requirements in the server side.
For more information about graphql types. please refer this url - https://graphql.org/learn/schema/#object-types-and-fields
Thanks
Rigin Oommen

Resources