@reverb-app/functions v1.0.0
Reverb - Functions Server
This server hosts your application's Reverb jobs and contains all the code needed to create these jobs from the provided template. The jobs will be called from the Graphile Worker Server which host the Graphile Worker runners.
The Sample Server provides you with an index.ts file, which serves as a template where you can define functions and the events or cron-jobs that will trigger these functions. Additionally index.ts will boot up the Functions Server.
A function can be a single function invocation or can be comprised of different steps. Each step will need to be awaited.
A step can be run, delayed, invoked, or emit an event.
Install
This project uses node and npm. For local development, simply run:
$ npm installUsage
Environment
To run the Functions Server locally, you will first need to configure several environment variables in a .env file.
GRAPHILE_CONNECTION_STRINGis for connecting with the PostgreSQL database that will host your job queue. This should be the same database to which your workers server is connected.PORTis the port the functions server will be listening on. This is usually 3002.
Running the Functions Server
To initialize the Functions Server in a development environment, from the Sample Server, run:
$ npm run devThis will boot up the Functions Server. The Function Server will populate the database with the necessary data and expose the API endpoint the Workers Server uses to send requests to invoke your functions.
Function Server API
The Sample Server provides you with an index.ts file, where you can find the templates for defining the different kinds of functions.
You define functions with the createFunction method. The createFunction method takes 1 argument, an FunctionData object. There are four properties that can be applied to this object:
id- This is the unique string identifier for the objectfn- The function's code- Has the type
(event: Event, step: Step) => Promise<any>
- Has the type
event- Optional If the function is tied to an event, this is the event's name- Can not be present with a
cronproperty
- Can not be present with a
cron- Optional If the function is tied to a cron, this is the cron string- Can not be present with an
eventproperty - Proper format can be found here
- Can not be present with an
A FunctionData must have either an event or cron property, but not both, for it to be valid.
Inside the fn function we provide two parameters. These must always be awaited:
event- The data tied to the event being firedid- The unique string ID generated when the event was fired. Is an empty string for a cron.name- The name of the event that was fired.payload- Optional Any data passed with the event when it was fired.objecttype. This will be defined by you and you should check the typing when you run the function.
step- An object to provide step functionality. It provides these methods:run-(id: string, callback: () => Promise<any>) => Promise<any>idmust be uniquecallbackis used to run an individual step.- The return value is the return value of the
callback
delay-(id: string, timePeriod: string) => Promise<any>idmust be uniquetimePeriodis the period of time before continuing the function.- Can be any combination of numberperiod and any number of spaces between
- period can be
s(seconds),m(minutes),h(hours),d(days),w(weeks),o(months) - Sample
1d 12h 10m30swould be 1 day, 12 hours, 10 minutes, and 30 seconds.
emitEvent-(id: string, eventId: string, payload?: object) => Promise<any>idmust be uniqueeventIdis the event name to be emittedpayloadOptional is the payload you wish to pass to the event
invoke=(id: string, invokedFnName: string, payload?: object) => Promise<any>idmust be uniqueinvokedFnNameis the function name to be invokedpayloadOptional is the payload you wish to pass to the function via theeventobject
Examples:
const func1 = server.createFunction({
id: "first-function",
event: "event1",
fn: async () => {
console.log("Hello world");
},
});
const func2 = server.createFunction({
id: "second-function",
cron: "event1",
fn: async () => {
console.log("Hi :)");
},
});
const func3 = server.createFunction({
id: "third-function",
event: "event2",
fn: async (event) => {
if (
!!event.payload &&
"url" in event.payload &&
typeof event.payload.url === "string"
) {
fetch(event.payload.url);
}
},
});
const func4 = server.createFunction({
id: "step-function",
event: "event3",
fn: async (event, step) => {
await step.run("phone person", async () => console.log("phone person"));
await step.delay("some delay", "1m30s");
await step.run("email person", async () => console.log("email person"));
},
});
const func5 = server.createFunction({
id: "function-calls-function",
event: "event4",
fn: async (event, step) => {
await step.invoke("call 3rd function", "third-function", {
url: "https://enaeajsfdm4b.x.pipedream.net/",
});
},
});
const func6 = server.createFunction({
id: "emit-event-function",
event: "event5",
fn: async (event, step) => {
await step.emitEvent("emit-event2", "event2", {
url: "https://enaeajsfdm4b.x.pipedream.net/",
});
},
});
const func7 = server.createFunction({
id: "cron-function",
cron: "*/4 * * * *",
fn: async (event, step) => {
await step.invoke("call 3rd function", "third-function", {
url: "https://enaeajsfdm4b.x.pipedream.net/",
});
},
});
const func8 = server.createFunction({
id: "error-function",
event: "error",
fn: async () => {
throw new Error("This error is for testing purposes");
},
});
const func9 = server.createFunction({
id: "webhook_test",
event: "reverb-received-webhook",
fn: async (event) => {
console.log(event);
},
});