loading...

Using Dev.to as a CMS with Next.js

cvr profile image Cristian Velasquez Ramos Originally published at cvr.im ・2 min read

Thanks to @swyx for the idea and most of the logic. Check his post out.

Dev.to is a great place to share and learn from others.

It can get pretty tedious if you're busy posting blogs in two places. Luckily, Dev.to provides an API that allows you to get all your posted content for free!

If you want a full implementation, check out my website code

Here's the code I used to get it working. It has some minor changes from Swyx's code.

// postsApi.tsx

let normalizePost = (post: Post): Post => {
  const { data, content } = matter(post.body_markdown);
  return {
    ...post,
    // remove the last bit (its a 4 digit identifier, not needed here)
    slug: post.slug.split("-").slice(0, -1).join("-"),
    matter: { data, content },
  };
};

let sleep = async (ms: number = 0) => new Promise(resolve => setTimeout(resolve,ms))

export let query = async () => {
  // next does parallel builds
  // dev.to doesnt allow more than 2 requests per second (as far as I can tell
  // we gotta slow it down
  await sleep(1000)
  // we cache the response
  // otherwise we'll hit the 429 error "Too many requests" during build times
  let cached = cache.get<Post[]>();
  if (cached) return cached;

  let posts: Post[] = [];
  let page = 0;
  let per_page = 30; // can go up to 1000
  let latestResult = [];

  do {
    page += 1; // bump page up by 1 every loop
    latestResult = await fetch(
      `https://dev.to/api/articles/me/published?page=${page}&per_page=${per_page}`,
      {
        headers: {
          "api-key": process.env.dev_token as string,
        },
      }
    )
      .then((res) =>
        res.status !== 200 ? Promise.reject(res.statusText) : res.json()
      )
      .then((x) => (posts = posts.concat(x)))
      .catch((err) => {
        throw new Error(`error fetching page ${page}, ${err}`);
      });
  } while (latestResult.length === per_page);
  posts = posts.map(normalizePost);
  cache.set(posts);
  return posts;
};
Enter fullscreen mode Exit fullscreen mode

Here you can see it's a simple loop aggregating all of our posts.

I've added a cache because, on my website, Dev.to would return a 429 status code because the requests were happening too fast.

This is fine since there's almost no chance our posts will become stale while we are building.

After that I call query to get a list of my posts:

// pages/blog.tsx

export async function getStaticProps() {
  let posts = await postsApi.query();
  return {
    props: {
      posts,
    },
    revalidate: 1,
  };
}
Enter fullscreen mode Exit fullscreen mode

Tada! It was much smoother than I expected. Thanks a lot dev.to!

Discussion

pic
Editor guide
Collapse
tylerlwsmith profile image
Tyler Smith

I've been thinking about doing this myself! As of this week I'm up to 20 posts and that seems like enough to warrant their own site.

Collapse
ije profile image
X.

awesome!

Collapse
shadowtime2000 profile image
shadowtime2000

I did something like this. I used on the fly server side prop fetching instead though. Github.