I am trying to retrieve data in form of a list from markdown frontmatter using gatsbyjs and graphql. Given the List in the markdown file, the goal would be to fetch the list items into my react component for further use.
This is for my work where I want to create a website using GatsbyJS and React (that's why I can't share real code).
So far I've tried to simply fetch the data via GraphQL from frontmatter but either the field is undefined or just contains null values.
I have a frontmatter like this:
---
name: "someName"
expertise: "someExpertise"
...
technologies:
- tech1
- tech2
- tech3
---
in my js code I try to fetch data using graphql like so:
query($slug: String!) {
profile: markdownRemark(fields: { slug: { eq: $slug } }) {
html
frontmatter {
date(formatString: "DD MMMM, YYYY")
author
expertise
name
dev_type
degree
technologies
}
}
}
I expected the output to match a form like this:
technologies = ["tech1", "tech2", "tech3"]
but I get the following error(s) when trying to compile and run the site in development environment:
Case 1.
Errors:
Int cannot represent non-integer value: "tech1"
I don't know how to fix this problem. In my understanding this should work. Already tried to create new NodeField called technologies in gatsby-node.js but this did not work, too.
I also thought about another thing according this frontmatter, in my understanding frontmatter is a YAML, or rather the desired list of technologies in the markdown file is a YAML list, isn't it?
In this case I should adjust/extend the gatsby-transformer-yaml plugin in gatsby-config.js but I have no clue how to achieve this one the right/correct way.
Case 2.
Unfortunately I cannot reproduce my query anymore but I found a way where I didn't get the type error, but then technologies only contained null values (having same frontmatter content).
technologies = [ null, null, null, ...]
Found out technologies: [ tech1, tech2, tech3 ] is the solution for this problem. Actual Problem with this was that there was inconsistent data provided in my markdown files.
Related
For ref, using "#apollo/client": "^3.5.5",
I've defined my typePolicies like so as suggested in docs:
HistoricalData: {
keyFields: ["variable", "workspace"],
fields:{...}
}
and when my cache is built, I am expecting my cacheId to be like
<__typename>:<id>:<id>
HistoricalData:${props.variable}:${props.workspace}`;
but instead, when I look in the Apollo cache, it's been created using the keyField names and the values in an object, such as
HistoricalData:{"variable":"GAS.TOTAL","workspace":"ABC"}
instead of
HistoricalData:GAS.TOTAL:ABC
so when I try to readFragment it returns null
client.readFragment({
id: `HistoricalData:${props.variable}:${props.workspace}`,
fragment: apolloGQL`fragment MyHistorical on Historical {
variable
workspace
}`})
It does actually return a value from the cache if I create my id in the structure that exists in the cache and readFragment using this.
Has anyone else noticed that Apollo client is not creating the cache id's in the structure that they describe in the docs?
After some research I came upon the correct way to handle this case. I know that you have already moved on, but just in case anyone else has the same problem in the future, here goes:
As described in the documentation for customizing the cache ID, the cache ID will be an stringified object, as you pointed out. It's not quite explicit in the documentation, but at this point in time it provides this nested example for a cache ID:
Book:{"title":"Fahrenheit 451","author":{"name":"Ray Bradbury"}}
But as users we don't have to preoccupy us with the format of this ID, because there's a helper for that, called cache.identify.
For your specific case, you could use something like this:
const identifiedId = cache.identify({
__typename: 'HistoricalData',
variable: 'GAS.TOTAL',
workspace: 'ABC'
});
cache.readFragment({
id: identifiedId,
fragment: apolloGQL`fragment MyHistorical on Historical {
variable
workspace
}`
});
I'm trying to create a blog page that can optionally display any image assets when they exist.
First, I created a content modal in Contentful with a Rich Text named "Body", and then I created an entry with some random text in it. In Gatsby, when I try to query the data via GraphQL explorer, I can query the "raw" field inside the "body". The "references" field can not be queried unless I add at least one image asset (aka, "dummy content").
below is my graphql query
export const query = graphql`
query ($slug: String!) {
contentfulBlog(slug: { eq: $slug }, node_locale: { eq: "en-US" }) {
title
body {
raw
references {
... on ContentfulAsset {
contentful_id
__typename
gatsbyImageData(formats: AUTO, layout: FULL_WIDTH)
}
}
}
}
}
`
The problem with the above query is that if there is no image asset found in a post, it will break the query. And my goal is to be able to optionally query the references field when there are assets available. But I'm not sure how to do it.
**Updated on Aug 24th, 2021:
For anyone that is also struggling with this problem as of today:
After digging through tons of documentation and answers, I've found out that this is in fact one of the limitations of the gatsby-source-contentful plugin. In short, the plugin(v4), as it states in its documentation:
At the moment, fields that do not have at least one populated instance
will not be created in the GraphQL schema.
Not sure if this will be addressed in a future release, but currently, it is required to have at least one dummy content(embedded image, links...etc) added in your rich text body in order for the plugin to create the "references" field in the graphql schema.
Here is the official discussion thread
If the idea is to only query references if they are available you need to customize the GraphQL schema to make the field nullable (meaning it can be empty, null) because, by default, it's inferred that it isn't.
You can check for Contentful content types in: https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/content-types/content-model
This would be the ideal solution. Alternatively, according to this Medium article, you can bypass it by wrapping your offending node within a Node-type object. In your case:
export const query = graphql`
query ($slug: String!) {
contentfulBlog(slug: { eq: $slug }, node_locale: { eq: "en-US" }) {
title
body {
raw
references {
... on Node {
... on ContentfulAsset {
contentful_id
__typename
gatsbyImageData(formats: AUTO, layout: FULL_WIDTH)
}
}
}
}
}
}
`
You may need to tweak it. Without knowing your data and schema structure it's impossible to guess if the query above will work alone, but get the idea. Check it in the GrahiQL playground (localhost:8000/___graphql)
Other resources:
https://github.com/gatsbyjs/gatsby/issues/20696
https://github.com/gatsbyjs/gatsby/pull/20794
I don't really understand graphql or gatsby that well but I believe all my images are loaded into graphql by putting this in my gatsby-config.js
{
resolve: `gatsby-source-filesystem`,
options: {
name: `images`,
path: path.join(__dirname, `src/assets/images`),
},
},
I am then trying to query a specific image which I can do with
query MyQuery {
allImageSharp(filter: {id: {eq: "7acfccd5-4aef-532b-8889-9d844ae2068b"}}) {
edges {
node {
sizes {
sizes
srcSet
src
aspectRatio
}
id
}
}
}
}
And this returns what I want, but the id that I have to enter for this query is 7acfccd5-4aef-532b-8889-9d844ae2068b. Is this id even going to stay the same if I put it in my code? Is there a way to set the id to something more sensical?
If I save my query to a variable data, it turns out that on Netlify data.allImageSharp.edges[0] is null, while locally this value will return what I need
I'm looking for the best way to query a single image. Not multiple images.
If I could set my own id's then I could query these.
Update
I found an example in the gatsby-source-filesystem documentation, but don't really know how to add it to my code
With this snippet:
{
resolve: `gatsby-source-filesystem`,
options: {
name: `images`,
path: path.join(__dirname, `src/assets/images`),
},
},
Basically you are telling your GraphQL schema that will find images in src/assets/images folder so you will be able to query all the content inside this folder via GraphQL. You are specifying the source for your data.
From gatsby-source-filesystem documentation:
The plugin creates File nodes from files. The various “transformer”
plugins can transform File nodes into various other types of data e.g.
gatsby-transformer-json transforms JSON files into JSON data nodes and
gatsby-transformer-remark transforms markdown files into
MarkdownRemark nodes from which you can query an HTML representation
of the markdown.
Answering your question, of course, you can filter and sort for any property or node that your image has, such as name, path, extension, etc. You may find a useful and autocompletion tool for your queries under /___graphql path when you run a gatsby develop command. This will help you to check out what parameters can be queried and filtered.
If you have an image with a path of src/images/exampleImage.jpg, you can query by the images name by querying using file rather than allImageSharp.
query HeaderQuery {
exampleImage: file(
extension: {eq: "jpg"},
name: {eq: "exampleImage"}
) {
childImageSharp {
fluid {
src
srcSet
sizes
srcSetWebp
}
}
}
}
You can also replace {extension: {eq: "jpg"} with {extension: {regex: "/(jpg)|(jpeg)|(png)/"} to query files of any extension type.
EDIT: I ended up creating a small reproduction of my problem and posted it to Gatsby's GitHub and I ended up getting some help there.
Here's the link to that issue!
I'm sorry in advance if this issue was treated somewhere else but I couldn't find an answer to what I need.
I'm currently working with Gatsby, GraphQl and YAML files and I have multiple sections that I'm mapping over but some have images and some don't.
I defined my GraphQL by stating the image with childImageSharp
sections {
title
description
imageHere {
childImageSharp {
fluid(maxWidth: 600) {
...GatsbyImageSharpFluid_noBase64
}
}
}
and I saw a solution that involved adding this to my gatsby-node.js so that the paths could be properly read from my YAML files
exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions;
const typeDefs = `
type allProjectPageYaml implements Node {
imageHere: [String]!
}
`;
createTypes(typeDefs);
};
Now my problem is that some sections have the imageHere field and some don't
sections:
- title: Title 1
description: Description 1
- title: Title 2
description: Description 2
imageHere: Image1
The error I get is TypeError: Cannot read property 'childImageSharp' of null
What's my workaround this?
I ended up creating a small reproduction of my problem and posted it to Gatsby's GitHub and I ended up getting some help there.
Here's the link to that issue!
Check the spelling of the filepath that ChildImageSharp is trying to reference. When I was getting this error, the img filepath needed to be corrected.
for me, it was a caching issue try delete .cache, public, dirs, or run
gatsby clean
UPDATE
if using netlify ci/cd, also need to add this into package.json
"resolutions": {
"sharp": "0.24.0"
}
https://community.netlify.com/t/error-input-file-contains-unsupported-image-format-gatsby/10891/4
You've defined imageHere as a required array of strings with this: imageHere: [String]!.
If it's actually supposed to be an optional image node, you might want to instead define it as the correct type (something like GatsbyImage, but I can't recall exactly what it is off-hand—you should be able to check in graphiql). That would look more like imageHere: GatsbyImage (no bang, no square brackets).
My use case is a mobile app with react native, but I guess it's very common good practices.
I want to be able, in an app, to take an image (from the camera or the gallery), and to be able to store it so it can be fetched from the date it was added, or some metadata added by the user.
The theory seems quite simple, a way of doing it can be :
Use any library (eg this great one) to get the image,
Store image as base64 and metadata in, let's say RealmJS (some internal DB),
Query this DB to get what I want.
This should work, and should be quite simple to implement.
But I'm wondering about a few things :
According to the performance of a smartphone's camera, isn't it quite a shame to store it as base64 (and no checksum, more memory used, ...) ?
This format, base64, isn't a bad idea in general for storing image ?
Is it a good idea to store the image in RealmJS, as it will be a pain for the user to reuse the image (share it on facebook...), but on the other hand, if I wrote it to the smartphone and store a URI, it can lead to a lot of problems (missing file if the user deletes it, need to access to memory, ...)
Is this approach "clean" (ok it works, but ...) ?
If you have any experience, tips, or good practice to share, I'll be happy to talk about it :)
You can store binary data (images) in Realm. But if you are using Realm locally (not sync), I will suggest that you store the image on the file system and store the path in Realm. Your model could be something like:
const ImageSchema = {
name: 'Image',
properties: {
path: 'string',
created: 'Date',
modified: 'Date?',
tags: 'Tag[]'
}
};
const TagSchema = {
name: 'Tag',
properties: {
name: 'string',
images: { type: 'linkingObjects', objectType: 'Image', property: 'tags' }
}
};
That is, for every image the timestamp for its creation is stored. Moreover, it has an optional timestamp if the image has been modified. The property path is where to find the image. If you prefer to store the image, you can use a property of type data instead. To find image less that a week old, you can use realm.objects('Image').filtered('created >= $1', new Date(Date.now()-7*24*60*60)).
Just for fun, I have added a list of tags for each image. The linkingObject in Tag makes it possible to find all image which have a particular tag e.g., realm.objects('Tag').filtered('#links.Tag.name == "Dog"').