All Products
Search
Document Center

Node.js runtime

Last Updated: Aug 22, 2019

Currently, Function Compute supports Node.js 6.10 with runtime version 6 and Node.js 8.9.0 with runtime version 8. This topic describes the following features of the Node.js runtime environments:

Use the logging module

You can specify a Logstore when you create a service. The Logstore collects the data that the function outputs to print by using the console.log file. For more information about function logs, see Logs.

  1. exports.handler = function (event, context, callback) {
  2. console.info(null, 'hello world');
  3. callback(null, 'hello world');
  4. };

The preceding code outputs the following log content:

  1. message:2017-07-05T05:13:35.920Z a72df088-f738-cee3-e0fe-323ad89118e5 [INFO] hello world

You can run the console.warn and console.error commands to package WARN and ERROR logs, respectively.

You can change log levels by using the setLogLevel handler. The following log levels are listed in descending order in terms of priority:

  • error: indicates that the corresponding handler is console.error.
  • warn: indicates that the corresponding handler is console.warn.
  • info: indicates that the corresponding handler is console.info.
  • verbose: indicates that the corresponding handler is console.log.
  • debug: indicates that the corresponding handler is console.debug.
  1. 'use strict';
  2. exports.handler = function(evt, ctx, cb) {
  3. console.setLogLevel("error");
  4. console.error("console error 1");
  5. console.info("console info 1");
  6. console.warn("console warn 1");
  7. console.log("console log 1");
  8. console.setLogLevel("warn");
  9. console.error("console error 2");
  10. console.info("console info 2");
  11. console.warn("console warn 2");
  12. console.log("console log 2");
  13. console.setLogLevel("info");
  14. cb(null, evt);
  15. };

The preceding code outputs the following log content:

  1. message:2017-07-05T05:13:35.920Z a72df088-f738-cee3-e0fe-323ad89118e5 [ERROR] console error 1
  2. message:2017-07-05T05:13:35.920Z a72df088-f738-cee3-e0fe-323ad89118e5 [ERROR] console error 2
  3. message:2017-07-05T05:13:35.920Z a72df088-f738-cee3-e0fe-323ad89118e5 [WARN] console warn 2

Use built-in modules

In addition to the standard Node.js modules, the Node.js runtime environments for Function Compute provide the following common modules.

Module Version Description Link
co 4.6.0 Throttling https://github.com/tj/co
gm 1.23.0 GraphicsMagick https://github.com/aheckmann/gm
ali-oss 4.10.1 OSS SDK https://github.com/ali-sdk/ali-oss
ali-mns 2.6.5 MNS SDK https://github.com/InCar/ali-mns
tablestore 4.2.0 Table Store SDK https://github.com/aliyun/aliyun-tablestore-nodejs-sdk
aliyun-sdk 1.10.12 Alibaba Cloud SDK https://github.com/aliyun-UED/aliyun-sdk-js
@alicloud/fc2 2.1.0 Function Compute SDK https://github.com/aliyun/fc-nodejs-sdk
opencv 6.0.0 OpenCV https://github.com/peterbraden/node-opencv
body 5.1.0 HTTP body resolution library https://github.com/Raynos/body
raw-body 2.3.2 HTTP raw body resolution library https://github.com/stream-utils/raw-body

For example, the function that uses GraphicsMagick to flip an image is as follows:

  1. var gm = require('gm').subClass({ imageMagick: true });
  2. exports.handler = function (event, context, callback) {
  3. gm(event)
  4. .flip()
  5. .toBuffer('PNG', function (err, buffer) {
  6. if (err) return callback(err);
  7. callback(null, buffer);
  8. });
  9. };

Note: The preceding function directly uses event as the binary data of the image, and directly returns the generated image as the binary data.

Use custom modules

