Subscribe

Creating a generic posts function - part 14

โœ๏ธ

Modifying our posts calls to one generic reusable function

21 Oct, 2022 ยท 6 min read

For this article, I want to add the latest two blog posts to our homepage. However, it quickly made me realize we are reusing the same code repeatedly.

So let's start this article by re-doing some of the code so we can reuse it in existing components.

Before we start, we'll identify the different calls we currently have:

  • A function that retrieves all posts
  • A function that retrieves a single post by slug

And for the homepage, we want to retrieve all posts sorted by date and render two of them.

Creating a shared function

Create a new folder called lib inside your root directory. Add a file called api.js which will serve as our API.

We can start by adding the call for all posts. We have this function already in our blog post overview, so modify and copy that one.

import fs from 'fs';
import matter from 'gray-matter';

export function getAllPosts() {
  const files = fs.readdirSync('./posts');
  const posts = files
    .map((fileName) => {
      const slug = fileName.replace('.md', '');
      const readFile = fs.readFileSync(`posts/${fileName}`, 'utf-8');
      const { data: frontmatter } = matter(readFile);
      return {
        slug,
        ...frontmatter,
      };
    })
    .sort((post1, post2) => (post1.date > post2.date ? -1 : 1));

  return posts;
}

The only thing I added is a sort based on the date. This way, we always show the newest articles first.

As mentioned, the rest is a copy-paste from what we already had.

Now let's also create the getPostBySlug function, which we can copy from our [slug] page again.

export function getPostBySlug(slug) {
  const fileName = fs.readFileSync(`posts/${slug}.md`, 'utf-8');
  const { data: frontmatter, content } = matter(fileName);
  return {
    frontmatter,
    content,
  };
}

For the people paying attention, you'll see this function duplicates what we had inside the all-post function.

Let's refactor that one so it uses this now.

export function getAllPosts() {
  const files = fs.readdirSync('./posts');
  const posts = files
    .map((fileName) => {
      const slug = fileName.replace('.md', '');
      const { frontmatter } = getPostBySlug(slug);
      return {
        slug,
        ...frontmatter,
      };
    })
    .sort((post1, post2) => (post1.date > post2.date ? -1 : 1));

  return posts;
}

Way cleaner!

Using the new API calls

Now that our API calls are ready, let's adjust our pages so they use this.

The first one is the pages/blog/index.js file.

import { getAllPosts } from '../../lib/api';

export async function getStaticProps() {
  const posts = getAllPosts();

  return {
    props: {
      posts,
    },
  };
}

Yep, it's as simple as that! The benefit, of course, is that if we ever change something in our post call, it's reflected everywhere that uses it.

The next file we need to change is the pages/blog/[slug].js file. This will use both function as they will look like this:

import { getAllPosts, getPostBySlug } from '../../lib/api';

export async function getStaticPaths() {
  const posts = getAllPosts();

  return {
    paths: posts.map((post) => {
      return {
        params: {
          slug: post.slug,
        },
      };
    }),
    fallback: false,
  };
}

export async function getStaticProps({ params: { slug } }) {
  const post = getPostBySlug(slug);
  return {
    props: post,
  };
}

And yep, that's all. Our system is now back to working as intended.

Adding the latest post to the homepage.

Let's come back to the initial idea for this article. We wanted to add the latest two articles on the homepage, so how can we do this now?

Open up the pages/index.js file and add a staticProps function.

import { getAllPosts } from '../lib/api';

export async function getStaticProps() {
  const posts = getAllPosts();

  return {
    props: {
      posts,
    },
  };
}

This will ensure we have access to our post data. Inside the component, we can retrieve this posts variable and, in return, pass it to our recent posts component.

export default function Home({ posts }) {
  return (
    <div>
      <Head>
        <title>NextJS Portfolio</title>
        <meta name='description' content='Generated by create next app' />
        <link rel='icon' href='/favicon.ico' />
      </Head>
      <IntroHeader />
      <RecentPosts posts={posts} />
      <FeaturedWork />
    </div>
  );
}

Let's go to the recent posts component and ensure we know what to do with this data.

import SectionHeader from './sectionHeader';
import Article from './article';

export default function RecentPosts({ posts }) {
  return (
    <section className='bg-blue-100 px-6'>
      <div className='max-w-4xl mx-auto py-12'>
        <SectionHeader title='Recent posts' href='#' />
        <div className='grid grid-cols-1 md:grid-cols-2 gap-6'>
          <Article post={posts[0]} />
          <Article post={posts[1]} />
        </div>
      </div>
    </section>
  );
}

And yep, that's it!

Homepage latest posts

Our homepage now shows the latest two articles, and we didn't have to recreate existing code for it.

You can also find the completed code for today on GitHub.

Thank you for reading, and let's connect!

Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on Facebook or Twitter

Spread the knowledge with fellow developers on Twitter
Tweet this tip
Powered by Webmentions - Learn more

Read next ๐Ÿ“–

Next portfolio - Filter by category

30 Nov, 2022 ยท 5 min read

Next portfolio - Filter by category

A glance at Turbopack

17 Nov, 2022 ยท 3 min read

A glance at Turbopack

Join 1903 devs and subscribe to my newsletter