Functions development

A PubNub Function is a JavaScript module with one default export: a handler function that PubNub calls when the configured trigger fires. This page explains how that handler is structured, what objects it receives, how Promises work in Functions, how to chain Functions, and what the Package/Revision/Deployment model means before your code goes live.

For trigger types and execution modes, see Function types. For per-execution limits, see API limits.

Handler signatures​

The handler signature varies by event type:

  • Most event types receive a request object:

    export default async (request) => {
    // Your business logic here. Await every async call.
    return request.ok();
    // return request.abort(); to fail execution
    };
  • On Request receives both request and response:

    export default async (request, response) => {
    // Your code here. Await every async call.
    return response.send();
    };
  • On Interval receives an event object instead of request:

    export default async (event) => {
    // Your code here. Await every async call.
    return event.ok();
    };

The handler must return a Promise, which an async handler always does. Execution is complete when that Promise settles. Only work you await before the handler returns is guaranteed to finish, and only errors from that work reach your handler. Don't rely on anything you start without awaiting it: whether it completes isn't guaranteed and can differ between Functions runtime versions. Synchronous triggers (Before Publish, Before Signal, On Request) hold the publisher or HTTP caller waiting until the Promise settles.

Handler objects​

Most trigger types pass a request object. On Request passes both request and response. On Interval and Subscribe on Interval pass an event object.

Use request.message to read or change a message in a synchronous trigger. Call request.abort() to prevent delivery. On Request uses response.send() to return an HTTP response.

For every field, method, and trigger-specific object contract, see Request and response objects.

Promises​

Functions use ES6 Promises for all async operations and support async/await. The promise object is available in every Function without a require() call.

Promise.all() runs multiple async calls in parallel and waits for all of them before proceeding. This example fetches a username from the KV store and an IP address from a web service at the same time, then updates the message with both results:

export default async (request) => {
const store = require('kvstore');
const xhr = require('xhr');

try {
const [fullName, ipResponse] = await Promise.all([
store.get('fullName'),
xhr.fetch('https://httpbin.org/ip'),
]);

request.message.fullNameResult = fullName;
request.message.ip = JSON.parse(ipResponse.body).origin;

return request.ok();
} catch (err) {
show all 19 lines

Await every async call before the handler returns. If you return while a call is still pending, your Function doesn't see whether that call succeeded and can't handle its error.

Chain Functions​

A Function can trigger other Functions by publishing to a channel that another Function runs on. Your Function waits only until PubNub accepts the publish. The downstream Function then runs on its own, and your Function doesn't wait for it to finish.

Await the publish before you return, and handle its error:

export default async (request) => {
const pubnub = require('pubnub');

request.message.hello = 'world!'; // augment hello = world

try {
const publishResponse = await pubnub.publish({
channel: 'hello_universe',
message: request.message,
});
console.log('Published to hello_universe with timetoken', publishResponse[2]);
} catch (err) {
console.error('Publish to hello_universe failed:', err);
}

show all 17 lines

This example still delivers the original message when the downstream publish fails. Return request.abort() from the catch block instead if the original message must not be delivered without it.

Don't skip the await to return sooner. A publish you don't await isn't guaranteed to complete, and nobody sees it fail. If a side effect shouldn't delay a synchronous trigger such as Before Publish, move it to an After Publish Function on the same channel instead.

Functions support chaining up to 3 hops, so a Function can trigger at most two more Functions downstream. Trigger each hop in the sequence using pubnub.publish() or pubnub.signal(). The system detects and blocks infinite loops, but design your logic to avoid circular chains rather than relying on that detection.

Group Functions you intend to chain in the same Package. Starting or stopping a Package affects all its Functions at once, so chained Functions that share a Package are easier to manage.

Package, Revision, and Deployment lifecycle​

Before a Function processes live traffic, it must be organized into a Package and activated through a Deployment:

  • Package. A Package groups related Functions. It is the unit you start and stop together. All Functions in a Package share a lifecycle: starting the Package starts all of them, stopping it stops all of them.
  • Revision. A Revision is a saved version of a Package. PubNub creates the first Revision automatically when you create the Package. Saving a code or event-type change creates a new Revision. Revisions are immutable once saved, so you can re-deploy an earlier one to roll back.
  • Deployment. A Deployment links a Revision to one or more keysets. Only a deployed Revision processes live traffic. You can keep one Revision running in production while testing a newer one in a separate deployment. Stopping a Deployment stops all Functions in the Package on those keysets. Messages still flow through PubNub, but On Request endpoints return 404 Not Found until a Revision is started again.

Code environment​

Functions run in a sandboxed JavaScript runtime. The runtime does not support:

  • Native Node.js modules (such as fs or http)
  • npm packages
  • The process global
  • WebSocket connections

Use the built-in modules for all external calls and storage. They load with require() and are not subject to the npm restriction. See Built-in modules for the full list.

Was this page useful?

Last updated on