{"id":14384205,"url":"https://github.com/s8sg/faas-flow","last_synced_at":"2025-04-07T07:15:04.108Z","repository":{"id":49854791,"uuid":"138843380","full_name":"s8sg/faas-flow","owner":"s8sg","description":"Function Composition for OpenFaaS","archived":false,"fork":false,"pushed_at":"2023-03-26T09:35:05.000Z","size":33359,"stargazers_count":258,"open_issues_count":14,"forks_count":38,"subscribers_count":16,"default_branch":"master","last_synced_at":"2025-04-06T10:14:21.656Z","etag":null,"topics":["automation","faas","faas-flow","function-chain","function-composition","integration","openfaas","openfaas-template","pipeline","pipeline-framework","serverless","workflow-engine"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/s8sg.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2018-06-27T07:08:49.000Z","updated_at":"2025-03-23T11:44:04.000Z","dependencies_parsed_at":"2024-01-14T19:29:26.801Z","dependency_job_id":null,"html_url":"https://github.com/s8sg/faas-flow","commit_stats":null,"previous_names":["s8sg/faasflow","s8sg/faaschain"],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s8sg%2Ffaas-flow","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s8sg%2Ffaas-flow/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s8sg%2Ffaas-flow/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s8sg%2Ffaas-flow/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/s8sg","download_url":"https://codeload.github.com/s8sg/faas-flow/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247608153,"owners_count":20965952,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["automation","faas","faas-flow","function-chain","function-composition","integration","openfaas","openfaas-template","pipeline","pipeline-framework","serverless","workflow-engine"],"created_at":"2024-08-28T18:01:12.218Z","updated_at":"2025-04-07T07:15:04.020Z","avatar_url":"https://github.com/s8sg.png","language":"Go","funding_links":[],"categories":["Go"],"sub_categories":[],"readme":"# Faas-flow - Function Composition for [OpenFaaS](https://github.com/openfaas/faas)\n\n![Template CI](https://github.com/s8sg/faas-flow/workflows/Template%20Docker%20Image%20CI/badge.svg)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![OpenTracing Badge](https://img.shields.io/badge/OpenTracing-enabled-blue.svg)](http://opentracing.io)\n[![OpenFaaS](https://img.shields.io/badge/openfaas-serverless-blue.svg)](https://www.openfaas.com)\n[![GoDoc](https://godoc.org/github.com/faasflow/lib/openfaas?status.svg)](https://godoc.org/github.com/faasflow/lib/openfaas)\n\n\u003e - [x] **Pure**      \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; FaaS with [OpenFaaS](https://github.com/openfaas/faas)\n\u003e - [x] **Fast**      \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;  Built with `Go`\n\u003e - [x] **Secured**   \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; With `HMAC`\n\u003e - [x] **Stateless** \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; By design\n\u003e - [x] **Tracing**   \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; With `open-tracing`\n\u003e - [x] **Available** \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;As `faas-flow` template\n\n[**Faas-flow tower**](https://github.com/s8sg/faas-flow-tower) visualizes and monitors flow functions.\n\n## Overview\n\nFaas-flow allows you to realize OpenFaaS function composition with ease. By\ndefining a simple pipeline, you can orchestrate multiple functions without\nhaving to worry about the internals.\n\n```go\nfunc Define(flow *faasflow.Workflow, context *faasflow.Context) (err error) {\n    flow.SyncNode().Apply(\"Func1\").Apply(\"Func2\")\n    return nil\n}\n```\n\nAfter building and deploying, it will give you an OpenFaaS function that\norchestrates calling `Func2` with the output of `Func1`.\n\n## Use Cases\n\nFaas-flow as a function composure provides the back-bone for building complex\nsolutions and promote automation.\n\n### Data Processing Pipeline\n\nFaas-flow can orchestrate a pipeline with long and short running function\nperforming ETL jobs without having to orchestrate them manually or maintaining a\nseparate application. Faas-flow ensures the execution order of several functions\nrunning in parallel or dynamically and provides rich construct to aggregate\nresults while maintaining the intermediate data.\n\n### Application Orchestration Workflow\n\nFunctions are great for isolating certain functionalities of an application.\nAlthough one still need to call the functions, write workflow logic, handle\nparallel processing and retries on failures. Using Faas-flow you can combine\nmultiple OpenFaaS functions with little codes while your workflow will scale\nup/down automatically to handle the load.\n\n### Function Reusability\n\nFass-flow allows you to write function only focused on solving one problem\nwithout having to worry about the next. It makes function loosely coupled from\nthe business logic promoting reusability. You can write the stateless function\nand use it across multiple applications, where Faas-flow maintains the execution\nstate for individual workflow per requests.\n\n## Pipeline Definition\n\nBy supplying a number of pipeline operators, the complex composition can be\nachieved with little work:\n![alt overview](https://github.com/s8sg/faas-flow/blob/master/doc/overview.jpg)\n\nThe above pipelines can be achieved with little, but powerful code:\n\n### Sync chain\n\n```go\nfunc Define(flow *faasflow.Workflow, context *faasflow.Context) (err error) {\n    flow.SyncNode()\n        .Apply(\"func1\")\n        .Apply(\"func2\")\n        .Modify(func(data []byte) ([]byte, error) {\n            // do something\n            return data, nil\n        })\n    return nil\n}\n```\n\n### Async chain\n\n```go\nfunc Define(flow *faasflow.Workflow, context *faasflow.Context) (err error) {\n    dag := flow.Dag()\n    dag.Node(\"n1\").Apply(\"func1\")\n    dag.Node(\"n2\")\n        .Apply(\"func2\")\n        .Modify(func(data []byte) ([]byte, error) {\n            // do something\n            return data, nil\n        })\n    dag.Node(\"n3\").Apply(\"func4\")\n    dag.Edge(\"n1\", \"n2\")\n    dag.Edge(\"n2\", \"n3\")\n    return nil\n}\n```\n\n### Parallel branching\n\n```go\nfunc Define(flow *faasflow.Workflow, context *faasflow.Context) (err error) {\n    dag := flow.Dag()\n    dag.Node(\"n1\").Modify(func(data []byte) ([]byte, error) {\n        // do something\n        return data, nil\n    })\n    dag.Node(\"n2\").Apply(\"func1\")\n    dag.Node(\"n3\").Apply(\"func2\").Modify(func(data []byte) ([]byte, error) {\n        // do something\n        return data, nil\n    })\n    dag.Node(\"n4\", faasflow.Aggregator(func(data map[string][]byte) ([]byte, error) {\n        // aggregate branch result data[\"n2\"] and data[\"n3\"]\n        return []byte(\"\"), nil\n    }))\n\n    dag.Edge(\"n1\", \"n2\")\n    dag.Edge(\"n1\", \"n3\")\n    dag.Edge(\"n2\", \"n4\")\n    dag.Edge(\"n3\", \"n4\")\n    return nil\n}\n```\n\n### Dynamic branching\n\n```go\nfunc Define(flow *faasflow.Workflow, context *faasflow.Context) (err error) {\n    dag := flow.Dag()\n    dag.Node(\"n1\").Modify(func(data []byte) ([]byte, error) {\n        // do something\n        return data, nil\n    })\n    conditionalDags := dag.ConditionalBranch(\n        \"C\",\n        []string{\"c1\", \"c2\"}, // possible conditions\n        func(response []byte) []string {\n            // for each returned condition the corresponding branch will execute\n            // this function executes in the runtime of condition C\n            return []string{\"c1\", \"c2\"}\n        },\n        faasflow.Aggregator(func(data map[string][]byte) ([]byte, error) {\n            // aggregate all dynamic branches results\n            return []byte(\"\"), nil\n        }),\n    )\n\n    conditionalDags[\"c2\"].Node(\"n1\").Apply(\"func1\").Modify(func(data []byte) ([]byte, error) {\n        // do something\n        return data, nil\n    })\n    foreachDag := conditionalDags[\"c1\"].ForEachBranch(\n        \"F\",\n        func(data []byte) map[string][]byte {\n            // for each returned key in the hashmap a new branch will be executed\n            // this function executes in the runtime of foreach F\n            return map[string][]byte{\"f1\": data, \"f2\": data}\n        },\n        faasflow.Aggregator(func(data map[string][]byte) ([]byte, error) {\n            // aggregate all dynamic branches results\n            return []byte(\"\"), nil\n        }),\n    )\n    foreachDag.Node(\"n1\").Modify(func(data []byte) ([]byte, error) {\n        // do something\n        return data, nil\n    })\n    dag.Node(\"n2\")\n    dag.Edge(\"n1\", \"C\")\n    dag.Edge(\"C\", \"n2\")\n\n    return nil\n}\n```\n\nFull implementation of the above examples are available\n[here](https://github.com/s8sg/faasflow-example).\n\nOpenFaaS flow librray are available as [github.com/faasflow/lib/openfaas](https://godoc.org/github.com/faasflow/lib/openfaas)\n\n## Faas-flow Design\n\nThe current design consideration is made based on the below goals:\n\n1. Leverage the OpenFaaS platform\n2. Not to violate the notions of function\n3. Provide flexibility, scalability, and adaptability\n\n### Just as function as any other\n\nFaas-flow is deployed and provisioned just like any other OpenFaaS function. It\nallows Faas-flow to take advantage of rich functionalities available on\nOpenFaaS. Faas-flow provide an OpenFaaS template (`faas-flow`) and just like any\nother OpenFaaS function it can be deployed with `faas-cli`.\n\n![alt its a function](https://github.com/s8sg/faas-flow/blob/master/doc/design/complete-faas.jpg)\n\n### Adapter pattern for zero instrumentation in code\n\nFaas-flow function follows the adapter pattern. Here the adaptee is the\nfunctions and the adapter is the flow. For each node execution, Faas-flow handle\nthe calls to the functions. Once the execution is over, it forwards an event to\nitself. This way the arrangement logic is separated from the functions and is\nimplemented in the adapter. Compositions need no code instrumentations, making\nfunctions completely independent of the details of the compositions.\n\n![alt function is independent of composition](https://github.com/s8sg/faas-flow/blob/master/doc/design/adapter-pattern.jpg)\n\n### Aggregate pattern as chaining\n\nAggregation of separate function calls is done as chaining. Multiple functions\ncan be called from a single node with order maintained as per the chain. This\nway one execution node can be implemented as an aggregator function that invokes\nmultiple functions collects the results, optionally applies business logic, and\nreturns a consolidated response to the client or forward to next nodes.\nFaas-flow fuses the adapter pattern and aggregate pattern to support more\ncomplex use cases.\n\n![alt aggregation](https://github.com/s8sg/faas-flow/blob/master/doc/design/aggregate-pattern.jpg)\n\n### Event driven iteration\n\nOpenFaaS uses [Nats](https://nats.io) for event delivery and Faas-flow leverages\nOpenFaaS platform. Node execution in Faas-flow starts by a completion event of\none or more previous nodes. A completion event denotes that all the previous\ndependent nodes have completed. The event carries the execution state and\nidentifies the next node to execute. With events Faas-flow asynchronously\ncarry-on execution of nodes by iterating itself over and over till all nodes are\nexecuted.\n\n![alt iteration](https://github.com/s8sg/faas-flow/blob/master/doc/design/event-driven-iteration.jpg)\n\n### 3rd party KV store for coordination\n\nWhen executing branches, one node is dependent on more than one predecessor\nnodes. In that scenario, the event for completion is generated by coordination\nof earlier nodes. Like any distributed system the coordination is achieved via a\ncentralized service. Faas-flow keeps the logic of the coordination controller\ninside of Faas-flow implementation and lets the user use any external\nsynchronous KV store by implementing\n[`StateStore`](https://godoc.org/github.com/faasflow/sdk#StateStore).\n\n![alt coordination](https://github.com/s8sg/faas-flow/blob/master/doc/design/3rd-party-statestore.jpg)\n\n### 3rd party Storage for intermediate data\n\nResults from function execution and intermediate data can be handled by the user\nmanually. Faas-flow provides data-store for intermediate result storage. It\nautomatically initializes, store, retrieve and remove data between nodes. This\nfits great for data processing applications. Faas-flow keeps the logic of\nstorage controller inside of Faas-flow implementation and lets the user use any\nexternal object storage by implementing\n[`DataStore`](https://godoc.org/github.com/faasflow/sdk#DataStore).\n\n![alt storage](https://github.com/s8sg/faas-flow/blob/master/doc/design/3rd-party-storage.jpg)\n\nFaas-flow design is not fixed and like any good design, it is evolving. Please\ncontribute to make it better.\n\n## Getting Started\n\n### Deploy OpenFaaS\n\nFaasFlow requires the OpenFaaS to be deployed and the OpenFaaS Cli to be installed. You\ncan either have your OpenFaaS deployed in [Kubernets](https://kubernetes.io) or\nin [Swarm](https://docs.docker.com/engine/swarm/).\n\nTo deploy OpenFaaS and to\ninstall the OpenFaaS cli client follow this guide:\n[https://docs.openfaas.com/deployment/](https://docs.openfaas.com/deployment/).\n\n### Deploy Faas-flow Components with Faas-flow Infra\n\n[Faas-Flow infra](https://github.com/s8sg/faas-flow-infra) provides the kubernetes and swarm deployment resources for faas-flow dependencies. Follow the [README](https://github.com/faasflow/faas-flow-infra#getting-started) to deploy Faas-Flow Infra \nin Kubernets or in Swarm\n\n### Deploy Faas-flow Tower\n\n[Faas-Flow tower](https://github.com/faasflow/faas-flow-tower) provides the dashboard to visualise and monitor your flow. Follow the [README](https://github.com/faasflow/faas-flow-tower#deploy-faas-flow-tower) to deploy Faas-Flow tower on OpenFaaS\n\n### Writing Flow\n\nThis example implements a very simple flow to `Greet`\n\n#### Get template\n\nPull `faas-flow` template with the `faas-cli`\n\n```shell\nfaas template pull https://github.com/s8sg/faas-flow\n```\n\n#### Create new flow function\n\nCreate a new function using `faas-flow` template\n\n```shell\nfaas new greet --lang faas-flow\n```\n\n#### Edit stack.yml\n\nEdit function stack file `greet.yml`\n\n```yaml\ngreet:\n  lang: faas-flow\n  handler: ./greet\n  image: greet:latest\n  labels:\n    faas-flow: 1\n  annotations:\n    faas-flow-desc: \"test flow to greet\"\n  environment_file:\n    - flow.yml\n  secrets:\n    - s3-secret-key\n    - s3-access-key\n```\n\n#### Add configuration\n\nAdd a separate configuration file `flow.yml` with faas-flow related configuration.\n\n```yaml\nenvironment:\n  gateway: \"gateway.openfaas:8080\" # The address of OpenFaaS gateway\n  enable_tracing: true # tracing allows to monitor requests\n  trace_server: \"jaeger-agent.faasflow:5775\" # The address of jaeger tracing agent\n  consul_url: \"consul.faasflow:8500\" # The address of consul\n  s3_url: \"minio.faasflow:9000\" # The address of minio\n```\n\n#### Edit flow definition\n\nEdit `greet/handler.go` and Update `Define()`\n\n```go\nfunc Define(flow *faasflow.Workflow, context *faasflow.Context) (err error) {\n    flow.SyncNode().Modify(func(data []byte) ([]byte, error) {\n        result := \"Hello \" + string(data)\n        return []byte(result), nil\n    })\n    return nil\n}\n```\n\n#### Build and Deploy\n\nBuild and deploy\n\n```shell\nfaas build -f greet.yml\nfaas deploy -f greet.yml\n```\n\nThis function will generate one Synchronous node\n\n```text\nModify(\"name\") -\u003e Hello name\n```\n\nAll calls will be performed in one single execution of the flow function and\nresult will be returned to the callee.\n\n\u003e Note: For flow that has more than one nodes, Faas-flow doesn't return any\n\u003e response. External storage or callback can be used to retrieve an async result.\n\n#### Invoke\n\n```shell\necho \"Adam\" | faas invoke greet\n```\n\n\n\n## Request Tracking by ID\n\nFor each new request, faas-flow generates a unique `Request Id` for the flow.\nThe same Id is used when logging.\n\n```shell\n2018/08/13 07:51:59 [Request `bdojh7oi7u6bl8te4r0g`] Created\n2018/08/13 07:52:03 [Request `bdojh7oi7u6bl8te4r0g`] Received\n```\n\nThe assigned request Id is set on the response header `X-Faas-Flow-Reqid`\nOne may provide custom request Id by setting `X-Faas-Flow-Reqid` in the request\nheader.\n\n## Request Tracing with [Faas-Flow-Tower](https://github.com/s8sg/faas-flow-tower)\n    \nFaasFlow Tower enables the real time monitoring \nfor each requests. Request traces are visible when `enable_tracing` is enabled. FaaSFlow is\nthe best way to monitor flows and execution status of each node for each request.\n\nBelow is an example of tracing page for a request of\n[faas-flow-example](https://github.com/faasflow/faas-flow-example).\n\n![alt monitoring](https://github.com/s8sg/faas-flow-tower/blob/master/doc/monitoring.png)\n\n## Use of Callback\n\nTo receive a result of long running **FaaSFlow** request, you can specify the\n`X-Faas-Flow-Callback-Url`. FaaSFlow will invoked the callback URL with the\nfinal result and with the request ID set as `X-Faas-Flow-Reqid` in request\nHeader. \n\u003e Note: `X-Callback-Url` from OpenFaaS is not supported in FaaSFlow.\n\n## Pause, Resume or Stop Request\n\nA request in faas-flow has three states:\n\n1. Running\n2. Paused\n3. Stopped\n\nFaas-flow doesn't keep the state of a finished request\n\nTo pause a running request:\n\n```shell\nfaas invoke \u003cworkflow_name\u003e --query pause-flow=\u003crequest_id\u003e\n```\n\nTo resume a paused request\n\n```shell\nfaas invoke \u003cworkflow_name\u003e --query resume-flow=\u003crequest_id\u003e\n```\n\nTo stop an active (paused/running) request\n\n```shell\nfaas invoke \u003cworkflow_name\u003e --query stop-flow=\u003crequest_id\u003e\n```\n\n## Use of context\n\nContext can be used inside definition for different use cases. Context provide\nvarious information such as:\n\n- **HttpQuery** to retrieve original request queries\n- **State** to get flow state\n- **Node** to get current node\nalong with that it wraps the **DataStore** to store data\n\n### Store data in context with `DataStore`\n\nContext uses `DataStore` to store/retrieve data. User can do the same by calling\n`Get()`, `Set()`, and `Del()` from `context`:\n\n```go\nflow.SyncNode().\n    Modify(func(data []byte) {\n        // parse data and set to be used later\n        // json.Unmarshal(\u0026req, data)\n        context.Set(\"commitsha\", req.Sha)\n    })\n    .Apply(\"myfunc\")\n    .Modify(func(data []byte) {\n        // retrieve the data that was set in the context\n        commitsha, _ = context.GetString(\"commitsha\")\n        // use the query\n    })\n```\n\n### Getting Http Query to Workflow\n\nHttp Query to flow can be used retrieved from context using `context.Query`\n\n```go\nflow.SyncNode()\n    .Apply(\"myfunc\", Query(\"auth-token\", context.Query.Get(\"token\"))) // pass as a function query\n    .Modify(func(data []byte) {\n        token = context.Query.Get(\"token\") // get query inside modifier\n    })\n```\n\n### Use of request context\n\nNode, requestId, State is provided by the `context`\n\n```go\ncurrentNode := context.GetNode()\nrequestId := context.GetRequestId()\nstate := context.State\n```\n\nfor more details check Faas-flow\n[GoDoc](https://godoc.org/github.com/s8sg/faas-flow).\n\n## External `StateStore` for coordination controller\n\nFaas-flow implements coordination controller and store the intermediate request\nwith StateStore. By default Faas-flow uses\n[consul](https://github.com/faasflow/faas-flow-consul-statestore) as default\nstate-store, although user can define custom state-store with `StateStore`\ninterface and use any external Synchronous KV store as backend.\n\n```go\ntype StateStore interface {\n    // Configure the StateStore with flow name and request ID\n    Configure(flowName string, requestId string)\n    // Initialize the StateStore (called only once in a request span)\n    Init() error\n    // Set a value (override existing, or create one)\n    Set(key string, value string) error\n    // Get a value\n    Get(key string) (string, error)\n    // Compare and Update a value\n    Update(key string, oldValue string, newValue string) error\n    // Cleanup all the resorces in StateStore (called only once in a request span)\n    Cleanup() error\n}\n```\n\nThe custom `StateStore` can be set with `OverrideStateStore()` at\n`function/handler.go`:\n\n```go\n// OverrideStateStore provides the override of the default StateStore\nfunc OverrideStateStore() (faasflow.StateStore, error) {\n    myss, err := myStateStore.Init()\n    return myss, err\n}\n```\n\n`StateStore` is mandatory for a FaaSFlow to operate.\n\n### Official state-stores\n\n- **[ConsulStateStore](https://github.com/faasflow/faas-flow-consul-statestore)**:\n  statestore implementation with **consul** (default);\n- **[EtcdStateStore](https://github.com/s8sg/faas-flow-etcd-statestore)**:\n  statewtore implementation with **etcd**.\n\n## External `DataStore` for storage controller\n\nFaas-flow uses the `DataStore` to store partially completed data between nodes\nand request context data. By default Faas-flow uses\n[minio](https://github.com/faasflow/faas-flow-minio-datastore) as default data-store,\nalthough user can define custom data-store with `DataStore` interface and use\nany external storage as backend.\n\n```go\n type DataStore interface {\n    // Configure the DaraStore with flow name and request ID\n    Configure(flowName string, requestId string)\n    // Initialize the DataStore (called only once in a request span)\n    Init() error\n    // Set store a value for key, in failure returns error\n    Set(key string, value string) error\n    // Get retrives a value by key, if failure returns error\n    Get(key string) (string, error)\n    // Del delets a value by a key\n    Del(key string) error\n    // Cleanup all the resorces in DataStore\n    Cleanup() error\n }\n```\n\nData Store can be implemented and set by user at the `OverrideDataStore()` at\n`function/handler.go`:\n\n```go\n// OverrideDataStore provides the override of the default DataStore\nfunc OverrideDataStore() (faasflow.DataStore, error) {\n    myds, err := myDs.Init()\n    return myds, err\n}\n```\n\n`DataStore` is mandatory for a FaaSFlow to operate.\n\n### Available data-stores\n\n- **[MinioDataStore](https://github.com/faasflow/faas-flow-minio-datastore)**:\n  allows to store data in **amazon s3** or local **minio DB** (default).\n\n## Cleanup with `Finally()`\n\nFinally provides an efficient way to perform post-execution steps of the flow.\nIf specified `Finally()` invokes in case of both failure and success of the\nflow. A Finally method can be set as:\n\n```go\nfunc Define(flow *faasflow.Workflow, context *faasflow.Context) (err error) {\n    // Define flow\n    flow.SyncNode().Modify(func(data []byte) {\n        // parse data and set to be used later\n        // json.Unmarshal(\u0026req, data)\n        context.Set(\"commitsha\", req.Sha)\n    }).\n    Apply(\"myfunc\").Modify(func(data []byte) {\n        // retrieve the data in different node from context\n        commitsha, _ = context.GetString(\"commitsha\")\n    })\n    flow.OnFailure(func(err error) {\n        // failure handler\n    })\n    flow.Finally(func() {\n        // delete the state resource\n        context.Del(\"commitsha\")\n    })\n}\n```\n\n## Contribute\n\n- **Issue/Suggestion** Create an issue at\n  [Faas-flow-issue](https://github.com/s8sg/faas-flow/issues).\n- **ReviewPR/Implement** Create Pull Request at\n  [Faas-flow-pr](https://github.com/s8sg/faas-flow/issues).\n\nJoin Faasflow [Slack](https://join.slack.com/t/faas-flow/shared_invite/enQtNzgwNDY2MjI4NTc5LWZiOGQ4M2ZlZTI0OTI0ZjU5YmUyMDgwOWJiOWU0YzIzMGQ3Y2QxMTMzMDlhZGZhYWFlZTkzMGQxMzU4NDdmOGU)\nfor more.\n\n## Supported By\n\u003ca href=\"https://www.jetbrains.com/?from=faas-flow\"\u003e\n\u003cimg src=\"https://github.com/s8sg/faas-flow/blob/master/doc/jetbrains.png\" data-canonical-src=\"https://github.com/s8sg/faas-flow/blob/master/doc/jetbrains.png\" width=\"200\" height=\"200\" /\u003e\n\u003c/a\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fs8sg%2Ffaas-flow","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fs8sg%2Ffaas-flow","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fs8sg%2Ffaas-flow/lists"}