trying to map images from a folder in a gatsby project. I set up the query in a way I thought would work based on guides I've seen. I do not get any errors and the site loads, but the images do not show up. Any thoughts? Thanks!
gatsby-config.js:
plugins: [
`gatsby-plugin-react-helmet`,
`gatsby-transformer-sharp`,
`gatsby-plugin-sharp`,
{
resolve: `gatsby-source-filesystem`,
options: {
name: `images`,
path: `${__dirname}/src/images`,
},
},
query:
const data = useStaticQuery(graphql`
query {
allFile(filter:{extension:{regex:"/social/"}}) {
edges {
node {
childImageSharp {
fixed(width: 150, height: 150) {
...GatsbyImageSharpFixed
}
}
}
}
}
}
`)
return:
<div className='socialPhotos'>
{data.allFile.edges.map(node =>
<Img
key={node.id}
title="Photo image"
alt="Photo"
fixed={node.childImageSharp.fixed}
/>
)}
</div>
The filter in your graphQL query might be the problem. Did you try your query in graphiQL in the browser?
Because you define images in your gatsby-config.js, I would suggest filtering with sourceInstanceName:
const data = useStaticQuery(graphql`
query {
allFile(filter: {sourceInstanceName: {eq: "images"}}) {
edges {
node {
childImageSharp {
fixed(width: 150, height: 150) {
...GatsbyImageSharpFixed
}
}
}
}
}
}
`)
Paste your query into GraphiQL in your browser and test if you get data back: http://localhost:8000/___graphql
EDIT
This is how you can get one image from your query inside your component
const YourComponent = (props) => {
const { data: { allFile: { edges } } } = props;
const oneImage = edges.filter(
el => el.node.childImageSharp.fixed.originalName === "yourImageFileName.png")
[0].node.childImageSharp.fixed;
// ...
Related
I am trying to build sub-pages for a projects category in Gatsby, each project parent page already generates the way it should but the sub-pages do not.
Each project can have zero to many sub-pages, I only want a sub-page to be generated if it exists. Data is coming from a headless CMS through GraphQL
My loop for generating these pages in gatsby-node.js currently looks like this:
result.data.allSanityProjects.edges.forEach(({ node }) => {
node.projectChildPages.map(childPage => {
if (node && node.projectChildPages.length > 0 && node.projectChildPages.slug) {
createPage({
path: childPage.slug + "/" + node.projectChildPages.slug,
component: projectsSubPages,
context: {
slug: childPage.slug + "/" + node.projectChildPages.slug,
},
});
}
});
});
});
This loops through the "allSanityProjects" part of this GrapQL query
{
allSanityDefaultPage {
edges {
node {
slug
}
}
}
allSanityProjects {
edges {
node {
slug
projectChildPages {
slug
}
}
}
}
}
The results of running just the allSanityProjects-query looks like this
{
"data": {
"allSanityProjects": {
"edges": [
{
"node": {
"slug": "project-3",
"projectChildPages": []
}
},
{
"node": {
"slug": "project-1",
"projectChildPages": [
{
"slug": "project-1"
},
{
"slug": "Doggolicious"
},
{
"slug": "no-cats"
}
]
}
},
{
"node": {
"slug": "Project-2",
"projectChildPages": []
}
}
]
}
}
}
Gatsby fails building the project child pages with the following error.
warn The GraphQL query in the non-page component
Exported queries are only executed for Page components. It's possible you're
trying to create pages in your gatsby-node.js and that's failing for some
reason.
If the failing component(s) is a regular component and not intended to be a page
component, you generally want to use a <StaticQuery> (https://gatsbyjs.org/docs/static-query)
instead of exporting a page query.
If you're more experienced with GraphQL, you can also export GraphQL
fragments from components and compose the fragments in the Page component
query and pass data down into the child component — https://graphql.org/learn/queries/#fragments
My template looks like this:
import React from "react";
import { useStaticQuery, graphql } from "gatsby";
import Layout from "../components/layout";
const BlockContent = require("#sanity/block-content-to-react");
const projectsSubPages = ({ data }) => {
const pageData = data.sanityProjects.projectChildPages;
return (
<Layout>
<BlockContent blocks={pageData._rawBlockContent} />
</Layout>
);
};
export const query = graphql`
query($slug: String!) {
sanityProjects(slug: { eq: $slug }) {
projectChildPages {
_rawBlockContent
slug
title
}
}
}
`;
export default projectsSubPages;
As far as I can tell my error is in my gatsby-node.js file, not in my template even though gatsby tells me my error is in my template. I've tried running the exact same templates as the others I use (just with different queries in them) and still get the same error.
My full gatsby-node.js file:
exports.createPages = ({ actions, graphql }) => {
const path = require(`path`);
const { createPage } = actions;
const projects = path.resolve("src/templates/projects.js");
const defaultPage = path.resolve("src/templates/defaultPage.js");
const projectsSubPages = path.resolve("src/templates/projectsSubPages.js");
return graphql(`
{
allSanityDefaultPage {
edges {
node {
slug
}
}
}
allSanityProjects {
edges {
node {
slug
projectChildPages {
slug
}
}
}
}
}
`).then((result) => {
if (result.errors) {
reporter.panic("failed to create pages ", result.errors);
}
result.data.allSanityDefaultPage.edges.forEach(({ node }) => {
createPage({
path: node.slug,
component: defaultPage,
context: {
slug: node.slug,
},
});
});
result.data.allSanityProjects.edges.forEach(({ node }) => {
createPage({
path: node.slug,
component: projects,
context: {
slug: node.slug,
},
});
});
result.data.allSanityProjects.edges.forEach(({ node }) => {
node.projectChildPages.map(childPage => {
if (node && node.projectChildPages.length > 0 && node.projectChildPages.slug) {
createPage({
path: childPage.slug + "/" + node.projectChildPages.slug,
component: projectsSubPages,
context: {
slug: childPage.slug + "/" + node.projectChildPages.slug,
},
});
}
});
});
});
};
code
result.data.allSanityProjects.edges.forEach(({ node }) => {
node.projectChildPages.map(childPage => {
if (node && node.projectChildPages.length > 0 && node.projectChildPages.slug) {
Condition doesn't have much sense there:
if you're in .map() then for sure node and node.projectChildPages.length > 0 are true
projectChildPages is an array so no projectChildPages.slug here
query
Your fetched data (source) doesn't contain _rawBlockContent so you can't query for this in page component.
Using the Flexible Gatsby starter (https://github.com/wangonya/flexible-gatsby/), exact versions of Gatsby dependencies are:
"gatsby": "2.15.36",
"gatsby-image": "2.2.27",
"gatsby-plugin-feed": "2.3.15",
"gatsby-plugin-google-analytics": "2.1.20",
"gatsby-plugin-manifest": "2.2.20",
"gatsby-plugin-offline": "3.0.12",
"gatsby-plugin-react-helmet": "3.1.10",
"gatsby-plugin-sass": "2.1.17",
"gatsby-plugin-sharp": "2.2.28",
"gatsby-remark-images": "3.1.25",
"gatsby-remark-prismjs": "3.3.17",
"gatsby-source-filesystem": "2.1.30",
"gatsby-transformer-remark": "2.6.27",
"gatsby-transformer-sharp": "2.2.20",
I can run gatsby develop to get a watch on the files in the folder that refreshes UI changes and such. However, as soon as I save any changes to any of the files in content/blog develop hits the following error:
info added file at /Users/mattsi/dev/me/newsite/gatsby/content/blog/conference-on-javascript/index.md
ERROR #11321 PLUGIN
"gatsby-node.js" threw an error while running the createPages lifecycle:
The "path" argument must be of type string. Received type undefined
GraphQL request:14:17
13 | title
14 | img {
| ^
15 | childImageSharp {
After that, the local server responds with an error page saying TypeError: Cannot read property 'page' of undefined and a stack trace pointing to rootjs:44. If I kill the watch and do gatsby develop again, it works fine. But that feedback loop is obviously much slower.
My Gatsby Config (some details omitted with ...):
module.exports = {
siteMetadata: {
title: `...`,
description: `...`,
author: `...`,
siteUrl: `...`,
social: {
twitter: `...`,
facebook: ``,
github: `...`,
linkedin: `...`,
email: `...`,
},
},
plugins: [
{
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/content/blog`,
name: `blog`,
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/content/assets`,
name: `assets`,
},
},
{
resolve: `gatsby-transformer-remark`,
options: {
plugins: [
{
resolve: `gatsby-remark-images`,
options: {
maxWidth: 970,
},
},
`gatsby-remark-prismjs`,
],
},
},
`gatsby-transformer-sharp`,
`gatsby-plugin-sharp`,
{
resolve: `gatsby-plugin-google-analytics`,
options: {
//trackingId: `ADD YOUR TRACKING ID HERE`,
},
},
`gatsby-plugin-feed`,
{
resolve: `gatsby-plugin-manifest`,
options: {
name: `...`,
short_name: `...`,
start_url: `/`,
background_color: `#663399`,
theme_color: `#663399`,
display: `minimal-ui`,
icon: `./static/gatsby-icon.png`, // This path is relative to the root of the site.
},
},
// `gatsby-plugin-offline`,
`gatsby-plugin-react-helmet`,
`gatsby-plugin-sass`,
],
}
My gatsby-node.js file:
const path = require(`path`)
const { createFilePath } = require(`gatsby-source-filesystem`)
exports.createPages = ({ graphql, actions }) => {
const { createPage } = actions
const blogPost = path.resolve(`./src/templates/blog-post.js`)
return graphql(
`
{
allMarkdownRemark(
sort: { fields: [frontmatter___date], order: DESC }
limit: 1000
) {
edges {
node {
fields {
slug
}
frontmatter {
title
img {
childImageSharp {
fluid(maxWidth: 3720) {
aspectRatio
base64
sizes
src
srcSet
}
}
}
}
}
}
}
}
`
).then(result => {
if (result.errors) {
throw result.errors
}
// Create blog posts pages.
const posts = result.data.allMarkdownRemark.edges
posts.forEach((post, index) => {
const previous = index === posts.length - 1 ? null : posts[index + 1].node
const next = index === 0 ? null : posts[index - 1].node
createPage({
path: post.node.fields.slug,
component: blogPost,
context: {
slug: post.node.fields.slug,
previous,
next,
},
})
})
// Create blog post list pages
const postsPerPage = 10
const numPages = Math.ceil(posts.length / postsPerPage)
Array.from({ length: numPages }).forEach((_, i) => {
createPage({
path: i === 0 ? `/` : `/${i + 1}`,
component: path.resolve("./src/templates/blog-list.js"),
context: {
limit: postsPerPage,
skip: i * postsPerPage,
numPages,
currentPage: i + 1,
},
})
})
})
}
exports.onCreateNode = ({ node, actions, getNode }) => {
const { createNodeField } = actions
if (node.internal.type === `MarkdownRemark`) {
const value = createFilePath({ node, getNode })
createNodeField({
name: `slug`,
node,
value,
})
}
}
Looking into the stack trace, it seems as if the post.node.fields.slug field on the modified post from the allMarkdownRemark GraphQL DB is coming back as undefined. But using the built-in GraphQL browser it seems to always be populated. Perhaps it's very briefly unpopulated while refreshing, faster than I can catch, and this causes the watch to crash? Any ideas how I can fix Gatsby's watch functionality?
I have this issue too and I believe it will be fixed by this PR - it was for me at least. Perhaps you could test it out and comment on the PR if it helps you.
Subscriptions with Nexus are undocumented but I searched Github and tried every example in the book. It's just not working for me.
I have cloned Prisma2 GraphQL boilerplate project & my files are as follows:
prisma/schema.prisma
datasource db {
provider = "sqlite"
url = "file:dev.db"
default = true
}
generator photon {
provider = "photonjs"
}
generator nexus_prisma {
provider = "nexus-prisma"
}
model Pokemon {
id String #default(cuid()) #id #unique
number Int #unique
name String
attacks PokemonAttack?
}
model PokemonAttack {
id Int #id
special Attack[]
}
model Attack {
id Int #id
name String
damage String
}
src/index.js
const { GraphQLServer } = require('graphql-yoga')
const { join } = require('path')
const { makeSchema, objectType, idArg, stringArg, subscriptionField } = require('#prisma/nexus')
const Photon = require('#generated/photon')
const { nexusPrismaPlugin } = require('#generated/nexus-prisma')
const photon = new Photon()
const nexusPrisma = nexusPrismaPlugin({
photon: ctx => ctx.photon,
})
const Attack = objectType({
name: "Attack",
definition(t) {
t.model.id()
t.model.name()
t.model.damage()
}
})
const PokemonAttack = objectType({
name: "PokemonAttack",
definition(t) {
t.model.id()
t.model.special()
}
})
const Pokemon = objectType({
name: "Pokemon",
definition(t) {
t.model.id()
t.model.number()
t.model.name()
t.model.attacks()
}
})
const Query = objectType({
name: 'Query',
definition(t) {
t.crud.findManyPokemon({
alias: 'pokemons'
})
t.list.field('pokemon', {
type: 'Pokemon',
args: {
name: stringArg(),
},
resolve: (parent, { name }, ctx) => {
return ctx.photon.pokemon.findMany({
where: {
name
}
})
},
})
},
})
const Mutation = objectType({
name: 'Mutation',
definition(t) {
t.crud.createOnePokemon({ alias: 'addPokemon' })
},
})
const Subscription = subscriptionField('newPokemon', {
type: 'Pokemon',
subscribe: (parent, args, ctx) => {
return ctx.photon.$subscribe.pokemon()
},
resolve: payload => payload
})
const schema = makeSchema({
types: [Query, Mutation, Subscription, Pokemon, Attack, PokemonAttack, nexusPrisma],
outputs: {
schema: join(__dirname, '/schema.graphql')
},
typegenAutoConfig: {
sources: [
{
source: '#generated/photon',
alias: 'photon',
},
],
},
})
const server = new GraphQLServer({
schema,
context: request => {
return {
...request,
photon,
}
},
})
server.start(() => console.log(`🚀 Server ready at http://localhost:4000`))
The related part is the Subscription which I don't know why it's not working or how it's supposed to work.
I searched Github for this query which results in all projects using Subscriptions.
I also found out this commit in this project to be relevant to my answer. Posting the related code here for brevity:
import { subscriptionField } from 'nexus';
import { idArg } from 'nexus/dist/core';
import { Context } from './types';
export const PollResultSubscription = subscriptionField('pollResult', {
type: 'AnswerSubscriptionPayload',
args: {
pollId: idArg(),
},
subscribe(_: any, { pollId }: { pollId: string }, context: Context) {
// Subscribe to changes on answers in the given poll
return context.prisma.$subscribe.answer({
node: { poll: { id: pollId } },
});
},
resolve(payload: any) {
return payload;
},
});
Which is similar to what I do. But they do have AnswerSubscriptionPayload & I don't get any generated type that contains Subscription in it.
How do I solve this? I think I am doing everything right but it's still not working. Every example on GitHub is similar to above & even I am doing the same thing.
Any suggestions?
Edit: Subscriptions aren't implemented yet :(
I seem to have got this working despite subscriptions not being implemented. I have a working pubsub proof of concept based off the prisma2 boilerplate and Ben Awad's video tutorial https://youtu.be/146AypcFvAU . Should be able to get this up and running with redis and websockets to handle subscriptions until the prisma2 version is ready.
https://github.com/ryanking1809/prisma2_subscriptions
Subscriptions aren't implemented yet.
I've opened up an issue to track it.
I'll edit this answer as soon as it's implemented in Prisma 2.
I have a Nuxt.js, statically generated site that has some dynamic pages. I'm using a GraphQL based headless CMS (DatoCMS) to provide the data for these pages, accessed using Apollo (#nuxt/apollo). I have it generating all the routes correctly but when I navigate to these pages from my site nav I'm receiving the following error 3 times:
TypeError: Cannot read property '_seoMetaTags' of undefined
at f.head (cf150f1920d36ab67139.js:1)
at wn.get (008dfc959ff6e6a713a0.js:2)
at wn.evaluate (008dfc959ff6e6a713a0.js:2)
at f.$metaInfo (008dfc959ff6e6a713a0.js:2)
at f.created (008dfc959ff6e6a713a0.js:2)
at Qt (008dfc959ff6e6a713a0.js:2)
at fn (008dfc959ff6e6a713a0.js:2)
at f.t._init (008dfc959ff6e6a713a0.js:2)
at new f (008dfc959ff6e6a713a0.js:2)
at 008dfc959ff6e6a713a0.js:2
This is coming from the head code in my page component so clearly something isn't being generated correctly. I can also see in the Chrome network tab that calls are being made to the GraphQL interface which also tells me the static generation isn't working correctly.
Here's the head() and apollo portions of my page component:
head() {
return {
title: this.blogPost._seoMetaTags.find(element => {
return element.tag === 'title';
}).content,
meta: [
{ hid: 'keywords', keywords: this.blogPost.keywords },
{ hid: 'description', description: this.blogPost._seoMetaTags.find(element => {
return element.tag === 'meta' && element.attributes.name === 'description';
}).attributes.content}
],
script: [
{ src: 'https://cdn.commento.io/js/commento.js', defer: true }
]
}
},
apollo: {
blogPost: {
query: gpl`
query BlogPost($slug: String!) {
blogPost(filter: { slug:{ eq: $slug }}) {
title
titleColor {
hex
}
slug
author
keywords
_seoMetaTags {
tag
attributes
content
}
_firstPublishedAt
banner {
id
url
title
}
content {
... on HeadingRecord {
_modelApiKey
heading
}
... on SubHeadingRecord {
_modelApiKey
subHeading
}
... on TextRecord {
_modelApiKey
content
}
... on CodeRecord {
_modelApiKey
codeBlock
}
... on ImageRecord {
_modelApiKey
image {
id
height
width
url
title
alt
}
}
... on VideoRecord {
_modelApiKey
video {
height
provider
providerUid
thumbnailUrl
title
url
width
}
}
}
}
}
`,
prefetch({ route }) {
return {
slug: route.params.slug
};
},
variables() {
return {
slug: this.$route.params.slug
};
}
And my nuxt.config.js if it helps:
const pkg = require('./package')
const webpack = require('webpack');
import fetch from 'node-fetch';
import { execute, makePromise } from 'apollo-link';
import { createHttpLink } from 'apollo-link-http';
import gql from 'graphql-tag';
module.exports = {
mode: 'universal',
/*
** Headers of the page
*/
head: {
title: pkg.name,
htmlAttrs: {
lang: 'en'
},
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: pkg.description }
],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
{ rel: 'stylesheet', href: 'https://cdn.jsdelivr.net/gh/tonsky/FiraCode#1.206/distr/fira_code.css' }
]
},
/*
** Customize the progress-bar color
*/
loading: { color: '#fff' },
/*
** Global CSS
*/
css: [
],
/*
** Plugins to load before mounting the App
*/
plugins: [
],
/*
** Nuxt.js modules
*/
modules: [
'#nuxtjs/style-resources',
'#nuxtjs/apollo',
'#nuxtjs/google-analytics'
],
/*
** #nuxtjs/google-analytics settings
*/
googleAnalytics: {
id: 'UA-136517294-1'
},
/*
** #nuxtjs/style-resources settings
*/
styleResources: {
scss: [
'./assets/css/*.scss'
]
},
/*
** Apollo setup for DatoCMS graphql queries
*/
apollo: {
includeNodeModules: true,
clientConfigs: {
default: '#/apollo/default.js'
}
},
/*
** Build configuration
*/
build: {
postcss: {
preset: {
features: {
customProperties: false
}
}
},
/*
** You can extend webpack config here
*/
extend(config, ctx) {
}
},
/*
** Generate configuration
*/
generate: {
routes: function(callback) {
// Get the list of posts
const uri = 'https://graphql.datocms.com';
const link = new createHttpLink({ uri: uri, fetch: fetch });
const operation = {
query: gql`
{
allBlogPosts {
id
slug
keywords
_seoMetaTags {
tag
attributes
content
}
}
}`,
context: {
headers: {
authorization: 'Bearer <my token>'
}
}
};
makePromise(execute(link, operation))
.then(data => {
// Build the routes from the posts
const postRoutes = data.data.allBlogPosts.map(item => {
return { route: `/blog/${item.slug}`, payload: { keywords: item.keywords, seoData: item._seoMetaTags }};
});
// Register the routes
callback(null, postRoutes);
})
.catch(error => console.log(`received error ${error}`));
}
}
}
If you expect all api requests to be bundled into app and not made anymore, thats not how nuxt currently work. It still will do requests to the api`s on client navigation withing app.
There is a plan for making full static mode in nuxt, you can track it here https://github.com/nuxt/rfcs/issues/22
I have the following Relay container in my Feed component, that it is a simple list of Posts
export default Relay.createContainer(Feed, {
initialVariables: {
count: 5,
},
fragments: {
viewer: () => Relay.QL`
fragment on Viewer {
posts(first: $count) {
pageInfo {
hasNextPage
}
edges {
node {
id,
body
},
},
},
},
`,
},
});
Theses posts are organized by the most recent ones, how can I check if there are newer posts in my graphql server? I want to do a pulling strategy until I migrate it to graphql/relay subscriptions
Can I use forceFetch? I don't wanna to update Relay Store, I just wanna to notify the user that has new posts
One way I could think of is to have an endpoint which you supply with the latest timestamp. This endpoint returns for example the ids of the newer posts. On relay side you do a setTimeout(() => { this.props.relay.setVariables({timestamp: timestamp}); }, 60000)
and in the fragment something like:
const FooBar = Relay.createContainer(FooBarClass, {
initialVariables: {
timestamp: null
},
prepareVariables: vars => {
vars['timestampIsTruthy'] = !!vars['timestamp'];
return vars;
},
fragments: {
store: () => Relay.QL`
fragment on Query {
newerPosts(timestamp: $timestamp, first: 3) #include(if: $queryIsTruthy) {
totalCount
edges {
node {
id
}
}
}
}`,
},
});