Fancy Blog With GraphCMS

author
Andrés Sanabria

5 MIN READ


As a developer I love to play with the newest and coolest technologies. The last months I've been learning a lot about GraphQL and the way it works seems really interesting to me. Other technology I've been using a lot is Next.js to build web applications which provides powerful features out the box like SSR and SSG (and even serverless APIs).

It comes a time when you want to use all these new technologies but sometimes you don't have the chance in your day to day working place. I think that is the main reason why curious developers don't use sites like Wordpress or Medium for their personal blogs (and end up building a custom site for blogging, writing a few posts and never write again). These kind of people like me, love to build things. And a personal blog is the perfect project to try new and fancy tech.

Where my blog started

At the beginning it was only my web portfolio with general information about me and projects I worked on. I remember the first version of it was built with Angular (then I tried React and I'm never going back). The last year I made a complete redesign of my portfolio to convert it in my own place in the internet, including my blog. It started with a new fresh project using Next.js.

The first version of my blog CMS was just a directory with markdown files. I used those files with SSG features provided by Next.js and it worked like charm. I was proud of it, but even if it worked pretty good and was a simple "elegant" solution it had its drawbacks. Some pain points came up:

  • How can I enable spell checking while writing in markdown?
  • How to manage assets?
  • What about entries in multiple languages?
  • How to manage posts authors?

Well I kind of figured it out while still using markdown but it was a tedious work. So I decided I needed something more sophisticated. Since I didn't want to migrate my blog to other platform the best solution was to use a headless CMS. I compared a lot of them and the one that catched my eye was GraphCMS.

Why GraphCMS?

To start, if you like GraphQL you will love GraphCMS. But that is not the only reason I choose GraphCMS for my blog, it has a good user experience and is aesthetically pleasing to use (that's important to me). One feature I liked the most was to being able to create completely custom schemas for my posts entries and authors. It really gives you completely freedom on how simple or complex setup your data models, with useful rules like required fields or minimum length and even relationships with other schemas.

Screen Shot 2021-05-16 at 12.47.18 PM.png

Other feature I liked was how simple it was to manage assets. Before, when I used markdown I had to put those files in a CDN or in the public directory. What if I changed the name of the assets or maybe the path? I had to change the url of those assets in all of my posts. GraphCMS handles this for you in a better structured way.

Their API also is straightforward to use and well documented. If you are familiar with GraphQL the playground is extremely useful to test queries, mutations and to review the documentation explorer. GraphCMS has its own playground integrated, and for sure developers like me appreciate it.

And you know? I can still write the content of my posts with markdown since GraphCMS provides this type of field. I prefer to write the content in markdown because I often include code snippets with blog tutorials. Also the whole rendering system of my frontend in Next.js uses markdown to render the posts content.

Next.js setup

Next.js makes easy for us to generate static websites at build time. When it comes to performance and SEO it has a lot of advantages against client side rendering. You can review a complete example of how to integrate GraphCMS with Next.js. It uses Next.js getStaticProps and getStaticPaths to build static pages. The required tasks are basically query the posts and then use that data to generate the pages.

type getPostParams = {
  slug: string
  locales: string[]
}
 
export const getPostQuery = gql`
  query PostQuery($slug: String!, $locales: [Locale!]!) {
    post(where: { slug: $slug }, locales: $locales) {
      id
      title
      slug
      views
      content
      date
      cover {
        url
      }
      author {
        name
        picture {
          url
        }
      }
    }
  }
`
 
async function getPost({
  slug,
  locales = ['en'],
}: getPostParams): Promise<Post> {
  const { post } = await graphcmsClient.request(getPostQuery, {
    slug,
    locales,
  })
 
  return post
}
 
export default getPost
interface Props {
  post: Post
}
 
export const getStaticPaths: GetStaticPaths = async () => {
  const posts = await getAllPosts({
    locales: ['en'],
  })
 
  return {
    paths: posts.map((post) => ({
      params: { slug: post.slug },
    })),
    fallback: false,
  }
}
 
export const getStaticProps: GetStaticProps = async ({ params }) => {
  const post = await getPost({
    locales: ['en'],
    slug: params.slug as string,
  })
 
  return {
    props: {
      post,
    },
  }
}
 
const PostPage: FC<Props> = ({ post }) => {
  return (
    <Layout seo={{ title: post.title }}>
      <article className="blogContent">
        <h1>{post.title}</h1>
        <Author
          name={post.author.name}
          picture={post.author.picture.url}
          date={post.date}
        />
        <Divider />
        <img className={styles.coverImage} src={post.cover.url} alt="cover" />
        <ReactMarkdown remarkPlugins={[gfm]} components={{ code: CodeBlock }}>
          {post.content}
        </ReactMarkdown>
      </article>
    </Layout>
  )
}
 
export default PostPage

Limitations

GraphCMS fulfills its purpose, but has its limitations too. I though I could build a views counter using a private schema value. And in fact you can but the API is not meant for that edge scenarios. What happens is that the API is optimized to manage content but not for transactional operations. So in the case of a views counter, multiple clients could try to update the number of views relying on the previous value. This is one example why batch and transactions are supported by databases.