Be a lazy developer: Firebase and Vue.js using TypeScript and beautiful hot-reloading everywhere
You will see how to set up a Firebase project which uses hosting and cloud functions written in Typescript. We add a VueJS project that will build our website and is hosted with Firebase hosting. After we set up these two parts, we are going to adjust some scripts to create a smooth development cycle with hot-reloading everywhere.
Create Firebase Project
Requirements: you need to install the Firebase CLI
First, go to the Firebase console and create a new project.
It is important to remember the Project-ID!
Now we have a Firebase project that can be used, let’s create the project locally. Create a folder which will be our project folder and then run firebase login
. If you use the CLI for the first time you are required to log in. After logging in, run firebase init
to initialize our project.
Since I want to use all Firebase features I simply select all of them …
To select the project you created earlier in the Firebase console, look for the project ID. You can accept all default values except the language for Cloud Functions, as we want to use Typescript. The hosting option for single page apps should also be set — we want to get all traffic to index.html
, because the app we’ll build later with Vue.js will be a Single Page App (SPA).
In our project folder we now have a fully initialized Firebase project. The important parts are:
- the
functions
directory —functions/src/index.ts
is the entry point for Cloud Functions. - the
public
directory — it contains the static hosting files. This is the place where we will later place our Vue.js App!
Run the Firebase project locally
Let’s see if Cloud Functions and hosting are working locally. First we need to uncomment some code inside /functions/src/index.ts
to have a Cloud Function to test.
Make your index.ts
look like this:
Now we have a function in the TypeScript source file in our source folder. The development server cannot use it directly because the it needs JavaScript files in the lib
directory. To create these JavaScript-Files, we need to compile the TypeScript sources using tsc
(TypeScript compiler). The Firebase CLI already created the necessary scripts in package.json
(in the functions
directory). To get the Cloud Function to work we need to run the following commands:
npm install
in thefunctions
foldernpm run build
in thefunctions
folder (this usestsc
and compiles the TypeScript sources fromindex.ts
into JavaScript in thelib
directory)firebase serve
to start the development server
After you run these commands, you can call the Cloud Function in a browser and see the “Hello from Firebase!” text in your browser.
Hosting also works as expected, just call the hosting server on http://localhost:5000
Add hot reloading of Cloud Functions
Currently, when you update the source code in the cloud function, the change is not reflected in the development server, because you only change the TypeScript source code and the development server only listens to changes to the compiled JavaScript files. To simplify development we want to make sure any changes made to the TypeScript source files are reflected immediately.
Beside the running development server(command: firebase serve
) we need a script that watches the source folder and compiles the TypeScript sources into JavaScript in the lib
directory on demand.
The development itself supports hot-reloading out of the box, but registers the functions during startup. So you need to restart the development server whenever you add a new function. You don’t need a development server restart for changes in existing functions!
To have a central script we’ll add a package.json
in the main directory — we already have one in the functions
directory, but that one is only for our functions. To create a fresh package.json
, just runnpm init
in the root folder.
You can add more details about your project here or just go with the defaults as I did.
Now let’s create one command that runs the development server and compiles our TypeScript sources on demand.
To be precise: We want to run the TypeScript compiler once before anything else happens to generate the JavaScript files needed by the development server at startup. Then we want the development server to be started and parallel a TypeScript compiler in watch mode.
Let’s install the necessary dependencies first. Beside TypeScript, we need the concurrently
package which enables us to execute things in parallel. To install these packages run:
npm install typescript concurrently --save-dev
Add the following to the script section of the package.json
in the root directory:
...
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"serve": "tsc --project ./functions && concurrently --kill-others 'firebase serve' 'tsc --project ./functions --watch'"
},
...
If you now execute npm run serve
in the root folder, the TypeScript compiler compiles the sources in the functions/src
folder into the functions/lib
folder once. If that succeeds (&&
), concurrently
starts the development server (firebase serve
) and the TypeScript compiler again, but this time in watch mode. The kill-others
option in concurrently is needed to kill all processes if one of them dies, everything else would cause unexpected behaviour.
Let’s run npm run serve
in the root folder and check that everything works as expected…
As you can see, no restart is required. We can update the Cloud Function and the new function is executed immediately. That’s the way we want to work!
Let’s switch gears now and add a Vue.js Project!
Add a Vue.js project
Vue.js provides the Vue CLI, which is the preferred way to generate a new Vue.js project. It includes many best practices. In this section we will use the CLI to create a Vue.js project in a subfolder and modify our serve script to create a build of the vue
project in our Firebase public folder, which is served locally by the Firebase development server. It also enabling hot reloading for the vue
part!
Requirements: you need to install the Vue-CLI
npm install -g @vue/cli
Let’s first create a Vue.js project in a subfolder called frontend
. The CLI already provides TypeScript based presets, but I prefer to select all features manually, because I like SASS over stylus and want my app to be a Progressive Webapp (PWA).
vue create frontend
After some time you should see something like this:
Let’s try to serve the vue project locally using the Vue-CLI.
cd frontend
npm run serve
That should end up like this:
When you visit http://localhost:8080
you will see this page:
Our Vue.js project works, but is completely served by the Vue CLI and not using our Firebase development server. Let’s change that!
Change your serve script in package.json
in the root folder like the following:
"serve": "tsc --project ./functions && rm -rf ./public/* && concurrently --kill-others 'firebase serve' 'tsc —-project ./functions --watch' 'cd frontend && ./node_modules/.bin/vue-cli-service build --no-clean --dest ../public --watch'"
What happens in the new bold parts here? We want to have a clean start at least when we restart our script. For that reason we clean the public
folder completely during startup (rm -rf ./public/*
). We add a call to the Vue CLI to the list of commands that all executed by concurrently to build the vue
project. We tell the Vue CLI to place the files in the public directory and running in watch mode. That will execute a build when one of the vue
projects source files is changed. The no-clean
option prevents a clean up of the public folder by the Vue CLI, which would normally delete the folder before every build.
Now run our serve
script and see how it works.
That works great so far but we can still improve it a little bit more. Currently we need to wait until the build is done to hit the reload button. We can use Browersync to watch for changes in our public directory and trigger a page reload if something has changed.
First we need to add the Browsersync dependency…
npm install browser-sync --save-dev
And then add an additional entry to the list of concurrently
commands in our serve script:
"serve": "tsc --project ./functions && rm -rf ./public/* && concurrently --kill-others 'firebase serve' 'tsc --project ./functions --watch' 'cd frontend && ./node_modules/.bin/vue-cli-service build --no-clean --dest ../public --watch' 'sleep 15 && browser-sync start --proxy \"localhost:5000\" --files \"public/*\"'"
What happens in the new bold parts? First we wait on startup a little bit until the Vue project build is done, then we startup Browsersync in proxy mode, which means that browsersync operates on top of our development server.
Let’s see this in action:
BrowserSync opens the browser for you and reloads it whenever you make any change to the vue
project sources, as well as any changes in the SASS sources. Please note that the browser opened by Browsersync points to http://localhost:3000
, our development server is running on http://localhost:5000
and our Cloud Functions are available on http://localhost:5001
.
Summary
In this article, I showed you how to integrate a Vue.js project into a Firebase project using TypeScript and hot reloading everywhere. I hope you like this solution! If you have any comments or run into issues, feel free to leave feedback in the comments!