Like in my last lesson, there will be a reference image of my file structure and file content so you can compare.
Creating a Sanity client
We need to create a way for our app to fetch the data we store inside of Sanity. Luckily Sanity makes this very easy for us. You can read up more on what I'm using here. Sanity Client
npm i -g @sanity/client &&
npm i next-sanity
This will install the Sanity client globally to use for any project.
We need to create a folder in our root named lib
and a file within that folder named sanity.js
and paste this code in your new file.
const sanityClient = require("@sanity/client");
const client = sanityClient({
projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID,
dataset: process.env.NEXT_PUBLIC_SANITY_DATASET,
apiVersion: "2021-03-25", // use current UTC date - see "specifying API version"!
useCdn: true, // `false` if you want to ensure fresh data
});
export default client;
This code can be found on the Sanity Client package docs.
Fetching our posts
In your /pages/index.js
file (not your /studio/schemas/index.js
), at the bottom, you need to create an async/await function to fetch your data.
export async function getStaticProps() {}
While at the top of this page you need to import the client we just created.
import client from "../lib/sanity";
You can, and should, read up on getStaticProps
here NextJS docs.
On that page it will explain what this function does, how it works, and when to use it. One of the reasons to use it is if The data comes from a headless CMS
, which is exactly how we plan to use it.
Inside of our new function we are going to make a GROQ query to tell it what data to look for, how to order the data, and what pieces of that data we want returned to us.
export async function getStaticProps() {
const query = `*[_type == "post"] | order(publishedAt desc) {
_id,
title,
publishedAt,
'slug': slug.current,
body
}`;
const posts = await client.fetch(query);
return {
props: { posts },
};
}
I'm going to break each part of this function down to make it easier to understand, because just copying and pasting code without the knowledge of what each piece means isn't going to be very useful later on.
-
const query = `...`
- Obviously this is just us declaring a variable for our query to use later. But what is important here is the fact that our code within the query is surrounded by back ticks.
-
*[_type == "post"]
- This is telling our client to grab every entry we have in our database that has a
name
of post - You can see that we named our entries back when we created our 'Post' schema. That is what this
"post"
query is referring to.
- This is telling our client to grab every entry we have in our database that has a
-
| order(publishedAt desc)
- Simple enough, this is telling our query to return the 'posts' that it fetched in an order by the published date in descending order. This means newest posts return first.
{
_id,
title,
publishedAt,
'slug': slug.current,
body
}
- We are telling the fetch request what data we actually want returned. This is the power of GROQ, you only receive the data you want. Without this part you would get the entire 'post' data which looks something like this.
{
"_createdAt": "2022-07-17T00:48:06Z",
"_id": "f026b8eb-0fc6-4a58-8494-789def2703ff",
"_rev": "IvZ71YmXkO22WtmwIxDKV0",
"_type": "post",
"_updatedAt": "2022-07-17T00:48:06Z",
"body": (5) [{…}, {…}, {…}, {…}, {…}],
"publishedAt": "2022-07-17T00:45:31.070Z",
"slug": {
"_type": "slug",
"current": "the-art-of-creating-a-post"
},
"title": "The art of creating a post"
}
That's way more information than we need. So we are going to tell our query to only return to us the _id, title, publishedAt, slug, and body.
For the curious, slug is written as 'slug': slug.current
because as you can see above, slug returns an object but we only need the current
value, not the _type
. So we are returning slug.current
but assigning it to the slug
key name.
-
const posts = await client.fetch(query);
- Simple fetch request that uses the
client
we created and imported in this lesson, thequery
variable we just added, and assigns the return to a new variable namedposts
- Simple fetch request that uses the
-
return { props: { posts } };
- Return props with our posts variable so our component can use it.
Rendering our posts
Back at the top of our /pages/index.js
file we are currently in, we are going to add our posts
variable we just fetched to our Home
component.
export default function Home({ posts }) {
return <h1>Hi :D</h1>;
}
We are able to grabs our posts
data from the props
value by a method called destructuring
.
Now to create HTML elements from our posts
array. We will handle this by mapping over the data and saving it to a variable.
const postsElements = posts.map((post, index) => (
<div key={index}>
<p>{new Date(post.publishedAt).toDateString().slice(4)}</p>
<h3>{post.title}</h3>
</div>
));
This will return a div
with a p
and h3
tag for each post we have.
We need the outer div
simply because you need multiple elements to be wrapped in 1 so you can return it. Returning the p
and the h3
without wrapping it in something will throw an error. We also add the key
to this with the index
of it's position in the array so React doesn't scream at us. (this is very useful to read up on, it gave me hours of debugging when I started learning React).
We add a p
tag with the publishedAt
value turned into a Date, turned into a string so HTML can read it, and we slice
off the first 4 characters of the string (essentially removing the day of the week).
And finally an h3
with the title
of our post.
Now if you add this variable in your return (wrapping it again in an outer div for the same reason as above, you must return 1 outer HTML element) like this.
return <div>{postsElements}</div>
Closing your current npm run dev
and starting it again you should see all of your posts (but only the title and published date) on your homepage by date they were published.
It's quite ugly right now, but that's not the point. I will create a future lesson, or maybe edit this one, to add the methods I'm going to use to style it. I just want to get this out in the meantime to help people that need it.
Upcoming lessons
That's everything for this part. I might have went overboard explaining every little detail, but all of these concepts are very fundamental and will be reused later in this project (and most likely in your future projects). So I wanted to help you get a grasp of how everything works so you will have the knowledge to explore and make something yourself.
In the next part I will explain how to create dynamic urls for your posts so you can see the full information for it on it's on page.
References
- File Structure
- /pages/index.js
import client from "../lib/sanity";
export default function Home({ posts }) {
const postsElements = posts.map((post, index) => (
<div key={index}>
<p>{new Date(post.publishedAt).toDateString().slice(4)}</p>
<h3>{post.title}</h3>
</div>
));
return <div>{postsElements}</div>;
}
export async function getStaticProps() {
const query = `*[_type == "post"] | order(publishedAt desc) {
_id,
title,
publishedAt,
'slug': slug.current,
body
}`;
const posts = await client.fetch(query);
return {
props: { posts },
};
}
- /lib/sanity.js
const sanityClient = require("@sanity/client");
const client = sanityClient({
projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID,
dataset: process.env.NEXT_PUBLIC_SANITY_DATASET,
apiVersion: "2021-03-25", // use current UTC date - see "specifying API version"!
useCdn: true, // `false` if you want to ensure fresh data
});
export default client;
Top comments (0)