Git Product home page Git Product logo

fdk-node's Introduction

Fn Function Developer Kit for Node.js

This Function Developer Kit makes it easy to deploy Node.js functions to Fn. It currently supports default (cold) and hot functions using the JSON format.

Creating a Node Function

Writing a Node.js function is simply a matter of writing a handler function that you pass to the FDK to invoke each time your function is called.

Start by creating a node function with fn init and installing the FDK:

fn init --runtime node nodefunc
cd nodefunc

This creates a simple hello world function in func.js:

const fdk=require('@fnproject/fdk');

fdk.handle(function(input){
  let name = 'World';
  if (input.name) {
    name = input.name;
  }
  return {'message': 'Hello ' + name}
})

The handler function takes the string input that is sent to the function and returns a response string. Using the FDK you don't have to worry about reading input from standard input and writing to standard output to return your response. The FDK let's you focus on your function logic and not the mechanics.

Now run it!

fn deploy --local --app fdkdemo 
fn invoke fdkdemo nodefunc 

Now you have a basic running Node function that you can modify and add what you want.

echo -n '{"name": "Tom"}' | fn invoke fdkdemo nodefunc

You should see the result

{"message": "Hello Tom"}

Function Context

Function invocation context details are available through an optional function argument. To receive a context object, simply add a second argument to your handler function. in the following example the callID is obtained from the context and included in the response message:

const fdk=require('@fnproject/fdk');

fdk.handle(function(input, ctx){
  let name = 'World';
  if (input) {
    name = input;
  }
  return 'Hello ' + name + ' from Node call ' + ctx.callID + '!';
})

The context contains other context information about the request such as:

  • ctx.config : An Object containing function config variables (from the environment ) (read only)
  • ctx.headers : an object containing input headers for the event as lists of strings (read only)
  • ctx.deadline : a Date object indicating when the function call must be processed by
  • ctx.callID : The call ID of the current call
  • ctx.fnID : The Function ID of the current function
  • ctx.memory : Amount of ram in MB allocated to this function
  • ctx.contentType : The incoming request content type (if set, otherwise null)
  • ctx.setResponseHeader(key,values...) : Sets a response header to one or more values
  • ctx.addResponseHeader(key,values...) : Appends values to an existing response header
  • ctx.responseContentType set/read the response content type of the function (read/write)
  • ctx.httpGateway The HTTP Gateway context for this function (if set) see HTTPGatewayContext below

Asynchronous function responses

You return an asynchronous response from a function by returning a Javascript Promise from the function body:

const fdk=require('@fnproject/fdk');

fdk.handle(function(input, ctx){
  return new Promise((resolve,reject)=>{
     setTimeout(()=>resolve("Hello"),1000);
  });
})

You can also use async/await calling conventions in functions.

Handling non-json input and output

By default the FDK will try and convert input into a JSON object, or fall back to its string format otherwise.

Likewise by default the output of a function will be treated as a JSON object and converted using JSON.stringify.

To change the handling of the input you can add an additional options parameter to fdk.handle that specifies the input handling strategy:

function myfunction(input,ctx){}

fdk.handle(myfunction, {inputMode: 'string'})

valid input modes are:

  • json (the default) attempts to parse the input as json or falls back to raw (possibly binary) string value otherwise
  • string always treats input as a string
  • buffer reads input into a Buffer object and passes this to your function

To change the output handling of your function from the default you should wrap the result value using a response decorator:

function myfunction(input,ctx){
   return fdk.rawResult("Some string")
}

fdk.handle(myfunction)

the available decorators are:

  • rawResult({string|Buffer}) passes the result directly to the response - the value can be a string or a buffer - this will not encode quotes on string objects
  • streamResult({ReadableStream}) pipes the contents of a ReadableStream into the output - this allows processing of data from files or HTTP responses

Using HTTP headers and setting HTTP status codes

You can read http headers passed into a function invocation using ctx.protocol.header(key), this returns the first header value of the header matching key (after canonicalization) and ctx.protocol.headers which returns an object containing all headers.

const fdk=require('@fnproject/fdk');

fdk.handle(function(input, ctx){
  
  let hctx = ctx.httpGateway
  console.log("Request URL" , hctx.requestURL)
  
  console.log("Authorization header:" , hctx.getHeader("Authorization"))
  console.log( hctx.headers) // prints e.g. { "Content-Type": ["application/json"],"Accept":["application/json","text/plain"] } 
})

Outbound headers and the HTTP status code can be modified in a similar way:

const fdk=require('@fnproject/fdk');

