diff --git a/README.md b/README.md index dfd13e1..4ec5b16 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## FaaS Node.js Runtime Framework +# Node.js Function Framework [![Node.js CI](https://github.com/boson-project/faas-js-runtime/workflows/Node.js%20CI/badge.svg)](https://github.com/boson-project/faas-js-runtime/actions?query=workflow%3A%22Node.js+CI%22+branch%3Amaster) [![codecov](https://codecov.io/gh/boson-project/faas-js-runtime/branch/main/graph/badge.svg?token=Z72LKANFJI)](https://codecov.io/gh/boson-project/faas-js-runtime) @@ -6,18 +6,69 @@ This module provides a Node.js framework for executing a function that exists in a user-provided directory path as an `index.js` file. The directory may also contain an optional `package.json` file which can -be used to declare runtime dependencies for the function. +be used to declare runtime dependencies for the function. You can also +provide a path to an arbitrary JavaScript file instead of a directory +path, allowing you to execute a single file as a function. The function is loaded and then invoked for incoming HTTP requests at `localhost:8080`. The incoming request may be a [Cloud Event](https://github.com/cloudevents/sdk-javascript#readme.) or -just a simple HTTP GET request. In either case, the function will receive -a `Context` object instance that has a `cloudevent` property. For a raw HTTP -request, the incoming request is converted to a Cloud Event. +just a simple HTTP GET/POST request. The invoked user function can be +`async` but that is not required. -The invoked user function can be `async` but that is not required. +## Function Signatures -### CLI +This module supports two different function signatures: HTTP or CloudEvents. In the type definitions below, we use TypeScript to express interfaces and types, but this module is usable from JavaScript as well. + +### HTTP Functions + +The HTTP function signature is the simplest. It is invoked for every HTTP request that does not contain a CloudEvent. + +```typescript +interface HTTPFunction { + (context: Context, body?: IncomingBody): HTTPFunctionReturn; +} +``` + +Where the `IncomingBody` is either a string, a Buffer, a JavaScript object, or undefined, depending on what was supplied in the HTTP POST message body. The `HTTTPFunctionReturn` type is defined as: + +```typescript +type HTTPFunctionReturn = Promise | StructuredReturn | ResponseBody | void; +``` + +Where the `StructuredReturn` is a JavaScript object with the following properties: + +```typescript +interface StructuredReturn { + statusCode?: number; + headers?: Record; + body?: ResponseBody; +} +``` + +If the function returns a `StructuredReturn` object, then the `statusCode` and `headers` properties are used to construct the HTTP response. If the `body` property is present, it is used as the response body. If the function returns `void` or `undefined`, then the response body is empty. + +The `ResponseBody` is either a string, a JavaScript object, or a Buffer. JavaScript objects will be serialized as JSON. Buffers will be sent as binary data. + +### CloudEvent Functions + +CloudEvent functions are used in environments where the incoming HTTP request is a CloudEvent. The function signature is: + +```typescript +interface CloudEventFunction { + (context: Context, event: CloudEvent): CloudEventFunctionReturn; +} +``` + +Where the return type is defined as: + +```typescript +type CloudEventFunctionReturn = Promise | CloudEvent | HTTPFunctionReturn; +``` + +The function return type can be anything that a simple HTTP function can return or a CloudEvent. Whatever is returned, it will be sent back to the caller as a response. + +## CLI The easiest way to get started is to use the CLI. You can call it with the path to any JavaScript file which has a default export that @@ -37,6 +88,9 @@ function handle(context) { module.exports = handle; ``` +Additionally, if your JavaScript file exports more than a single function, +an exported `handle` function will be invoked. + You can expose this function as an HTTP endpoint at `localhost:8080` with the CLI. @@ -44,9 +98,9 @@ with the CLI. npx faas-js-runtime ./index.js ``` -### Usage +## Usage as a Module -In my current working directory, I have an `index.js` file like this. +In the current working directory, there is an `index.js` file like this. ```js const { start } = require('faas-js-runtime'); @@ -57,10 +111,11 @@ const options = { logLevel: 'info' } -// My function directory is in ./function-dir +// The function directory is in ./function-dir start(require(`${__dirname}/function-dir/`), server => { // The server is now listening on localhost:8080 - // and the function will be invoked for each HTTP + // and the function defined in `function-dir/index.js` + // will be invoked for each HTTP // request to this endpoint. console.log('Server listening'); @@ -74,7 +129,7 @@ like this. ```js module.exports = async function myFunction(context) { - const ret = 'This is a test function for Node.js FaaS. Success.'; + const ret = 'This is a test for Node.js functions. Success.'; return new Promise((resolve, reject) => { setTimeout(_ => { context.log.info('sending response to client') @@ -104,15 +159,10 @@ $ curl -X POST -d '{"hello": "world"}' \ -H'Ce-id: 1' \ -H'Ce-source: cloud-event-example' \ -H'Ce-type: dev.knative.example' \ - -H'Ce-specversion: 0.2' \ + -H'Ce-specversion: 1.0' \ http://localhost:8080 ``` ### Sample -You can see this in action, executing the function at `test/fixtures/async` -by running `node hack/run.js`. - -### Tests - -Just run `npm test`. +You can see this in action by running `node hack/run.js`.