Skip to content

Latest commit

 

History

History
350 lines (279 loc) · 10.3 KB

nodejs.md

File metadata and controls

350 lines (279 loc) · 10.3 KB

Node.js Function Developer's Guide

When creating a Node.js function using the func CLI, the project directory looks like a typical Node.js project. Both HTTP and Event functions have the same template structure.

❯ func create fn
Project path: /home/developer/projects/fn
Function name: fn
Runtime: node

❯ tree fn
fn
├── func.yaml
├── index.js
├── package.json
├── README.md
└── test
    ├── integration.js
    └── unit.js

Aside from the func.yaml file, this looks like the beginning of just about any Node.js project. For now, we will ignore the func.yaml file, and just say that it is a configuration file that is used when building your project. If you're really interested, check out the reference doc. To learn more about the CLI and the details for each supported command, see the CLI Commands document.

Running the function locally

To run a function, you'll first need to build it. This step creates an OCI container image that can be run locally on your computer, or on a Kubernetes cluster.

❯ func build

After the function has been built, it can be run locally.

❯ func run

Functions can be invoked with a simple HTTP request. You can test to see if the function is working by using your browser to visit http://localhost:8080. You can also access liveness and readiness endpoints at http://localhost:8080/health/liveness and http://localhost:8080/health/readiness. These two endpoints are used by Kubernetes to determine the health of your function. If everything is good, both of these will return OK.

Deploying the function to a cluster

To deploy your function to a Kubernetes cluster, use the deploy command.

❯ func deploy

You can get the URL for your deployed function with the info command.

❯ func info

Testing a function locally

Node.js functions can be tested locally on your computer. In the project there is a test folder which contains some simple unit and integration tests. To run these locally, you'll need to install the required dependencies. You do this as you would with any Node.js project.

❯ npm install

Once you have done this, you can run the provided tests with npm test. The default test framework for Node.js functions is tape. If you prefer another, that's no problem. Just remove the tape dependency from package.json and install a test framework more to your liking.

Function reference

Boson Node.js functions have very few restrictions. You can add any required dependencies in package.json, and you may include additional local JavaScript files. The only real requirements are that your project contain an index.js file which exports a single function. In this section, we will look in a little more detail at how Boson functions are invoked, and what APIs are available to you as a developer.

Invocation parameters

When using the func CLI to create a function project, you may choose to generate a project that responds to a CloudEvent or simple HTTP. CloudEvents in Knative are transported over HTTP as a POST request, so in many ways, the two types of functions are very much the same. They each will listen and respond to incoming HTTP events.

When an incoming request is received, your function will be invoked with a Context object as the first parameter. If the incoming request is a CloudEvent, it will be provided as the second parameter. For example, a CloudEvent is received which contains a JSON string such as this in its data property,

{
  "customerId": "0123456",
  "productId": "6543210"
}

So to log that string from your function, you might do this:

function handle(context, event) {
  console.log(JSON.stringify(event.data, null, 2));
}

Return Values

Functions may return any valid JavaScript type, or nothing at all. When a function returns nothing, and no failure is indicated, the caller will receive a 204 No Content" response. Functions may also return a CloudEvent, or a Message object in order to push events into the Knative eventing system. In this case, the developer is not required to understand or implement the CloudEvent messaging specification. Headers and other relevant information from the returned values are extracted and sent with the response.

Example

function handle(context, event) {
  return processCustomer(event.data)
}
function processCustomer(customer) {
  // process customer and return a new CloudEvent
  return new CloudEvent({
    source: 'customer.processor',
    type: 'customer.processed'
  })
}

Response headers

Functions may additionally set headers to be sent with the response by adding a headers property to the object being returned. These headers will be extracted and sent with the response to the caller.

Example

function processCustomer(customer) {
  // process customer and return custom headers
  // the response will be '204 No content'
  return { headers: { customerid: customer.id } };
}

Response codes

Developers may set the response code returned to the caller by adding a statusCode property to the response.

Example

function processCustomer(customer) {
  // process customer
  if (customer.restricted) {
    return { statusCode: 451 }
  }
}

