How to put post date in Gatsby URL? - reactjs

All the Gatsby starter demos have a path like /gatsby-starter-blog/hi-folks/
How do I set it up with /2015-05-28/hi-folks/ or just the year with /2015/hi-folks/.
Thanks!

Two options:
1) Just put the blog posts in directories named like you want the url to be so in this case /2015-05-28/hi-folks/index.md.
2) You can programmatically set paths by exporting a function from gatsby-node.js called rewritePath. It is called for each page with filesystem data for the file the page comes from + the page's metadata. So say you want to set the date of the post in your markdown's frontmatter and have each post be a simple markdown file with paths like /a-great-blog-post.md
So to do what you want, add to your gatsby-node.js something like:
import moment from 'moment'
exports.rewritePath = (parsedFilePath, metadata) => {
if (parsedFilePath.ext === "md") {
return `/${moment(metadata.createdAt).format('YYYY')}/${parsedFilePath.name}/`
}
}

rewritePath is no longer supported in Gatsby. Here's a solution confirmed to works in Gatsby 2.3,
const m = moment(node.frontmatter.date)
const value = `${m.format('YYYY')}/${m.format('MM')}/${slug}`
createNodeField({ node, name: 'slug', value })

In my gatsby-node.js file I added a slug generator and a template resolver.
Inside src/posts I added a folder 2020 inside that folder I create folders that make slug address paths like so my-blog-post and inside those folders, I have an index.md file.
Now I have URL's that look like http://www.example.com/2020/my-blog-post/.
/**
* Implement Gatsby's Node APIs in this file.
*
* See: https://www.gatsbyjs.org/docs/node-apis/
*/
const { createFilePath } = require(`gatsby-source-filesystem`);
// Generates pages for the blog posts
exports.createPages = async function ({ actions, graphql }) {
const { data } = await graphql(`
query {
allMarkdownRemark {
edges {
node {
fields {
slug
}
}
}
}
}
`);
data.allMarkdownRemark.edges.forEach((edge) => {
const slug = edge.node.fields.slug;
actions.createPage({
path: slug,
component: require.resolve(`./src/templates/blog-post.js`),
context: { slug: slug },
});
});
};
// Adds url slugs to the graph data response
exports.onCreateNode = ({ node, getNode, actions }) => {
const { createNodeField } = actions;
if (node.internal.type === `MarkdownRemark`) {
const slug = createFilePath({ node, getNode, basePath: `posts` });
createNodeField({
node,
name: `slug`,
value: slug,
});
}
};
Inside my src/templates/blog-post.js file I have:
import React from 'react';
import { graphql } from 'gatsby';
import Layout from '../components/layout';
export default function BlogPost({ data }) {
const post = data.markdownRemark;
const tags = (post.frontmatter.tags || []).map((tag, i) => {
return <li key={i}>#{tag}</li>;
});
return (
<Layout>
<article className="post">
<header>
<h1>{post.frontmatter.title}</h1>
<div className="meta">
<time>{post.frontmatter.date}</time>
<ul>{tags}</ul>
</div>
</header>
<section dangerouslySetInnerHTML={{ __html: post.html }} />
</article>
</Layout>
);
}
export const query = graphql`
query($slug: String!) {
markdownRemark(fields: { slug: { eq: $slug } }) {
html
frontmatter {
title
date(formatString: "DD-MMMM-YYYY")
tags
}
}
}
`;
My markdown files then use frontmatter data like so:
---
title: Post title here
date: "2020-05-25T14:23:23Z"
description: "A small intro here"
tags: ["tag1", "tag2"]
---
Main post content would be here...

Related

Variable "$slug" of required type "String!" was not provided only when building yet not when developing