fdk.handle(function(input, ctx){
    let hctx = ctx.httpGateway

   hctx.setResponseHeader("Location","http://example.com")
   hctx.statusCode = 302
})

The HTTPGatewayContext object has a similar interface to Context but accesses only the HTTP headers of the function:

  • hctx.requestURL : Get the http request URL of the function as received by the gateway (null if not set)
  • hctx.method : Get the HTTP request method used to invoke the gateway
  • hctx.headers : Get the HTTP headers of the incoming request (read-only)
  • hctx.statusCode : Set the the HTTP status code of the HTTP resposne & hctx.setResponseHeader(key,values..), hctx.addResponseHeader(key,values) Set/add response headers

Fn and Node.js Dependencies

Fn handles Node.js dependencies in the following way:

  • If a package.json is present without a node_modules directory, an Fn build runs an npm install within the build process and installs your dependencies.
  • If the node_modules is present, Fn assumes you have provided the dependencies yourself and no installation is performed.

fdk-node's People

Contributors

denismakogon avatar hhexo avatar isaaclipszyc avatar metamemelord avatar michael-w-williams avatar mohitmagal avatar nishalad95 avatar preeti-misra avatar rdallman avatar roneetshaw avatar shaunsmith avatar skinowski avatar templecloud avatar treeder avatar zootalures avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fdk-node's Issues

console.log pollutes stdout

console.log seems to be directed to stdout by default:

in a JSON function :

const fdk = require('@fnproject/fdk')

fdk.handle(function (input, ctx) {
	console.log("Running function")
	return "OK"
})

causes a JSON parse error in fn:

{"error":{"message":"invalid json response from function err: invalid character 'R' looking for beginning of value"}}

console is assignable so fix is to replace it with a monkey-patched version that sends logs to stderr only .

config values in YAML are not where expected in context

In my func.yaml file I have this

name: crawl
version: 0.0.4
runtime: node
entrypoint: node func.js
type: async
format: json
timeout: 1800
config:
  ALLOWED_HTTP_METHOD: POST
  FN_SERVER_HOST:  http://someurl.com
  FN_TEST: test value
idle_timeout: 3600

In the context I receive when running
ALLOWED_HTTP_METHOD is under ctx.config.properties.ALLOWED_HTTP_METHOD
which is reasonable.
but the other two config values I added show up under
ctx.payload.server_host
ctx.payload.test

different path, lower case and removal of FN_
is there a reason this is so obfuscated?

[security] vulnerabilities being found in dependencies by npm

from recent fdk builds:

added 241 packages from 175 contributors and audited 657 packages in 2.746s
found 4 vulnerabilities (2 moderate, 2 high)
  run `npm audit fix` to fix them, or `npm audit` for details

not a nodejs expert, but i guess this should be fixed.

ctx.headers returns values are array inside array

In the node.js fn handler, the ctx parameter headers property returns:

get headers () {
let headers = {}
for (let k in this._headers) {
if (this._headers.hasOwnProperty(k)) {
headers[k] = new Array(this._headers[k])
}
}
return headers
}
However _headers[k] is already an array, so to get the value for a header you need:

ctx.headers["Content-type"][0]0] which does not seem right.

FDK doesn't handle undefined response

If I return undefined on the fdk it hangs until the function timeout.
I have a function that if there's an error, it just console.error('error message') and then return. When I try to invoke fn function it hangs until it timeout.

fdk.handle(async event => {
    const result = await someAwaitFunction()
    if(result.error) {
       console.error(result.error)
       return
    }

   ....
})

fdk: 0.0.15
fn: 0.5.96

stdin keep growing with input from all the POST

Hi,

I have a simple func.js

var fdk=require('@fnproject/fdk');
  fdk.handle( function(input) {
  return input
})

the function continue to append the input data from previous call until the docker is running (I use http-stream )

postak@mbp13 curl -X POST -d @payload.json 'http://127.0.0.1:8080/t/myapp/myfunc
"ciao"postak@mbp13 curl -X POST -d @payload.json 'http://127.0.0.1:8080/t/myapp/myfunc
"ciaociao"postak@mbp13 curl -X POST -d @payload.json 'http://127.0.0.1:8080/t/myapp/myfunc
"ciaociaociao"postak@mbp13 curl -X POST -d @payload.json 'http://127.0.0.1:8080/t/myapp/myfunc
"ciaociaociaociao"postak@mbp13 curl -X POST -d @payload.json 'http://127.0.0.1:8080/t/myapp/myfunc

this is my func.yaml

