Sitemap
Better Programming

Advice for programmers.

Migrating From Nuxt to Next.js With Google App Engine

Manato
6 min readJan 3, 2023

--

Photo by frank mckenna on Unsplash

This article will walk you through migrating from a Nuxt app to a Next.js using Google App Engine services. Here is the final code base on GitHub.

Dependencies

Here are the dependencies you’ll need:

  • next@13.1.1
  • nuxt@3.0.0

Overview

Suppose we have a monorepo app and structure a Nuxt app and a Next.js app in the repository.

./nuxt_to_next
├── package.json
├── packages
│ ├── next
│ └── nuxt
└── yarn.lock

The Nuxt project is the main application, and we will gradually migrate to the Next.js project on a page-by-page basis. The Nuxt application has four pages: Home, About, Works, and Blog:

The Nuxt application

We will migrate the About page from Nuxt to Next.js in this article.

Here is an overview of the implementation:

  • Deploy Nuxt3 app
  • Create an About page on Next.js
  • Deploy Next.js app
  • Override routing rules

Deploy Nuxt App

First, we will deploy the Nuxt app to the Google App Engine.

Let’s create a packages/nuxt/app.yamlin the Nuxt project. Here’s the code:

env: standard
runtime: nodejs16

instance_class: F1

automatic_scaling:
max_instances: 1
max_idle_instances: 1
min_instances: 0
min_idle_instances: 0

handlers:
- url: /.*
script: auto
secure: always

env_variables:
NUXT_HOST: "0.0.0.0"
NUXT_PORT: "8080"
NODE_ENV: "production"

During the Node.js deployment on Google App Engine, the runtime installs the dependencies using the npm install or yarn install then run the server by npm start or yarn start. To make it work on Nuxt3, we will need to update some scripts in the package.json like so:

{
"private": true,
"name": "package-nuxt",
"version": "1.0.0",
"scripts": {
- "build": "nuxt build",
+ "build": "yarn nuxt:prepare && nuxt build",
- "dev": "nuxt dev",
+ "dev": "yarn nuxt:prepare && PORT=3001 nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview",
+ "start": "HOST=0.0.0.0 PORT=8080 node .output/server/index.mjs",
- "postintall": "nuxi prepare",
+ "nuxt:prepare": "nuxi prepare",
},
}

Since Google App Engine installs the dependencies with NODE_ENV=production, we don’t want the postintall to be executed during the installation because the nuxi will not be included in the dependencies. To start the server, run the output/server/index.mjs which is generated by running the yarn build .

Let’s build it using the following command:

NODE_ENV=production yarn build

Then deploy it with this command:

gcloud app deploy

Create an About Page on Next.js

Next, we will create an About page on the Next.js project. We will create each link using a tag instead of NextLink because the Next.js project doesn't have other pages so far, we need to navigate them to the Nuxt project.

Let’s create a packages/next/pages/about.tsx. Here’s what that looks like:

import React from 'react'

export default function About() {
const title = 'About'

return (
<div>
<div className="text-sm font-medium text-center text-gray-500 border-b border-gray-200">
<ul className="flex flex-wrap -mb-px">
<li className="mr-2">
<a href="/" className="inline-block p-4 rounded-t-lg border-b-2 border-transparent hover:text-gray-600 hover:border-gray-300">Home</a>
</li>
<li className="mr-2">
<a href="about" className="inline-block p-4 rounded-t-lg border-b-2 border-transparent hover:text-gray-600 hover:border-gray-300">About</a>
</li>
<li className="mr-2">
<a href="/works" className="inline-block p-4 rounded-t-lg border-b-2 border-transparent hover:text-gray-600 hover:border-gray-300">Works</a>
</li>
<li className="mr-2">
<a href="/blog" className="inline-block p-4 rounded-t-lg border-b-2 border-transparent hover:text-gray-600 hover:border-gray-300">Blog</a>
</li>
</ul>
</div>
<div className="p-4">
<h1>Hello, {title} from Next.js</h1>
</div>
</div>
)
}

This page looks like this:

Next.js project

Deploy Next.js App

Next, let’s deploy the Next.js project.

Create a packages/next/app.yaml, and add this code:

env: standard
runtime: nodejs16

