October 29, 2020 | 2 minutes read

Using Dev.to as a CMS with Next.js

#react#nextjs#cms#devto

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.

1// postsApi.tsx
2
3let normalizePost = (post: Post): Post => {
4 const { data, content } = matter(post.body_markdown);
5 return {
6 ...post,
7 // remove the last bit (its a 4 digit identifier, not needed here)
8 slug: post.slug.split("-").slice(0, -1).join("-"),
9 matter: { data, content },
10 };
11};
12
13let sleep = async (ms: number = 0) => new Promise(resolve => setTimeout(resolve,ms))
14
15export let query = async () => {
16 // next does parallel builds
17 // dev.to doesnt allow more than 2 requests per second (as far as I can tell
18 // we gotta slow it down
19 await sleep(1000)
20 // we cache the response
21 // otherwise we'll hit the 429 error "Too many requests" during build times
22 let cached = cache.get<Post[]>();
23 if (cached) return cached;
24
25 let posts: Post[] = [];
26 let page = 0;
27 let per_page = 30; // can go up to 1000
28 let latestResult = [];
29
30 do {
31 page += 1; // bump page up by 1 every loop
32 latestResult = await fetch(
33 `https://dev.to/api/articles/me/published?page=${page}&per_page=${per_page}`,
34 {
35 headers: {
36 "api-key": process.env.dev_token as string,
37 },
38 }
39 )
40 .then((res) =>
41 res.status !== 200 ? Promise.reject(res.statusText) : res.json()
42 )
43 .then((x) => (posts = posts.concat(x)))
44 .catch((err) => {
45 throw new Error(`error fetching page ${page}, ${err}`);
46 });
47 } while (latestResult.length === per_page);
48 posts = posts.map(normalizePost);
49 cache.set(posts);
50 return posts;
51};

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

1429
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

1query
to get a list of my posts:

1// pages/blog.tsx
2
3export async function getStaticProps() {
4 let posts = await postsApi.query();
5 return {
6 props: {
7 posts,
8 },
9 revalidate: 1,
10 };
11}

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