This also works with Error objects thrown from the function.

Example

function processCustomer(customer) {
  // process customer
  if (customer.restricted) {
    const err = new Error(‘Unavailable for legal reasons’);
    err.statusCode = 451;
    throw err;
  }
}

The Context Object

Functions are invoked with a context object as the first parameter. This object provides access to the incoming request information. Developers can get the HTTP request method, any query strings sent with the request, the headers, the HTTP version, the request body. If the incoming request is a CloudEvent, the CloudEvent itself will also be found on the context object.

The Context object has several properties that may be accessed by the function developer.

log

Provides a logging object that can be used to write output to the cluster logs. The log adheres to the Pino logging API. For detailed documentation on this logging instance, see the Pino API documentation (https://getpino.io/#/docs/api).

Example

Function handle(context) {
  context.log.info(“Processing customer”);
}

Access the function via curl to invoke it.

curl http://example.com

The function will log

{"level":30,"time":1604511655265,"pid":3430203,"hostname":"localhost.localdomain","reqId":1,"msg":"Processing customer"}

Setting the log level

Developers can control the log level by setting the logLevel value in func.yaml The possible options for this adhere to the options available for pino, and may be one of 'fatal', 'error', 'warn', 'info', 'debug', 'trace' or 'silent'.

To temporarily override this value, set the environment variable FUNC_LOG_LEVEL to one of the options listed above. To set the environment variable, use the config command.

query

Returns the query string for the request, if any, as key value pairs. These attributes are also found on the context object itself.

Example

Function handle(context) {
  // Log the 'name' query parameter
  context.log.info(context.query.name);
  // Query parameters also are attached to the context
  context.log.info(context.name);
}

Access the function via curl to invoke it.

curl http://example.com?name=tiger

The function will log

{"level":30,"time":1604511655265,"pid":3430203,"hostname":"localhost.localdomain","reqId":1,"msg":"tiger"}
{"level":30,"time":1604511655265,"pid":3430203,"hostname":"localhost.localdomain","reqId":1,"msg":"tiger"}

body

Returns the request body if any. If the request body contains JSON, this will be parsed so that the attributes are directly available.

Example

Function handle(context) {
  // log the incoming request body's 'hello' parameter
  context.log.info(context.body.hello);
}

Access the function via curl to invoke it.

curl -X POST -d '{"hello": "world"}'  -H'Content-type: application/json' http://example.com

The function will log

{"level":30,"time":1604511655265,"pid":3430203,"hostname":"localhost.localdomain","reqId":1,"msg":"world"}

headers

Returns the HTTP request headers as an object.

Example

Function handle(context) {
  context.log.info(context.headers[custom-header]);
}

Access the function via curl to invoke it.

curl -H'x-custom-header: some-value’' http://example.com

The function will log

{"level":30,"time":1604511655265,"pid":3430203,"hostname":"localhost.localdomain","reqId":1,"msg":"some-value"}

method

Returns the HTTP request method as a string.

httpVersion

Returns the HTTP version as a string.

httpVersionMajor

Returns the HTTP major version number as a string.

httpVersionMinor

Returns the HTTP minor version number as a string.

httpVersionMinor

Returns the HTTP minor version number as a string.

Context Methods

There is a single method on the Context object which is a convenience function for returning a CloudEvent object. In Knative systems, if a function service is invoked by an event broker with a CloudEvent, the broker will examine the response. If the response is a CloudEvent, this event will then be handled by the broker just as with any other event it receives.

cloudEventResponse()

A function which accepts a data value and returns a CloudEvent.

Example

// Expects to receive a CloudEvent with customer data
function handle(context, event) {
  // process the customer
  const processed = processCustomer(event.data);
  return context.cloudEventResponse(processed);
}

Dependencies

Developers are not restricted to the dependencies provided in the template package.json file. Additional dependencies can be added as they would be in any other Node.js project.

Example

npm install --save opossum

When the project is built for deployment, these dependencies will be included in the resulting runtime container image.