Exoframe v5.0 - simplifying js function deployments
Tags: devops, node, javascript, docker
Published: 2019-07-17 12:30:09

[Exoframe](https://github.com/exoframejs/exoframe/) is a self-hosted tool that allows simple one-command deployments using Docker.
If you've never heard about it, you can read more [here](https://hackernoon.com/simplifying-docker-management-with-exoframe-9275e92c7406) or see it in action [here](https://www.youtube.com/watch?v=AEwLt5hmKYo).

And today I'm happy to release [Exoframe v5.0](https://github.com/exoframejs/exoframe/releases/tag/5.0.0) that adds support for simple javascript functions deployments in one command.  
Currently, Exoframe supports 4 types of javascript functions: 
 - HTTP functions - your typical HTTP request handler
 - Worker functions - anything that has to run in the background (e.g. bots, repeated tasks, etc)
 - Trigger functions - your own triggers that can invoke custom functions
 - Custom functions - handlers for your custom triggers

Here's a quick example walkthroughs that demonstrate creation of each type of functions.

### Creating HTTP function

First, let's write the function itself.
Exoframe will `require` whole function folder so make sure to either call your file `index.js` or to include `package.json` that points to your main file.
In this case, let's `touch index.js` and write the following code in it:

```js
// index.js
module.exports = async (event, context) => {
  // use context.log to provide logs to exoframe
  // those logs can be then accessed from exoframe CLI
  context.log('test log');
  context.log('other log');

  // you can just return a value
  return `hello world`;

  // alternatively you can use reply prop
  // to directly access Fastify reply object
  context.reply.code(200).send('hello world!');
  // make sure to return false-y value if you do this
  // so exoframe doesn't try to send the response second time
  return false;
};
```

Note the usage of `context.log` for logging. Doing so will allow you to get logs from your function using `exoframe logs your-function-name` command.

Next, we need to generate new Exoframe config for your function.
To do that, simply execute:

```
$ exoframe init -f
```

This will generate a config that looks like this:

```json
{
  "name": "test-function",
  "function": true
}
```

By default, Exoframe considers functions to be HTTP functions and will route them to `/${config.name}` - so, `/test-function` in our case.
This can be changed by editing the config to include `route` property, e.g.:

```json
{
  "name": "test-function",
  "function": {
    "route": "test"
  }
}
```

Now you can deploy your function using Exoframe by simply running `exoframe deploy`!

### Creating worker function

Once again, let's start with the function itself.
Let's `touch index.js` and write the following code in it:

```js
// index.js
module.exports = async (_, context) => {
  // use context.log to log stuff, just as in HTTP function
  context.log('Worker started.');
  // worker can execute any long-running task you want
  let counter = 0;
  setInterval(() => {
    context.log(`Worker: ${counter++}`);
  }, 1000);
};
```

After that we want to generate Exoframe config in the same manner as for HTTP function, but then we'll need to modify it to include new `type` property, like so:

```json
{
  "name": "test-worker",
  "function": {
    "type": "worker"
  }
}
```

This will tell Exoframe it should start current function in a separate [Worker Thread](https://nodejs.org/api/worker_threads.html).
And now your worker function is ready for deployment!

### Creating trigger function

And again, let's start with the function itself.
Let's `touch index.js` and write the following code in it:

```js
module.exports = async (dispatchEvent, context) => {
  // log
  context.log('Trigger started.');

  // in this case we trigger all subscribed functions every 1s
  const interval = setInterval(() => {
    context.log(`Triggering!`);
    // dispatching new events to all function with data
    dispatchEvent({data: 'hello world!'});
  }, 1000);

  // trigger function should return a cleanup function
  return () => {
    clearInterval(interval);
  };
};
```

Note that your trigger needs to return a cleanup function that, well, does the cleanup once you remove the function from your server.

After that, you'll need to generate a config file and edit it to include `type` property set to `trigger`, like so:

```json
{
  "name": "test-trigger",
  "function": {
    "type": "trigger"
  }
}
```

That's it, your trigger is ready to be deployed!

### Creating custom handler function

Finally, let's see how you can create custom handlers for your triggers.
Let's write that `index.js` once again: 

```js
module.exports = async (event, context) => {
  // Will get custom data from trigger above, so logging will say:
  // Custom function triggered: {"data": "hello world!"}
  context.log(`Custom function triggered: ${JSON.stringify(event.data)}`);
};
```

Now we need to tell Exoframe that our current function should react to our custom trigger. This is done by using `type` property and assigning a value equal to the name of our custom trigger, like so:

```json
{
  "name": "test-triggered-fn",
  "function": {
    "type": "test-trigger"
  }
}
```

And now you can deploy your custom handler!

### That's it!

That covers all the basics with regards to working with functions in Exoframe. 
You can find more detailed info in [Exoframe docs](https://github.com/exoframejs/exoframe/blob/master/docs/Functions.md).

[Try it out](https://github.com/exoframejs/exoframe) and let me know what you think!