min read

Migrating from Gridsome to Nuxt 3

While originally this site was built with Gridsome, I've decided to move forward and migrate it to Nuxt 3. This post is by no means a "How-To" but rather a small overview of the gains and difficulties coming with this step.

Why migrate in the first place?

The main reason for that decision was, because Gridsome seems to be dead

While the maintainer(s) of Gridsome have not made any official statement regarding its future

Gridsome commit activity

While I'm not saying Gridsome is unusable - it certainly is as good as it was - I don't want to be stuck with Vue 2 forever and watch packages outdating all over the place and running into version conflicts because of it.

Although there were ambitions already to migrate to Vue 3 and first steps were already made, its hard to believe that it will ever be completed.

While Vue was one of the core reasons of my decision, there was another big one: monorepos.

I've started probably over 100 projects in my last 10 years of being a developer (mosly tryouts/experiments and prototypes) and one thing that annoys me the most is everything about configurations that keep you away from the actual work.

With tools like Nx and Gradle I made the decision to create one frontend and one backend project for all of my projects. The core of all applications will evolve over time, standards and reusable components evolve and the quality is in my opinion expected to rise over time.

Difficulties

What sounded easy first, was a bit harder after looking into it closer. The main reasons were the plugins being available for Gridsome, but not (yet) for Nuxt 3.

Plugins that don't exist for Nuxt 3

  1. Gridsome Recommender
    The plugin was developed by myself for convenience. I did not want to link similar posts manually, but automatically by their context and I also did not want to link tags to posts manually.
    Gridsome's unique way of processing markdown files into a data layer first before providing it via GraphQL enabled the possibility to look at all the content and modify it before providing it to the GraphQL API.
    This is currently not possible with Nuxt Content, see this GitHub issue
    In my opinion, Gridsome's approach would make a good candiate for a Nuxt 3 plugin!
  2. Flex Search
    Used for the search field in the top, this plugin was amazing. It created an index of all posts during the build process which was then provided as a static asset to the client, being searchable through the Flex Search API. There is currently no plugin for search, except for Algolia, which requires their server backend. But this should be solvable with the Nuxt Plugin API

Data layer does not support relationships

Overflowed.dev had several relationships between entities, that were either handled by Gridsome directly, or by the gridsome-recommender-plugin.

For example:

  1. Posts relate to Tags (Gridsome)
  2. Tags relate to Posts (Gridsome)
  3. Posts relate to Authors (Gridsome)
  4. Authors relate to Posts (Gridsome)
  5. Posts relate to Categories (Gridsome)
  6. Categories relate to Posts (Gridsome)
  7. Posts relate to relevant Book Advertisements (Gridsome Recommender)
  8. Posts relate to similar Posts (Gridsome Recommender)

There won't be any generated relationship, but you have to perform queries manually, in order to look up referenced entities.

For example:

While this created the bidirectional relations between posts and tags automatically in Gridsome and was letting you access it directly with GraphQL from post and tag pages:

    {
      use: '@gridsome/source-filesystem',
      options: {
        path: 'content/tags/**/*.md',
        typeName: 'Tag',
      }
    },
    {
      use: '@gridsome/source-filesystem',
      options: {
        path: 'content/posts/**/*.md',
        typeName: 'Post',
        refs: {
          tags: 'Tag'
        },
      },
    },

I now have to do this instead:

in post page to get the related tags

const post = await queryContent("blog")
  .where({slug: route.params.slug})
  .find().then(posts => posts[0]);


const tags = await queryContent('tags')
  .where({id: {$in: post.tags}})
  .find()

in tag page to get the related posts

const tag = await queryContent('tags')
  .where({slug: route.params.slug})
  .find().then(tags => tags[0])

const posts = await queryContent("blog")
  .where({tags: {$in: tag.id}})
  .find().then(posts => posts[0])

It's a bit more overhead here and there, but its certainly doable.

However, the recommender plugin requires to store the relations somewhere, which is impossible right now without introducing an extra content layer and further manual lookups.

Deployment

While overflowed.dev was previously a standalone repository, it has now become part of a monorepo, as already mentioned. I've made the decision to create one repo for all of my projects, because it will save a lot of time in the long run in my opinion, especially when it comes to setting up new projects, configuring CI etc. There might come a separate post about it at some point in the future.

Using Netlify as a host, I'm not not using their standard CI setup anymore, which meant Netlify is hooking into my GitHub repository, reacting to changes and deploying automatically.

The monorepo uses NX to build and deploy my applications via CLI only.

Netlify CI as CMS

To manage the content of this site, I'm using Netlify CMS. Usually it works like that:

A new post is created in your GitHub Repository as a pull request. Publishing it will lead to a merge onto the master, and Netlify detects it and triggers a rebuild and publish of the changed site.

With a monorepository it's a bit more difficult, and to be honest I have no clue what the best way would be. The problem is, the repo is built via GitHub Actions and NX and not with Netlify directly.The perfect solution would be the one, where the post publishing will trigger a dedicated GitHub Action that only builds and deploys one specific app, but this has yet to be figured out.