schema_version: 20180708
name: myfunc
version: 0.0.66
runtime: node
entrypoint: node func.js
format: http-stream
triggers:
- name: myfunc
  type: http
  source: /myfunc

[object Object] returned by deployed function when invoked via curl

The current boilerplate generated Node.js example returns [object Object] instead of the expected JSON response when called via curl/direct http. Console output:

$ curl http://localhost:8080/r/demo/hello
[object Object]

I tried explicitly specifying Content-Type: application/json but there was no change. However when I called the function with fn call the function returns the expected JSON:

$ fn call demo hello
{"message":"Hello World"}

The output is essentially the same whether input is provided or not.

Client version: 0.4.66
Server version: 0.3.410
FDK version (in boilerplate package.json): 0.x

Possible fdk-node on K8S issue?

Hi there,

I'm running fn on a K8S cluster and can run through the Go instructions just fine:

https://fnproject.io/tutorials/Introduction/

But when following their Node equivalent:

https://fnproject.io/tutorials/node/intro/

The fn invoke nodeapp nodefn sends back a 500 error and the fn list calls comes back empty. Here's a full set of commands I'm running with results:

PETERCJO-M-63N9:fn petercjo$ fn init --runtime node --trigger http nodefn
Creating function at: /nodefn
Function boilerplate generated.
func.yaml created.
PETERCJO-M-63N9:fn petercjo$ cd nodefn
PETERCJO-M-63N9:nodefn petercjo$ ll
total 24
-rw-r--r--  1 petercjo  staff  176 Oct  2 17:24 func.js
-rw-r--r--  1 petercjo  staff  184 Oct  2 17:24 func.yaml
-rw-r--r--  1 petercjo  staff  193 Oct  2 17:24 package.json
PETERCJO-M-63N9:nodefn petercjo$ fn --verbose deploy --app nodeapp
Deploying nodefn to app: nodeapp
Bumped to version 0.0.2
Building image petecj2/nodefn:0.0.2 
FN_REGISTRY:  petecj2
Current Context:  No context currently in use.
Sending build context to Docker daemon   5.12kB
Step 1/9 : FROM fnproject/node:dev as build-stage
 ---> 016382f39a51
Step 2/9 : WORKDIR /function
 ---> Using cache
 ---> 8c66c3b8b760
Step 3/9 : ADD package.json /function/
 ---> Using cache
 ---> a8e6bba754a8
Step 4/9 : RUN npm install
 ---> Using cache
 ---> 366077317d69
Step 5/9 : FROM fnproject/node
 ---> 016382f39a51
Step 6/9 : WORKDIR /function
 ---> Using cache
 ---> 8c66c3b8b760
Step 7/9 : ADD . /function/
 ---> df3bd424e08c
Step 8/9 : COPY --from=build-stage /function/node_modules/ /function/node_modules/
 ---> 0970aa10bd82
Step 9/9 : ENTRYPOINT ["node", "func.js"]
 ---> Running in 784bb14078af
Removing intermediate container 784bb14078af
 ---> 2b1157cd2228
Successfully built 2b1157cd2228
Successfully tagged petecj2/nodefn:0.0.2

Parts:  [petecj2 nodefn:0.0.2]
Pushing petecj2/nodefn:0.0.2 to docker registry...The push refers to repository [docker.io/petecj2/nodefn]
4c4d0bf877e9: Pushed 
0b2623d672cd: Pushed 
4541c185b2ea: Pushed 
0b3e54ee2e85: Mounted from fnproject/node 
ad77849d4540: Mounted from fnproject/node 
5bef08742407: Mounted from fnproject/node 
0.0.2: digest: sha256:c75c2ffb8806778d3f89983b1185ea4261d0615912a1fe70a17a083059f6cda8 size: 1572
Updating function nodefn using image petecj2/nodefn:0.0.2...
Successfully created app:  nodeapp
Successfully created function: nodefn with petecj2/nodefn:0.0.2
Successfully created trigger: nodefn-trigger
PETERCJO-M-63N9:nodefn petercjo$ fn invoke nodeapp nodefn
{"message":"internal server error"}

Fn: Error calling function: status 500

See 'fn <command> --help' for more information. Client version: 0.5.13
PETERCJO-M-63N9:nodefn petercjo$ fn list calls nodeapp nodefn
PETERCJO-M-63N9:nodefn petercjo$

The only thing I did differently, and had to do the same on the Go example, was I didn't use the --local flag on the deploy command since I'm running on a remote K8S cluster instead of my local Docker.

Thoughts? Other logs I should be looking at?

I'd be happy to post a similar output for the successful set of Go commands if that'll help.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.