I’m working on a Next.js project where I need to fetch markdown files from a GitHub repository using an API route. The API route fetches and processes the markdown files, then returns the data to be used in a blog page. The setup works fine locally, but when deploying to Vercel, I’m facing performance issues and timeouts, likely due to Vercel’s Hobby plan limitations.
What I tried:
API Route for Fetching Blog Data:
I created an API route in Next.js to fetch markdown files from a GitHub repository. This route uses axios to fetch the file content, gray-matter to parse the front matter, and then returns the parsed data.
import axios from "axios";
import matter from "gray-matter";
import type { NextApiRequest, NextApiResponse } from "next";
const OWNER_NAME = "Attraxia";
const REPO_NAME = "Blogs";
const TOKEN = process.env.NEXT_PUBLIC_GITHUB_BLOGS_REPO_ACCESS_TOKEN!;
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const { slug } = req.query;
if (!slug || Array.isArray(slug)) {
return res.status(400).json({ error: "Invalid or missing slug parameter" });
}
try {
const url = `https://api.github.com/repos/${OWNER_NAME}/${REPO_NAME}/contents/Blogs/${slug}.mdx?ref=dev`;
const response = await axios.get(url, {
headers: {
Authorization: `token ${TOKEN}`,
},
});
const content = Buffer.from(response.data.content, "base64").toString("utf-8");
const { data: metadata, content: mdxContent } = matter(content);
return res.status(200).json({ metadata, content: mdxContent });
} catch (error) {
return res.status(500).json({ error: "Failed to fetch blog data" });
}
}
Using getServerSideProps for SEO:
I called this API route within getServerSideProps to fetch the blog data server-side for SEO purposes.
import React from "react";
import axios from "axios";
import { BlogProvider } from "context/blogs/BlogContext";
import { BlogData } from "context/blogs/BlogContext/BlogContext.types";
import { GetServerSideProps } from "next";
import WrappedBlogPage from "@features/blogs/Blog/Blog";
interface BlogProps {
initialBlogData: BlogData | null;
}
const Blog: React.FC<BlogProps> = ({ initialBlogData }) => {
return (
<BlogProvider initialBlogData={initialBlogData}>
<WrappedBlogPage />
</BlogProvider>
);
};
export const getServerSideProps: GetServerSideProps = async (context) => {
const { slug } = context.query;
const { req } = context;
const baseURL = req ? `http://${req.headers.host}` : "";
try {
const response = await axios.get(`${baseURL}/api/blog/fetchBlogData?slug=${slug}`);
return {
props: {
initialBlogData: response.data,
},
};
} catch (error) {
return {
props: {
initialBlogData: null,
},
};
}
};
export default Blog;
Context Setup for Blog Data:
I set up a context to manage the blog data state.
import React, { createContext, ReactNode, useContext, useEffect, useMemo } from "react";
import { MDXProvider } from "@mdx-js/react";
import components from "@features/blogs/Blog/components/MDX/MDXComponents";
import { BlogContextType, BlogData } from "./BlogContext.types";
const BlogContext = createContext<BlogContextType | undefined>(undefined);
export const BlogProvider: React.FC<{ children: ReactNode; initialBlogData: BlogData | null }> = ({
initialBlogData,
children,
}) => {
const [loading, setLoading] = React.useState<boolean>(true);
const [isError, setIsError] = React.useState<boolean>(false);
useEffect(() => {
if (!initialBlogData) {
setIsError(true);
} else {
setLoading(false);
}
}, [initialBlogData]);
const ContextValues = useMemo(() => ({ initialBlogData, loading, isError }), [initialBlogData, loading, isError]);
return (
<BlogContext.Provider value={ContextValues}>
<MDXProvider components={components}>{children}</MDXProvider>
</BlogContext.Provider>
);
};
export const useBlog = (): BlogContextType => {
const context = useContext(BlogContext);
if (!context) {
throw new Error("useBlog must be used within a BlogProvider");
}
return context;
};
What I Expected:
I expected the API route to quickly fetch the blog data from the GitHub repository and return it to the getServerSideProps function. The blog data should then be passed to the page component via props and rendered correctly. This setup should ensure good SEO performance since the blog content is rendered server-side.
However, I’m encountering performance issues and timeouts on Vercel, especially given the constraints of the Hobby plan. The API route is often slow, leading to delays and occasional failures in fetching the blog data.
Any insights on how to optimize the performance of this API route would be greatly appreciated!
Zied Abadlia is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.