I am deploying a Gatsby site using mdx files. When I use npm run develop, my site works as expected. When I use npm run build, I encounter the following error:
Variable "$slug" of required type "String!" was not provided.
It points to my blog.js file (Url path: /templates/blog/) as the source of the error. After some imports, blog.js appears as follows:
export const query = graphql`
query (
$slug: String!
) {
mdx(
fields: {
slug: {
eq: $slug
}
}
) {
frontmatter {
title
details
date(formatString: "LL")
tags
}
body
}
}`
export default function Blog(props) {
return (...content...
)
}
The relevant parts of my config file is as you would expect.
module.exports = {
siteMetadata: {
title: '...',
author: '...'
},
plugins: [...,
{resolve: 'gatsby-source-filesystem',
options: {
name:'src',
path: `${__dirname}/src/`
}
...
My gatsby-node.js is as follows:
/* pathing via node.js; for path.basename*/
const path = require('path')
/* node function that runs when node is created*/
module.exports.onCreateNode = ({node, actions}) => {
const {createNodeField} = actions
if (node.internal.type === `Mdx`) {
const slug = path.basename(node.fileAbsolutePath, '.mdx')
createNodeField({
node,
name: 'slug',
value: slug
})
}
}
// Creating blog pages
// 1.Get path to template
// 2.Get markdown data
// 3.create new pages
module.exports.createPages = async ({graphql, actions}) => {
const{ createPage } = actions
const blogTemplate = path.resolve('./src/templates/blog.js')
const res = await graphql(`
query {
allMdx {
edges {
node {
fields {
slug
}
}
}
}
}
`)
res.data.allMdx.edges.forEach((edge) => {
createPage({
component: blogTemplate,
path: `/blog/${edge.node.fields.slug}`,
context: {
slug: edge.node.fields.slug
}
})
})
}
I'm quite perplexed, as I can open graphQL and find exactly what I would expect at each stage. What am I doing wrong for the build process to fail yet develop process to work?
It's an odd issue, and it would need a careful debug to know what is happening.
My guess is that in some post, the slug is not properly defined, so it's not matching the String! condition, which means that the slug will be a string (everything ok until here) but non-nullable (because of the exclamation mark, !. Further reference here) so it's breaking your GraphQL query.
Try using it as a nullable field:
export const query = graphql`
query (
$slug: String
) {
mdx(
fields: {
slug: {
eq: $slug
}
}
) {
frontmatter {
title
details
date(formatString: "LL")
tags
}
body
}
}`
Your gatsby-node.js looks good so far.
As I mentioned, the ideal solution would be debugging each post to know if there's any invalid slug (adding a debugger or a console.log and checking its values in the terminal's console).

dynamic URL with API paramethers

I'm new in nextjs and I need to do a dynamic URL using one API parameter
I have some like this data for the API
[
{
"categories" : {
"department": "HR"
"location": "xxxx"
},
"id": "4d2f341-k4kj6h7g-juh5v3",
},
{
"categories" : {
"department": "Operarions"
"location": "xxxx"
},
"id": "4qd3452-fsd34-jfd3453",
},
{
"categories" : {
"department": "Marketing"
"location": "xxxx"
},
"id": "4d2f341-k4kwer37g-juh5v3",
},
]
And I need to do something like that in my index.js
return(
<h1>More details</h1>
<a href="https://page.com/${id}"> link <a>
)
If someone can help me pls
Yes, will try to help you
Imagine you have a folder with the name records, so your url will be /records
In data you provided, you have several categories with ids which ones you like to have as links.(generated records dynamically)
According to nextjs docs, you can create getStaticProps page and generate static pages (it means it's very good for SEO, so use it on visible by SEO pages)
Imagine you have the next structure:
/records
[pid].tsx (or js)
index.tsx
This structure means that if you go to /records URL your index file will be triggered. And for /records/4d2f341-k4kj6h7g-juh5v3 your [pid] file will be triggered.
in index.tsx
import Link from 'next/link'
const Records= ({ records }) => {
return(
<div className="container">
<h1>Records</h1>
{records.map(record=> (
<div>
<Link href={'/records/' + record.id} key={record.id}>
<a>
{record.categories.department}
</a>
</Link>
</div>
))}
</div>
);
}
export const getServerSideProps = async () => {
//your API path here
const res = await fetch('replace_by_your_api_route');
const data = await res.json()
return {
props: {records: data}
}
}
export default Records
in your [pid].tsx
import GeneralUtils from "lib/generalUtils";
const RecordDetails= ({record}) => {
return(
<div>
{record?
<div >
{record.categories.department} {record.categories.location}
</div>
: ''}
</div>
)
}
export async function getStaticProps(context) {
const pid = context.params.pid;
//your api path to get each record where pid it is your parameter you are passssing to your api.
const res = await fetch(process.env.APIpath + '/api/public/getRecord?pid=' + (pid));
const data = await res.json();
return {
props: {record: data}
}
}
export async function getStaticPaths() {
const res = await fetch('your_api_path_same_as_in_index_tsx')
const posts = await res.json()
// Get the paths we want to pre-render based on posts
const paths = posts.map((post) => ({
//your id of each record, for example 4d2f341-k4kj6h7g-juh5v3
params: { pid: post.id.toString() },
}))
// We'll pre-render only these paths at build time.
// { fallback: blocking } will server-render pages
// on-demand if the path doesn't exist.
return { paths, fallback: 'blocking' }
}
export default ProductDetails;
Output: - user types
/records - he will have list of recods with links
/records/4d2f341-k4kj6h7g-juh5v3 - he will have page with info of d2f341-k4kj6h7g-juh5v3
Check this info very carefully and this video

Problem with CreatePage API using gatsby and contentful(Not shown up the content )

I am trying to generate pages of each of the category's data from contentful ...
but there have some concerns.
Success
I could have got the data from contentful ..such as (title, image, and so on ) on the home page(localhost:8000)
Fail
when I clicked the read more button, Card on (localhost:8000), and switched into another page (localhost:8000/blog/tech/(content that I created on contentful)) that has not succeeded and 404pages has appeared(the only slag has changed and appeared that I hoped to).
Error
warn The GraphQL query in the non-page component "C:/Users/taiga/Github/Gatsby-new-development-blog/my-blog/src/templates/blog.js" will not
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.
my-repo
enter link description here
gatsby.node.js
const path = require(`path`);
const makeRequest = (graphql, request) => new Promise((resolve, reject) => {
// Query for nodes to use in creating pages.
resolve(
graphql(request).then(result => {
if (result.errors) {
reject(result.errors)
}
return result;
})
)
});
// Implement the Gatsby API "createPages". This is called once the
// data layer is bootstrapped to let plugins create pages from data.
exports.createPages = ({ actions, graphql }) => {
const { createPage } = actions;
// Create pages for each blog.
const getBlog = makeRequest(graphql, `
{
allContentfulBlog (
sort: { fields: [createdAt], order: DESC }
)
edges {
node {
id
slug
}
}
}
}
`).then(result => {
result.data.allContentfulBlog.edges.forEach(({ node }) => {
createPage({
path: `blog/${node.slug}`,
component: path.resolve(`src/templates/blog.js`),
context: {
id: node.id,
},
})
})
});
const getTech = makeRequest(graphql,`
{
allContentfulBlog (
sort: { fields: [createdAt], order: DESC }
filter: {
categories: {elemMatch: {category: {eq: "tech"}}}
},)
{
edges {
node {
id
slug
}
}
}
}
`).then(result => {
const blogs = result.data.allContentfulBlog.edges
const blogsPerPage = 9
const numPages = Math.ceil(blogs.length / blogsPerPage)
Array.from({ length: numPages }).forEach((_, i) => {
createPage({
path: i === 0 ? `/category/tech` : `/category/tech/${i + 1}`,
component: path.resolve("./src/templates/tech.js"),
context: {
limit: blogsPerPage,
skip: i * blogsPerPage,
numPages,
currentPage: i + 1
},
})
})
});
return Promise.all([
getBlog,
getTech
])
};

(Gatsby + Prismic): Content Relationship URLs

I haveĀ a Gatsby Prismic blog with two custom content types - guide, and review.
I want the URL structure to be www.mysite.com/guide_url/review_url.
What I've done so far:
I've added a content relationship field in Prismic to the parent custom type guide and linked the children (review) to it.
In my gatsby-node.js file the code looks like this:
exports.createPages = ({ graphql, boundActionCreators }) => {
const { createPage } = boundActionCreators;
return new Promise((resolve, reject) => {
graphql(`
{
allPrismicGuide {
edges {
node {
id
uid
}
}
}
allPrismicReview {
edges {
node {
id
uid
}
}
}
`).then(result => {
const categoriesSet = new Set();
result.data.allPrismicGuide.edges.forEach(({ node }) => {
createPage({
path: node.uid,
component: path.resolve(`./src/templates/GuideTemplate.js`),
context: {
// Data passed to context is available in page queries as GraphQL variables.
slug: node.uid
}
});
});
result.data.allPrismicReview.edges.forEach(({ node }) => {
createPage({
path: node.uid,
component: path.resolve(`./src/templates/ReviewTemplate.js`),
context: {
// Data passed to context is available in page queries as GraphQL variables.
slug: node.uid,
tags: node.tags
}
});
});
I'm pretty new to Gatsby and Prismic.
I would love to get some feedback (helpful links) and suggestions!
[Update] Prismic is not longer recommending the gatsby-source-prismic-graphql plugin.
Here's an article that'll help you migrating to the other one:
How to migrate a project from 'gatsby-source-prismic' to 'gatsby-source-prismic-graphql'
The Prismic docs are now recommending the gatsby-source-prismic plugin.
If your relationship field is named review your query should look something like this:
{
allPrismicGuide {
edges {
node {
id
uid
data {
review {
document {
... on PrismicReview {
uid
}
}
}
}
}
}
}
}
Then you should be able to access the child uid in node.data.review.document.uid. So:
createPage({
path: `${node.uid}/${node.data.review.document.uid}`,
// ... other stuff ...
});
In your linkResolver:
if (doc.type === 'guide') {
return `${doc.uid}/${doc.data.review.uid}`;
}
gatsby-source-prismic version: 3.2.1

error building static HTML pages in Gatsby

Getting an error when building site even though Gatsby develop works fine, the markdown is coming from Netlify CMS. The specific error is:
error Building static HTML failed for path "/null"
See our docs page on debugging HTML builds for help https://gatsby.app/debug-html
11 | export default function Template({ data }) {
12 | const { markdownRemark } = data;
> 13 | const { frontmatter, html } = markdownRemark;
| ^
14 | return (
15 | <Layout>
16 | <SEO title="Home" keywords={[`gatsby`, `application`, `react`]} />
WebpackError: TypeError: Cannot read property 'frontmatter' of null
- blogTemplate.js:13 Template
lib/src/templates/blogTemplate.js:13:11
I have been reading the other similar paths and have tried changing the path in my markdown files from the cms but it was to no avail.
gatsby-config.js
{
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/blog`,
name: "markdown-pages"
}
},
blogTemplate.js
export default function Template({ data }) {
const { markdownRemark } = data;
const { frontmatter, html } = markdownRemark;
return (
<Layout>
<SEO title="Home" keywords={[`gatsby`, `application`, `react`]} />
<div className="blog-posts">
<h1 className="blog-title">{frontmatter.title}</h1>
<h2 className="blog-date">{frontmatter.date}</h2>
<div
className="blog-post-content"
dangerouslySetInnerHTML={{ __html: html }}
/>
<Link to="/blog/" className="backBTN">
Go Back
</Link>
</div>
<Footer />
</Layout>
);
}
export const pageQuery = graphql`
query($path: String!) {
markdownRemark(frontmatter: { path: { eq: $path } }) {
html
frontmatter {
date(formatString: "MMMM DD, YYYY")
path
title
}
}
}
`;
gatsby-node.js
const path = require("path");
exports.createPages = ({ actions, graphql }) => {
const { createPage } = actions;
const blogPostTemplate = path.resolve(`src/templates/blogTemplate.js`);
return graphql(`
{
allMarkdownRemark(
sort: { order: DESC, fields: [frontmatter___date] }
limit: 1000
) {
edges {
node {
frontmatter {
path
}
}
}
}
}
`).then(result => {
if (result.errors) {
return Promise.reject(result.errors);
}
result.data.allMarkdownRemark.edges.forEach(({ node }) => {
createPage({
path: `${node.frontmatter.path}`,
component: blogPostTemplate,
context: {}
});
});
});
};
GraphQL
the query returns the following error unless I pass in a specific path of one of the blog posts then it returns the correct results
{
"errors": [
{
"message": "Variable \"$path\" of required type \"String!\" was not provided.",
"locations": [
{
"line": 1,
"column": 7
}
]
}
]
}
graphql screenshot with variable passed in
I think that is all the relevant code that is needed here, just let me know if anything else is needed.
Check out this very first line of the error:
error Building static HTML failed for path "/null"
I bet one of your .md has an empty path. Maybe you can try filtering to find a markdown node with empty frontmatter path, or null?
If that path was coming directly from netlifyCMS & you use the string widget, IIRC you can pass in a default options so it'll fallback to that

Resources