This week I decided to test if Firebase is suited to a personal project and to do it I needed to discovery how to host an API writen in Typescript with Nest framework and, UOU, it was not an easy task! Because of this, I decided to write down the success path and registry some problems that I had.
As a disclaimer, I know it's a hell of a specificy project and not so many people (if anyone) will build with a stack like this, but if you want you can speed this process with this repository, which I write while making this post as a starter repo.
Now, even if you won't build something with this stack you can get some tips that I will leave at the end of this post and may help you bootstrapting a project with a different stack.
Pre-requisites
You will need the following packages installed:
- Firebase Tools
- @google-cloud/functions-emulator
- WebPack
- Nodemon
TIP 1
I had seen a lot of people having problems in running the firebase emulator, which is used to run the project locally . I suggest to install in the following way:
If you already have firebase tools installed, uninstall it with
$ npm remove -g firebase-tools
.Then, install the package @google-cloud/functions-emulator with
$ npm install -g @google-cloud/firebase-functions
.Then, install firebase tools again:
$ npm install -g firebase-tools
.
Building project scaffold
Create your project folder and enter it
$ mkdir nestjs-firebase
$ cd nestjs-firebase
Use the Firebase CLI to create the initial project
$ firebase init
When you run this command, you will be promped with a configuration menu, then, follow the steps bellow:
- Mark the Functions and Hosting features with space and press enter.
- Set your Firebase project
- Enter
n
to not install npm dependencies yet - Press enter to use the default hosting directory as public.
- Enter
n
to disable single-app configuration
Then, open your favorite code editor. I will be using Visual Studio Code. You will see the following folder structure:
Now enter functions folder with $ cd functions
and update the package.json file inside it with the following.
{
"name": "nestjs-firebase",
"description": "NestJS app with Cloud Functions for Firebase",
"scripts": {
"build": "webpack",
"serve": "webpack && firebase serve --only functions,hosting",
"serve:dev": "nodemon -w *.ts --exec npm run serve",
"deploy": "webpack && firebase deploy --only functions,hosting"
},
"dependencies": {
"firebase-admin": "~4.2.1",
"firebase-functions": "^0.5.9",
"@nestjs/common": "*",
"@nestjs/core": "*",
"@nestjs/microservices": "*",
"@nestjs/testing": "*",
"@nestjs/websockets": "*",
"@types/express": "^4.0.37",
"express": "^4.15.4",
"redis": "^2.7.1",
"reflect-metadata": "^0.1.10",
"rxjs": "^5.4.0",
"typescript": "^2.3.2"
},
"devDependencies": {
"@types/node": "^7.0.5",
"ts-loader": "^2.3.3",
"webpack-node-externals": "^1.6.0"
},
"private": true
}
Then, run $ npm install
. After, create the src folder $ mkdir src
and delete index.js. Next, create webpack.config.js $ > webpack.config.js
with this.
// webpack.config.js
'use strict';
const nodeExternals = require('webpack-node-externals');
module.exports = {
entry: './src/server.ts',
output: {
filename: 'index.js', // <-- Important
libraryTarget: 'this' // <-- Important
},
target: 'node', // <-- Important
module: {
rules: [
{
test: /\.tsx?$/,
loader: 'ts-loader',
options: {
transpileOnly: true
}
}
]
},
resolve: {
extensions: [ '.ts', '.tsx', '.js' ]
},
externals: [nodeExternals()] // <-- Important
};
Now, create the this structure inside src folder, with the following content.
// server.ts
import * as functions from 'firebase-functions';
import * as express from 'express';
import { NestFactory } from '@nestjs/core';
import { ApplicationModule } from './modules/app.module';
const server = express();
const app = NestFactory.create(ApplicationModule, server);
app.init();
exports.api = functions.https.onRequest(server);
// app.module.ts
import { Module } from '@nestjs/common';
@Module({})
export class ApplicationModule {}
Finally, update firebase.json like this.
{
"hosting": {
"public": "public",
"rewrites": [{
"source": "**",
"function": "api"
}],
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
]
}
}
And, if you want to use the root url in your API, you need to delete or rename public/index.html.
TIP 2
If you are on Linux (I don't know if this happens in others OS), you can have problems when running $ firebase serve
. It is caused by permission problems and you can solve this in two ways:
- Run the serve and deploy commands from firebase with sudo.
- Change the owner of the firebase-tools folder in the global node_modules to your user.
I recommend the second way because, otherwise, when the nodemon restart the server it will not kill the previous process, so every restart the firebase tool will see the port 5000 as used and will start to listen in another process and this will pile up in your machine.
Conclusion
Now, you can develop your project and use run it with this commands:
- To build the app
$ npm run build
- To run the server
$ npm run serve
- To run the server for development
$ npm run serve:dev
- To deploy it to Firebase
$ npm run deploy
Wish I helped someone and, if you have questions or suggetions, please leave your comment.
Thank you!
Fonts
https://medium.com/netscape/firebase-cloud-functions-with-typescript-and-webpack-7781c882a05b
https://github.com/ultrasaurus/firebase-functions-typescript
Tips
Tip 1 - Firebase Emulator problem
Tip 2 - Firebase serve commands problem
Top comments (4)
great article, although server.ts is different that github source and needs to be updated.
One question, after deploy the api will respond to http requests, I was wondering how can I make a controller to respond to pub/sub or database changes (which firebase supports)
Another question at the end there will be 1 api end point, in matter of micro-srvices we should have fine grained separate apis, (maybe fined grained as modules), what is the best practice for that?
Try to use Graphql with nestjs & firebase, Graphql have sub/pub.
Amazing project. Thank you.
I am in desperate need of your help. I'm creating an app using ionic, can you assist me by showing me how to call data from firebase and do CRUD in an ionic 4 App.
Great !
Just the @google-cloud/functions-emulator has been deprecated use @google-cloud/functions-framework instead