Using SWC with Firebase Functions
In this article, we will consider using a compiler for js-code of cloud functions.
Problem
We create a project. Adding a Firebase function.
// index.js
export const helloWorld = https.onRequest(/** … */);
We’ll add a few more after a while.
// index.js
export const helloWorld = https.onRequest(/** … */);
export const lol = https.onRequest(/** … */);
export const pirojok = https.onRequest(/** … */);
After a while, more and more.
And since we write code for people, therefore, in addition to the code, it will have comments and documentation.
How does the increase in the amount of code affect the project?
The Firebase Functions project is a node module. AT index.js
all functions are exported.
At the first start of any function, all imported files are loaded and initialized, from all functions. Node.js is loading index.js
and all related code via require
and/or import
. This is a feature of node modules.
The initialization time of the node module is also affected by the expressions that are evaluated at that moment.
The following example will be evaluated each time the node module is initialized:
// constants.js
const ONE_MINUTES_IN_MS = 60 * 1000;
Next, the event is handled by the function.
Therefore, the more code and the more complex it is, the more time it takes to initialize each function.
Solution
If you reduce, simplify the code and load only the modules used for the running function, then you can reduce the time to initialize the functions.
How can you shorten the code? There are several options, among them minification and/or compilation.
Minification and compilation
Roughly speaking, minification will make variable names short, and compilation will remove unused code, pre-compute and simplify expressions, for example: 50 + 50
will turn into 100
.
Minification in action
Before:
const hello = sayHello("Pirojok");
console.log(hello, 40 + 2);
function sayHello(name) {
return `Hi, ${name}!`;
}
After:
"use strict";
const a = b("Pirojok");
console.log(a, 40 + 2);
function b(a) {
return `Hi, ${a}!`;
}
Compilation in action
Before:
const hello = sayHello("Pirojok");
console.log(hello, 40 + 2);
function sayHello(name) {
return `Hi, ${name}!`;
}
After:
"use strict";
var name;
const hello = `Hi, ${(name = "Pirojok")}!`;
console.log(hello, 42);
Compilation and minification in action
Before:
const hello = sayHello("Pirojok");
console.log(hello, 40 + 2);
function sayHello(name) {
return `Hi, ${name}!`;
}
After:
"use strict";
var a;
const b = `Hi, ${(a = "Pirojok")}!`;
console.log(b, 42);
How to load only used modules?
The main idea is to load modules at runtime, not function initialization.
All you need to do is put module loading in the body of the function handler.
Before:
import {https, logger} from "firebase-functions";
import {initializeApp} from "firebase-admin";
initializeApp();
export const helloWorld = https.onRequest((request, response) => {
logger.info("Hello logs!", {structuredData: true, computed: 50 + 50});
response.send("Hello from Firebase!");
});
After:
import {https, logger} from "firebase-functions";
let app_inited_by_helloWorld = false;
export const helloWorld = https.onRequest(async (request, response) => {
if (!app_inited_by_helloWorld) {
const {initializeApp} = await import("firebase-admin");
initializeApp();
app_inited_by_helloWorld = true;
}
logger.info("Hello logs!", {structuredData: true, computed: 50 + 50});
response.send("Hello from Firebase!");
});
global variable app_inited_by_helloWorld
needed to firebase-admin
the application was initialized, only once.
You can also place variables from imported modules in global variables.
More about this trick You can read the Firebase documentation.
Which minifier and compiler should I choose?
As a compiler and / or minifier, you can use the SWC project or any other.
Using SWC
I chose SWC, it can minify and compile code at the same time.
Installation
You can install SWC by instructions.
To create a config file .swcrc
and choose the settings you need, I recommend using playground project. You can get the configuration by clicking the button Edit as JSON.
For Firebase Functions, I recommend immediately setting Env Targets
= node 16
and remove jsc.target
in the final file.
playground adds an option to the config file "isModule"
= true
it will need to be removed if the compiler gives an error in your project.
As a bonus, you can configure the use of es-modules, all the old code using require
and the new one will live together just fine. An excellent opportunity for a smooth transition to es-modules.
Preparing the project code
In order for the code to be compiled and used, it must be somehow separated from each other. A great way is to divide by directories src
and lib
. If all the code is in the root of the package, then this will greatly complicate the process.
After preparation, you can set up scripts to compile the project.
AT package.json
you need to change the entry point and add scripts:
{
"main": "lib/index.js",
"scripts": {
"dev": "swc src -w -d lib",
"build": "swc src -d lib"
}
}
build
compiles code from a directory src
in lib
a dev
watches files for changes and recompiles them. dev
may be required for convenient work with the emulator, for it you will need to install an additional package.
Deploy automation
In order not to forget to compile the project before deployment, I recommend installing the predeploy script. This can be done in firebase.json
.
{
"functions": {
"predeploy": [
"npm --prefix \"$RESOURCE_DIR\" run lint",
"npm --prefix \"$RESOURCE_DIR\" run build"
]
}
}
Now when calling the command firebase deploy
the code will be pre-assembled.
Project example
Example already configured Firebase project can be viewed on GitHub.
Underwater rocks
If an error occurs during code execution, then it will be difficult to read its stack trace due to code modifications.
This problem is solved by generating source maps and module connection source-map-support.
useful links
Use TypeScript for Cloud Functions (Firebase Documentation)
Tips & tricks (Firebase Documentation)
The final
Congratulations! Now your project is compilable and you have taken a small step in its optimization.
To be one of the first to read my next article, you can subscribe to my channel in Telegram.
If you have any questions or comments, feel free to write them in the comments.
License
Licensed under Attribution 4.0 International (CC BY 4.0).