I am currently trying to create a blog site using gatsbyjs, and would like my blog posts to have their pages created for them programmatically.
I followed the steps in the tutorials section of the official documentation, but instead of creating the pages from a markdown file, I am pulling data from the contentful CMS. I keep on hitting this block whenever I run my code.
gatsby-node.js
const path= require('path')
exports.createPages=({graphql,actions})=>{
const {createPage}=actions
const blogPost= path.resolve('./src/components/blogComponents/blog-post.js')
return new Promise((resolve,reject)=>{
graphql(`
{
allContentfulBlog{
edges{
node{
slug
}
}
}
}
`).then(results=>{
// console.log(results)
if(results.error){
reject(results.error)
}
// create blog post pages
const posts=results.data.allContentfulBlog.edges
posts.forEach((post,index)=>{
console.log(`showing slugs: ${post.node.slug}`)
createPage({
path:post.node.slug,
component:blogPost ,
context:{
slug:post.node.slug,
}
})
})
}).then(resolve)
})
}
blog-post.js
import React from 'react'
import { graphql} from 'gatsby'
const blogPost=({data})=>{
return (
<div>
new page created
</div>
)
}
export default blogPost
export const pageQuery= graphql`
query ($slug: String!){
allContentfulBlog (slug:{eq: $slug }) {
edges{
node{
content{
content
}
}
}
}
}
`
I expected the blog-post.js file to receive all appropriate variables, but instead, it kept on throwing the "unknown argument 'slug' " error while making reference to the blog-post.js file.
In blog-post.js, it looks like you might have been filtering the posts incorrectly; either do this:
export const pageQuery= graphql`
query ($slug: String!){
- allContentfulBlog (slug:{eq: $slug }) {
+ allContentfulBlog (filter: { slug: { eq: $slug } })
edges {
node {
content {
content
}
}
}
}
}
`
Or this:
export const pageQuery= graphql`
query ($slug: String!){
- allContentfulBlog (slug:{eq: $slug }) {
+ ContentfulBlog (slug:{eq: $slug }) {
content {
content
}
}
}
`
Related
am new to gatsby and graphql and I came across a tutorial where it is mentioned to fetch all the data using .map. But I want to fetch only one element from the DB. So how do I do it?
import React from "react";
import Layout from "../components/layout";
import { useStaticQuery, graphql, Link } from "gatsby";
const Blogs = () => {
const data = useStaticQuery(
graphql`
query {
allMarkdownRemark(sort: { frontmatter: { date: ASC } }) {
edges {
node {
frontmatter {
title
date(formatString: "DD MM, YYYY")
}
excerpt
id
fields {
slug
}
}
}
}
}
`
);
return (
<Layout>
<ul>
{data.allMarkdownRemark.edges.map((edge) => {
return (
<li key={edge.node.id}>
<h2>
<Link to={`/blog/${edge.node.fields.slug}/`}>
{edge.node.frontmatter.title}
</Link>
</h2>
<div>
<span>
Posted on {edge.node.frontmatter.date}
</span>
</div>
<p>{edge.node.excerpt}</p>
<div>
<Link to={`/blog/${edge.node.fields.slug}/`}>Read More</Link>
</div>
</li>
);
})}
</ul>
</Layout>
);
};
export default Blogs;
Lets say I have multiple blogs and I wish to show only a specific one in a page through query like...
query MyQuery {
markdownRemark((id: {eq: "9ac19d6d"}) //Some ID {
title
description
content
}
}
How to get this on a page to display?
Thanks in advance!
Depending on what do you want to achieve:
If you want just a specific single post. You can filter your useStaticQuery to add the value of the id (if you know it beforehand) like:
query MyQuery {
markdownRemark((id: {eq: "123"}) {
title
description
content
}
}
useStaticQuery as the name points out, is static and doesn't accept dynamic values.
Another alternative is to get a specific position from data.allMarkdownRemark to display it.
If you just want a single post without any filter you can take advantage of the GraphQL query options:
{
allMarkdownRemark(limit: 1) {
edges {
node {
frontmatter {
title
}
}
}
}
}
If you are trying to create dynamic posts, hence each post template will display a different blog post (one per template), you need to pass a filter value from gatsby-node.js (where you create the post pages) to the template through Gatsby's context:
// gatsby-node.js
posts.forEach(({ node }, index) => {
createPage({
path: node.fields.slug,
component: path.resolve(`./src/templates/blog-post.js`),
context: {
id: node.id,
title: node.title
},
})
})
Note: here I'm lifting the id and the title. Use whatever works better for you
Now, you can take advantage of the context in your Blogs component (as long as it's a template):
const Blogs = ({data}) => {
console.log("your blog post is:", data)
return (
<Layout>
<h1>{data.markdownRemark.title}</h1>
</Layout>
);
};
export const query = graphql`
query($id: String!, $title: String!) {
markdownRemark((id: {eq: $id}) {
title
description
content
}
}
`
export default Blogs;
In other words: the first approach uses a static query (via useStaticQuery hook. Static, no dynamic parameters allowed) and the second uses a page query (only available in pages or templates)
With your query:
query MyQuery {
markdownRemark((id: {eq: "9ac19d6d"}) //Some ID {
title
description
content
}
}
Your data will be in data.markdownRemark
You can access those 3 fields directly.
const { title, description, content ] = data.markdownRemark;
return (
<Layout>
<div>
<p>{title}</p>
<p>{description]</p>
<p>{content}</p>
</div>
</Layout>
)
I want to turn my Wordpress Blog into a react app through Hygraph headless CMS.
My API query has "wordPressPosts" posts within a master "posts" property.
const { posts } = await hygraph.request(
`
{
posts {
wordPressPosts {
id
title {
rendered
}
categories
}
}
}
`
);
How can I access wordPressPosts "ids" and post "titles" to map them? I tried messing with posts.wordPressPosts but it does not display anything on screen.
return {
props: {
posts
}
};
}
export default ({ posts }) =>
posts.map(({ id, title }) => <p key={id}>{title}</p>);
Full code sandbox: https://codesandbox.io/s/wp-source-for-hygraph-li2ssl?file=/src/pages/index.js
Cheers,
L.
I'm unable to render a dynamic image coming from strapi cms using GatsbyImage from gatsby-plugin-image
Everything worked fine with the old gatsby-image plugin, really don't know what i'm doing wrong here.
Warning: Failed prop type: The prop srcis marked as required inJ, but its value is undefined.
How should i use the image object or the getImage() function to render the GatsbyImage ?
export const query = graphql`
{
allStrapiProjects(filter: { featured: { eq: true } }) {
nodes {
title
image {
localFile {
childImageSharp {
fluid {
...GatsbyImageSharpFluid
}
}
}
}
}
}
import { GatsbyImage, getImage, StaticImage } from "gatsby-plugin-image";
const Project = ({
image,
}: any) => {
const gatsbyImg = getImage(image);
const imgPath = image.localFile.childImageSharp.fluid;
console.log("gatsbyImg:", gatsbyImg);
console.log("image:", image);
console.log("imgPath:", imgPath);
return (
...
<GatsbyImage image={imgPath} alt={"alt"} />
)
Output result for the console logs above:
As you pointed out, the issue appears because of the mix of GraphQL nodes between gatsby-image and gatsby-plugin-image. Summarizing a lot, your queryable node should be gatsyImageData instead of fluid or fixed.
This is the previous syntax:
import { graphql } from "gatsby"
export const query = graphql`
{
file(relativePath: { eq: "images/example.jpg" }) {
childImageSharp {
fixed {
...GatsbyImageSharpFixed
}
}
}
}
`
While the new one looks like:
import { graphql } from "gatsby"
export const query = graphql`
{
file(relativePath: { eq: "images/example.jpg" }) {
childImageSharp {
gatsbyImageData(layout: FIXED)
}
}
}
`
Check further details in the migration guide
The problem is that you are querying the old GraphQL node (fluid or fixed) that worked for Img (from gatsby-image) while the new component, GatsbyImage requires an image prop, extracted from gatsbyImageData node.
That said, getImage is a helper function (not mandatory) that helps you to clean up the code.
Double-check the following steps:
Install the dependencies:
npm install gatsby-plugin-image gatsby-plugin-sharp gatsby-transformer-sharp
Remove the old gatsby-image dependency references if any.
Clean the cache
Query the proper nodes: check them in localhost:8000/___graphiql
(Optional) run the codemod:
npx gatsby-codemods gatsby-plugin-image <optional-path>
This will automatically adapt your old code to the new syntax.
In the end, your code should look like:
export const query = graphql`
{
allStrapiProjects(filter: { featured: { eq: true } }) {
nodes {
title
image {
localFile {
childImageSharp {
gatsbyImageData(layout: FIXED)
}
}
}
}
}
Your component should work alone since now is not working because it's not getting the proper data.
I'm building a website with gatsby and Strapi and i'm facing an issue that can't fix.
Here's my problem:
I made a custom graphQL Schema to avoid building fails if my clients leave empty fields in the CMS. But since i did that for my homepage banner, childImageSharp is null.
I'm assuming i did my custom graphql schemas wrong but I can't understand where.
Here's my files:
Homepage.js
const IndexPage = ({ data }) => {
return (
<Layout>
{data.allStrapiBanner.edges[0].node.banner.childImageSharp.fixed &&
<Img
fixed={data.allStrapiBanner.edges[0].node.banner.childImageSharp.fixed}
imgStyle={{ position: "static" }}
/>
}
</Layout>
)
}
export default IndexPage
export const pageQuery = graphql`
allStrapiBanner {
edges {
node {
banner {
childImageSharp {
fixed(width: 800){
src
}
}
}
}
}
}
}
`
Here's my gatsby-node.js custom Schema:
exports.sourceNodes = ({ actions }) => {
const { createTypes } = actions
const typeDefs = `
type allStrapiBanner implements Node {
banner: childImageSharp
}
type childImageSharp{
childImageSharp: fixed
}
type fixed{
fixed(width: Int): src
}
type src{
src: String
}
`
createTypes(typeDefs)
}
Now gatsby build without error when i don't upload an image to my banner field but when i upload an image childImageSharp is null
I already read the doc (https://www.gatsbyjs.com/docs/reference/graphql-data-layer/schema-customization/) but i can't manage to make it work.
if someone can give me a tips to how to correctly create my custom schemas for childImageSharp.
Thanks you very much in advance
SOLVED
After a lot of hours of brute force trying to understand what was wrong, I have finally fixed my problem. I can't believe it was so simple:
I simply changed my schema in gastby-node.js to
exports.sourceNodes = ({ actions }) => {
const { createTypes } = actions
const typeDefs = `
type allStrapiBanner implements Node {
banner: File
}
`
createTypes(typeDefs)
}
and it work
enter image description here
I have added the below code in main.ts
import { NestFactory } from '#nestjs/core';
import{SwaggerModule,DocumentBuilder} from '#nestjs/swagger'
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const options = new DocumentBuilder()
.setTitle('My API')
.setDescription('API description')
.setVersion('1.0')
.build();
const document = SwaggerModule.createDocument(app, options);
SwaggerModule.setup('api', app, document);
await app.listen(3000);
}
bootstrap();
//controller
#Post()
addProduct(
#Body('title') title:string,
#Body('price') price:number,
):any{
const idFromService=this.productserive.addNewProduct(title,price);
return idFromService;
}
//productservice
export class ProductService {
products:Product[]=[];
addNewProduct(title:string,price:number){
const id=uId();
const newproduct=new Product(id,title,price);
this.products.push(newproduct);
return {title,price};
}
}
// create a separate dto
import { ApiProperty } from '#nestjs/swagger';
export class TestDto {
#ApiProperty()
readonly title: string
#ApiProperty()
readonly price: number
}
// use it in your controller
#Post()
addProduct(#Body() TestDto:TestDto): any {
return;
}