If you need to use a custom module, package the module and the code together. The following steps describe how to use Fun to add a mysql module to access MySQL. For the installation guide of Fun, see here.

  1. Create a folder to store code and dependent modules:

    1. mkdir /tmp/code
  2. Create a new code file, such as /tmp/code/index.js, and use the mysql module in the code:

    1. var mysql = require('mysql');
    2. var connection;
    3. // exports.initializer: initializer function
    4. exports.initializer = function (context, callback) {
    5. connection = mysql.createConnection({
    6. host: 'localhost',
    7. user: 'me',
    8. password: 'secret',
    9. database: 'my_db'
    10. });
    11. connection.connect();
    12. }
    13. exports.handler = function (event, context, callback) {
    14. connection.query('SELECT 1 + 1 AS solution', function (error, results, fields) {
    15. if (error) return callback(error);
    16. console.log('The solution is: ', results[0].solution);
    17. callback(null, results[0].solution);
    18. });
    19. connection.end();
    20. };
    • Why is the operation of database connection in initializer?

      The initializer runs only once within the lifecycle of a Function Compute instance. Establishing a database connection belongs to a cold start overhead at the application layer. To optimize the function performance and avoid establishing the database connection repeatedly due to multiple subsequent requests, we put this operation under the initializer to maintain the database connection during the lifecycle of a Function Compute instance.

    • Why is using initializer better than using a global variable for the initialization?

      Within the lifecycle of a Function Compute instance, the initializer runs only once before the request to call the function. However, using the global variable affects the performance during the cold start on some occasions, such as calling a new Function Compute instance, upgrading code, periodic recycling for Function Compute, or updating containers.

  3. Install dependencies in the /tmp/code folder.

    1. cd /tmp/code
    2. npm install mysql
  4. Create a template.yml file, such as /tmp/code/template.yml, and enter the content as follows:

    1. ROSTemplateFormatVersion: '2015-09-01'
    2. Transform: 'Aliyun::Serverless-2018-04-03'
    3. Resources:
    4. FunDemo:
    5. Type: 'Aliyun::Serverless::Service'
    6. nodejsdemo:
    7. Type: 'Aliyun::Serverless::Function'
    8. Properties:
    9. Handler: index.handler
    10. Runtime: nodejs8
    11. Initializer: index.initializer
    12. CodeUri: '. /'

    The description of the template.yml file is as follows: Declare a FunDemo service and a nodejsdemo function for the FunDemo service. Then, specify the function runtime environment as nodejs8, and configure the function handler as index.handler and the initializer as index.initializer. In addition, specify the CodeUri property as the current folder. Fun packages and uploads the folder or files that the CodeUri property specifies during the deployment. For more configuration rules, see here.

    After the installation, the content of the /tmp/code folder is as follows:

    1. ls -l /tmp/code
    2. -rw-r--r-- 1 tan wheel 522B Jun 18 14:50 index.js
    3. drwxr-xr-x 13 tan wheel 416B Jun 18 14:49 node_modules
    4. -rw-r--r-- 1 tan wheel 297B Jun 18 14:58 template.yml
  5. Use Fun to deploy:

    1. fun deploy

    Related logs appear as follows when you successfully run the fun deploy command:

    1. using region: cn-hangzhou
    2. using accountId: ***********3557
    3. using accessKeyId: ***********r3Ra
    4. using timeout: 300
    5. Waiting for service FunDemo to be deployed...
    6. Waiting for function nodejsdemo to be deployed...
    7. Waiting for packaging function nodejs code...
    8. package function nodejs code done
    9. function nodejsdemo deploy success
    10. service FunDemo deploy success

    Log on to the console. You can see the related services and functions that are successfully created. The execution of functions can be triggered to return correct results.

    Here is a brief description about what the fun deploy command does: When you use the fun deploy command, it creates a FunDemo service according to the configuration of the template.yml file. In addition, it creates a nodejsdemo function with a runtime environment nodejs8. Then, the nodejsdemo function packages and uploads the current folder that the CodeUri property specifies to Function Compute as the function code. The packaged code contains the node_modules folder that includes all the installed packages, so Function Compute can directly use these packages.

Run external commands

The functions may use some tools that are not written in Node.js, such as shell scripts or executable files that are compiled in C++ or Go. You can still package these tools and the function code together and use them by running external commands in the functions. The following example demonstrates how to run a shell script:

  1. var exec = require('child_process');
  2. exports.handler = function(event, context, callback) {
  3. var scriptPath = process.env['FC_FUNC_CODE_PATH'] + '/script.sh';
  4. exec.exec('bash '+scriptPath, {}, function(err, stdout, stderr) {
  5. if (err) return callback(err);
  6. console.log(stdout, stderr);
  7. callback(null, stdout);
  8. });
  9. };

Executable files that are compiled in C, C++, or Go must be compatible with the runtime environment of Function Compute. The Node.js runtime environment of Function Compute is:

    • Linux kernel version: Linux 4.4.24-2.al7.x86_64
    • Docker base image: Docker Pull Node:6.10

Understand callback

Node.js uses an asynchronous programming model. Therefore, the function must call callback to respond. If no callback is used in the function, the system prompts that the function execution has timed out.

1. Make sure that callback is called

If a function does not call callback, the system assumes that the function is still in process, then waits for the result of the function until the operation times out. For example, when the following function is called, a time-out error is returned:

  1. exports.handler = function(event, context, callback) {
  2. console.log('hello world');
  3. };

Result of the call:

  1. {"errorMessage":"Function timed out after 3 seconds"}

2. The function execution ends after callback is called

The function execution ends after callback is called. If callback is called multiple times, only the result of the first callback takes effect. Make sure that all tasks are completed before calling callback. Otherwise, the remaining tasks will not run. For example, the following function returns “hello world” but does not output “message”:

  1. exports.handler = function(event, context, callback) {
  2. callback(null, 'hello world');
  3. callback(null, 'done');
  4. setTimeout(function() {
  5. console.log('message');
  6. }, 1000);
  7. };

Handle exceptions

After you run a function in the Node.js runtime environment, you may receive the following types of errors of which the error types are in the returned HTTP header field X-Fc-Error-Type:

  1. HandledInvocationError: indicates the error returned by the first callback parameter.
  2. UnhandledInvocationError: indicates other errors, such as an uncaptured exception, a timeout error, or an out-of-memory (OOM) error.

Example 1: Return HandledInvocationError

  1. exports.handler = function(event, context, callback) {
  2. callback(new Error('oops'));
  3. };

When callback is called, the following response is returned:

  1. {
  2. "errorMessage": "oops",
  3. "errorType": "Error",
  4. "stackTrace": [
  5. "at exports.handler (/code/index.js:2:12)"
  6. ]
  7. }

Example 2: Return UnhandledInvocationError

  1. exports.handler = function(event, context, callback) {
  2. throw new Error('oops');
  3. };

When callback is called, the following response is returned:

  1. {"errorMessage":"Process exited unexpectedly before completing request"}

For more information about errors in Function Compute, see Error types.