instance_class: F1

automatic_scaling:
max_instances: 1
max_idle_instances: 1
min_instances: 0
min_idle_instances: 0

handlers:
- url: /_next/static
static_dir: .next/static
- url: /(.*\.(gif|png|jpg|ico|txt|svg))$
static_files: public/\1
upload: public/.*\.(gif|png|jpg|ico|txt|svg)$
- url: /.*
script: auto
secure: always

env_variables:
HOST: "0.0.0.0"
PORT: "8080"
NODE_ENV: "production"

Then add the service property as follows:

env: standard
runtime: nodejs16

instance_class: F1
+ service: next

automatic_scaling:
max_instances: 1
max_idle_instances: 1
min_instances: 0
min_idle_instances: 0

handlers:
- url: /_next/static
static_dir: .next/static
- url: /(.*\.(gif|png|jpg|ico|txt|svg))$
static_files: public/\1
upload: public/.*\.(gif|png|jpg|ico|txt|svg)$
- url: /.*
script: auto
secure: always

env_variables:
HOST: "0.0.0.0"
PORT: "8080"
NODE_ENV: "production"

This will create a Service under the same application and an instance separated from the Nuxt project.

Services

By using services, you can deploy multiple services that behave like microservices. Each app engine has to include at least one service. If you skip the service field in the app.yaml, then the default service is automatically applied. In this case, the Nuxt project is the default service, and the Next.js project is the next service.

Go to the next package and deploy it with this code:

cd packages/next

gcloud app deploy

Override Routing Rules

Now that the Next.js project can handle the About page, we will add a new routing rule to navigate to Next.js from Nuxt.

Go to the Nuxt project, and add packages/nuxt/dispatch.yaml like this:

dispatch:
- url: "*/about"
service: next
- url: "*/_next/*"
service: next

The dispatch.yaml allows you to override routing rules to send incoming requests to a specific service based on a path or hostname in the URL. In this case, all requests to the about page will be redirected to the Next.js project without changing the URL. The Next.js app needs to serve the static files under the /_next folder.

Let’s deploy the dispatch file with this command:

gcloud app deploy dispatch.yaml

Once it's finished, we will change the link to the about page in the Nuxt project.

Open up a packages/nuxt/layouts/default.vue and change the link as follows:

<template>
<div>
<div class="text-sm font-medium text-center text-gray-500 border-b border-gray-200">
<ul class="flex flex-wrap -mb-px">
<li class="mr-2">
<NuxtLink to="/" class="inline-block p-4 rounded-t-lg border-b-2 border-transparent hover:text-gray-600 hover:border-gray-300">Home</NuxtLink>
</li>
<li class="mr-2">
- <NuxtLink to="/about" class="inline-block p-4 rounded-t-lg border-b-2 border-transparent hover:text-gray-600 hover:border-gray-300">About</NuxtLink>
+ <a href="/about" class="inline-block p-4 rounded-t-lg border-b-2 border-transparent hover:text-gray-600 hover:border-gray-300">About</a>
</li>
<li class="mr-2">
<NuxtLink to="/works" class="inline-block p-4 rounded-t-lg border-b-2 border-transparent hover:text-gray-600 hover:border-gray-300">Works</NuxtLink>
</li>
<li class="mr-2">
<NuxtLink to="/blog" class="inline-block p-4 rounded-t-lg border-b-2 border-transparent hover:text-gray-600 hover:border-gray-300">Blog</NuxtLink>
</li>
</ul>
</div>
<div class="p-4">
<slot />
</div>
</div>

</template>

Build it and deploy it.

NODE_ENV=production yarn build

gcloud app deploy

Once it’s deployed, it should work like so:

Deployed application

The About page is served through the Next.js project without changing the URL, while the other pages run in the Nuxt project.

Summary

Using services in Google App Engine allows you to share app engine features and communicate with another one through the dispatch file. Since the services behave like microservices, you can deploy multiple services independently and run them as a set of microservices.

In this article, we implemented Next.js from Nuxt, which can be applied to any framework, such as Remix and Astro. If you use Kubernetes on your microservices, you can achieve the same architecture using a service mesh like Istio.

Thanks for reading. Stay tuned for more.

--

--