Nuxt.js & Express API in TypeScript on Firebase Cloud Functions

Table of Contents

Nuxt.js for SSR+ Express for API + TypeScript + Firebase = Profit!

tl;dr: Here’s an example repo of Nuxt.js & Express using TypeScript, deployable to Firebase Cloud Functions and Hosting, set up for both development and production use.

Last time I shared how to deploy Nuxt.js on Firebase Cloud Functions. However some common use cases were not covered. What if I want to run a separate backend API server, instead of using Nuxt.js server middleware? Firebase Cloud Function supports TypeScript, what if I want to migrate to TypeScript?

In fact, in LikeCoin we are also trying to migrate our JavaScript codebase to TypeScript. Need not worry, this guide will get you covered.

Nuxt.js & Express API in TypeScript on Firebase Cloud Functions
Nuxt.js + TypeScript in action (also Vuetify)

Development Setup

Development Folder Structure

We will begin with a simple create-nuxt-app template withaxios module and TypeScript support. However, I will modify the folder structure a bit, moving all Nuxt.js source file into /src, and adding a /src/server and /src/server/api for placing our APIs. It should look like this.

Nuxt.js & Express API in TypeScript on Firebase Cloud Functions
Development environment folder structure

The reason for placing /server inside /src is that I want to run both the Nuxt builder and Express API server in a single npm command, allowing hot reload to trigger on file change in either directory.

Development Server

Inside /api are the server API routes that should be customized to your backend business logic. In this sample, they are just stub and will the actual code be omitted. Here I want to focus on /src/server/index.ts, which is the main Express instance of this development setup.

I have used new programmatic render syntax for Nuxt renderer/builder, loadNuxt and build. The advantage of using loadNuxt is that you won’t have to directly require() the nuxt.config.js config file. This solves the issue that if your config file uses import or other TypeScript syntax, it needed additional transpile to commonjs. The API routes are all included inside the /api/index.ts as an Express router and are served under /api.

By using this configuration, the development server will trigger a Nuxt build if run in a development environment. By running the server, both the /api endpoints and the Nuxt renderer with being served in an all-in-one configuration.

Extra tips:

Like I mentioned in my previous article, using the nuxt-start library for a lighter weight production start-up would also be a good idea for cloud function. However, to keep this example simple I will be using nuxt instead.

Since Nuxt middleware is not yet properly typed, in line 5 I used @ts-ignore, You can use other mitigation like declare module 'nuxt' . Also, there is an export {} at the end to ignore unwanted TypeScript warning.

Development Server TypeScript config

Speaking of TypeScript warning, we also want a separate tsconfig.json for this Express server. This is because the one provided in create-nuxt-app the template includes the DOM lib used for a browser environment. This will emit many unwanted warnings when running the Express server in node.js serve environment. The tsconfig.json for the server can be found here, and the main difference is to remove DOM from lib. However you can also set up other rules according to your needs, should you have some rules that differ in your Nuxt and Express environment.

Development package.json

The last part is how to run the above development server. One good developer experience for Nuxt is that when running, it provides hot-reload whenever we changed our code, and we definitely want to keep that trait in our new setup. We will be using ts-watch which is basically node-mon + tsc built together. This will provide hot-reload for the server-side code. When we are running the Nuxt builder and renderer middleware in development mode, they will provide the client-side hot reload just like running the default nuxt-ts command.

To archive this behavior, we will use these package.json scripts.

The major changes I have made are splitting up the build script into build:client and build:server. Also the dev script is changed to using tsc-watch. The build:server script is a standard one using tsc to compile Typescript into JavaScript. The dev script watches the server folder for any change in server code, rebuild the code into the output folder lib, then run the server code.

One point to notice is that any change in server code will reload the server instance and thus the Nuxt builder, which will trigger a full rebuild. So I recommend finishing the server API development first before drilling deep into the fancy frontend side of your application.

The above files are the main components of a dev setup. By now you should be able to run just run npm run dev and enjoy a front-backend hot reload server.

Production Setup

Production Folder Structure

After you complete the development of your Nuxt application. It is time to bring it to production. As mentioned we will be using Firebase hosting for static assets, and cloud functions for running SSR renderer. In addition, we will be serving our Express API server with a cloud function too.

Let us begin by adding two new folders /functions and /public_base. /functions is for housing our cloud functions, and /public_base is the folder for your static asset. We will later merge this folder with the static scripts generated by Nuxt builder together into the final /public folder to be put on hosting.

Nuxt.js & Express API in TypeScript on Firebase Cloud Functions
Production folder structure

Production Functions setup

Like in the dev environment, we will be putting our TypeScript source code into src and later build them into JavaScript in lib.

Inside the /functions/src, we have ssrapp which houses the Nuxt SSR renderer functions. We also have apiHttp which houses the API Express instance in index.ts and api which is a symlink to /src/server/api. A symlink was used so that we can guarantee the consistency of server code in the dev environment and production deployment without any extra synchronization overhead.

The difference between the index.ts used in dev and production environ for API is that Nuxt builder and renderer is not included thus nuxt not need to be loaded, and helmet was used to improve security. This separation of API and frontend rendering functions would be good for API performance. Also, it is useful if you have any other non-Nuxt.js frontend that you plan to share this set of API with.

Production Firebase config setup

Finally, the last step to wrap all things up ready for deployment is the firebase.json used by the Firebase command line. We can deploy the app with a simple firebase deploy command with the following configuration.

Like in my previous article, we will first build the production build for Nuxt.js frontend. In functions state we will build the TypeScript into JavaScript using tsc , then copy the nuxt build output for SSR function deployment. In hosting we also copy the static asset part of the nuxt output and put together with /public_base into /public for upload.

Also, we have defined redirection rules for hosting, anything under /api is directed to the API function, otherwise, the frontend SSR function will handle the rest. Since an exact match on hosting asset has higher priority then the rewrites, the static asset in/public will be served from CDN instead of cloud functions if the URL is an exact match.

As you might have noticed, using TypeScript actually made the functions build process simpler!

Deploy to production

The final step would be running the Firebase deploy command

firebase deploy

Then the Nuxt.js app and Express API server will be deployed to Firebase automagically.

The above instructions only cover the essential steps and concepts. For a complete config/code example, please visit my sample repo here. Also here is a sample site deployed to Firebase.


If you encounter an error like this

HTTP Error: 400, Billing account for project ‘xxxxxx’ is not found. Billing must be enabled for activation of service(s) ‘,’ to proceed.

You probably are on a free tier plan for Firebase and you are trying to deploy a Node.js 10 cloud function. Setting engines to Node.js 8 in cloud functions’ package.json should solve your issue.

"engines": { "node": "8"}

However, I recommend using an instance with Node.js version 10 if you do have a billing account setup, as version 8 is already deprecated.

#cloud functions express #nuxt express #nuxt express template #nuxt js typescript