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;
};
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,
};
}
Tada! It was much smoother than I expected. Thanks a lot dev.to!
Top comments (4)
Did you also find a way to resolve twitter / youtube embedded content?
Don't know if the
dev.to
custom markdown like{% twitter 834439977220112384 %}
can be used as mdx?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.
awesome!
I did something like this. I used on the fly server side prop fetching instead though. Github.