So now we wanna use our markdown files
in the posts folder as a data source.
For example, for the starting page here,
instead of using dummy posts, we wanna fetch our posts
from that post folder,
for example with help of get started props.
Now, before we do that,
I'll first of all create a new folder in the project though,
a lib folder or helpers folder,
whatever you wanna call it.
And in there, I'll add posts-util.js file.
This simply is a file
which will have extra utility functionality
for fetching my post data
and for extracting metadata from those markdown files.
And speaking off that,
for this we need to install another extra package.
So quit your running development server
and install an extra package with npm install.
And that would be the gray-matter package.
That's a package which allows us to read a markdown file
and split it into metadata and the actual markdown content.
Once that's installed,
we can start the development server again.
And now in posts-util.js,
I wanna add a couple of functions that fetch us all posts,
all featured posts and then also a single post.
Now let's maybe start with all posts.
Let's add a getAllPosts function here.
The idea here is that we take a look
at this posts folder with the markdown files
and simply check how many markdown files we have there
and then go through all those markdown files
to get the metadata for all those files.
Now for going through all those files here,
I will work with the file system
and hence we should import fs from fs.
So import does fs node module.
And then here in getAllPosts,
I'll start by reading all the content from a directory,
with the fs module and the readdirSync method,
readdirSync will read all the contents synchronously.
So in a blocking way, which is fine here,
because I wanna parse all posts anyways
before I do anything else.
So I'll read the entire content off a directory
in one go here.
Now we need to pass a path
to the directory to readdirSync.
And I'll create this
as a separate constant outside of all those functions here.
I'll name it postsDirectory,
though of course, the name is up to you.
And then here, I'll import another thing from node JS.
I'll import the path module here.
This allows me to construct an absolute path,
with the join method.
And here I join process.current working directory,
executed as a method,
which will point at the overall project folder,
as you learned, not at this lib folder,
but at the overall project folder.
So that is then an absolute path
to the overall project folder,
and then I'll add posts
as an extra folder into which it should dive.
So this posts folder here.
That's then an absolute path to my posts directory
and that's the directory I wanna read.
So that's what I pass to readdirSync.
Now this will return an array of strings, as you see.
In the end, it will be an array of file names.
So here I have my postFiles
which are load with readdirSync.
As a next step,
I now wanna go through all those post files and dive
into them to extract metadata and markdown content.
I also wanna derive a slug
which can be put in the URL from the file name.
So basically I wanna use the file name,
without the extension, which still is part inside
of postFiles at the moment.
Now for this I'll add an extra helper function,
getPostData, sounds like a fitting name.
And here I expect to get the file name as an argument.
Now in getPostData, we wanna read the content of a file.
We can again do that with the fs module.
And there, we have that readFileSync method
for reading the content of a file.
Here, we need the fileName, but not just the fileName,
we still need the full path to that file.
So I'll construct my file path here
by again using path.join
and I'll join this posts directory path with
which we already have, with that file name.
So that constructs a full absolute path
to that concrete file.
Now we can use this filePath
for reading the content of that file.
We can always add a second argument here
and make it clear that the file encoding is UTF-8
so that we support all those Unicode characters.
Then we got the file content here.
That's what readFileSync produces.
Now we're doing this for a single file here.
So I'm not doing this in a loop or anything like this.
I'm just doing this for one file.
We're going to call getPostData
in a loop for all files later, though.
But here we then have to content all the single file.
And now we know that in a single file,
we have this metadata and the actual content.
Now the overall content is what node JS extracted for us.
To split it up,
we'll use this gray matter package which we installed.
So for this I'll import matter from gray-matter, like this.
And matter is simply a function
which we can call here to which we pass a string.
And our file content is just a string.
It's the content of a file as text.
So we pass the file content to matter,
and matter will then return an object with two properties,
with a data property for the metadata.
So a data property, which contains the metadata
as a JavaScript object and a content property
which contains the actual content,
the markdown text as a string.
So we can use object destructuring
to pull these two properties out of the returned object
and store them in separate constants, data and content.
And these names are not up to you,
these are the properties in that object created by matter.
So by this matter function.
Now that we got that,
we can prepare our own postData object
which should contain all that metadata and that content,
but also contain an extra select field with the slug,
so with the file name, without the file extension.
For this I'll first of all, create my post slug here
and we can easily create this by using our file name
and simply replacing the file extension with nothing.
We could use a regular expression here
to select the markdown extension,
with this regular expression
and replace it with an empty string.
So this removes the file extension in the end,
that leaves us with a slug
which is just a file name without the extension.
And therefore, of course, you should make sure
that your file names do have this slug format
and do not contain any special characters or white space.
And once that's done,
we can prepare that post data which we return.
Here, I wanna add a slug field
and store my created post slug.
I will then also spread my data in there.
So the data we extract it from the markdown file
and I'll add a content field
and point at the content extracted from markdown.
And of course, here we can use the shortcut.
So that's now a object containing the data
for a single post read from that markdown file.
And that's what I wanna return here in getPostData.
Okay, so now we have that getPostData function,
we have getAllPosts where we get all those files.
Now we wanna call, getPostData
for every post we got here in getAllPosts.
So there, we can of course, just create a for loop
to go through all the postFiles we have here,
like this for example, and for every file we wanna call,
getPostData and pass the postFile,
so the file name in the end to getPostData.
And since in there we use readFileSync,
we will block the fervor loop execution,
until we parse the data, data.
And that will be super fast, no worries.
So that will not take long.
It will be milliseconds.
So then here we get our postData like this for a given post.
And overall, we now wanna create an array
of postData pieces.
So an array off such post objects.
And therefore, we can actually also change,
this a little bit.
In the end, what we wanna do is we wanna map our array
of postFiles into an array of postData objects.
So we could also just use postFiles and call map
to execute some logic on every post file.
And in the end, just return getPostData
for the given post file.
So then we would map our array of file names into an array
of objects with the actual data for those different posts.
And I think that makes more sense.
Now, map returns a new array
it does not change the existing array.
So I will create a new allPosts array
which is an array full of postData.
Now maybe we also wanna sort that
so that we have the latest posts first
and hence I'll call allPosts sort here to sort my posts.
It'll add my own sorting function here
where we get postA and postB.
And in the end, we wanna now check
if postA.date is greater than postB.date.
And if that's the case, return minus one,
otherwise return one.
That's the default JavaScript sort method
which will make sure that posts with a greater date,
so more recent posts are actually sort at,
in front of older posts.
That all returns a new array, the sortedPosts array
and that's now what I'll return here,
inside off this getAllPosts function.
Now that was a lot of work,
but I hopefully could make it clear what happens.
But with that, we have the getAllPosts function
which reads all the posts in the posts folder, no matter
if that's one or multiple posts and then gets all the data
for those posts and also sorts them and then returns them.
Now to finish up the work on this file,
I now just wanna add one other function
which is responsible for getting all the featured posts.
So I'll name it getFeaturedPosts.
And here, I'll first of all get all posts again
by calling getAllPosts.
So that's simple.
And then I'll just find out what my featured posts are
by taking all posts and filtering them
by calling filter on them.
So here I'll then look
at every single post and simply check if post isFeatured
if that's true.
If it's true, we'll keep it.
If it's false, we'll drop it.
So featuredPosts then only contains the posts
where isFeatured is set to true.
And then I'll return this here.
That means that we still go through all posts first,
but we'll have to do it anyways.
We'll have to look into every file anyways
to find out whether it's featured or not.
If you don't wanna do that,
you would have to keep an extra data source around,
something like a json file
which holds the slugs of all the featuredPosts.
And then you would just read those
and then just pull the specific markdown files
which this extra json file points at.
That could be an alternative
for a bigger blog with hundreds or thousands
of posts to increase the build performance a little bit,
but for this block, this approach is absolutely fine.
So now we've got all of this and now
as a last step I'll export the getFeaturedPosts
and the getAllPosts functions,
because we will need those to fetch that data.