{"id":20494051,"url":"https://github.com/jaystack/functionly","last_synced_at":"2025-04-13T17:21:30.218Z","repository":{"id":21268582,"uuid":"91099571","full_name":"jaystack/functionly","owner":"jaystack","description":"Functional microservices framework - best served serverless","archived":false,"fork":false,"pushed_at":"2024-03-05T13:35:35.000Z","size":1981,"stargazers_count":8,"open_issues_count":13,"forks_count":0,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-03-27T08:11:22.322Z","etag":null,"topics":["decorators","framework","nodejs","serverless"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jaystack.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-05-12T14:27:56.000Z","updated_at":"2024-02-28T17:12:27.000Z","dependencies_parsed_at":"2024-11-15T17:48:48.743Z","dependency_job_id":null,"html_url":"https://github.com/jaystack/functionly","commit_stats":{"total_commits":220,"total_committers":9,"mean_commits":"24.444444444444443","dds":"0.44999999999999996","last_synced_commit":"ee590ad81cdd5d377102f5002e906d9197cbb081"},"previous_names":[],"tags_count":42,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jaystack%2Ffunctionly","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jaystack%2Ffunctionly/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jaystack%2Ffunctionly/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jaystack%2Ffunctionly/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jaystack","download_url":"https://codeload.github.com/jaystack/functionly/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248750851,"owners_count":21155795,"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":["decorators","framework","nodejs","serverless"],"created_at":"2024-11-15T17:38:01.458Z","updated_at":"2025-04-13T17:21:30.190Z","avatar_url":"https://github.com/jaystack.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# functionly\n\nThe `functionly library` lets you build `serverless` nodejs applications in an innovative, functional, and fun by abstraction way.\nUse the JavaScript language and the JSON syntax to describe infrastructure and entities, service dependencies, and to implement service code. Deploy your solution to cloud providers, or run containerized on your onprem servers, or locally during development time using the `functionly CLI`.\n\nDefining a rest service which listens on `/hello-world`:\n```js\nimport { FunctionalService, rest, description, param } from 'functionly'\n\n@rest({ path: '/hello-world' })\n@description('hello world service')\nexport class HelloWorld extends FunctionalService {\n    static async handle(@param name = 'world') {\n        return `hello ${name}`\n    }\n}\n\nexport const helloworld = HelloWorld.createInvoker()\n```\nRunning on localhost:\n```sh\nfunctionly start\n```\nTry it on http://localhost:3000/hello-world?name=Joe\n\n---\n\n- [Install from npm](#install-from-npm)\n- [Getting started](#getting-started)\n- [Examples](#examples)\n\n# Install from npm:\n\n## functionly CLI\n```sh\nnpm install functionly -g\n```\n\n## functionly library\n```sh\nnpm install functionly\n```\n\n\n# Getting started\n\n* [Create an empty Functionly project](#create-an-empty-functionly-project)\n* [Create a Hello world service](#create-a-hello-world-service)\n    * [Resolve parameter values](#resolve-parameter-values)\n* [Create a Todo application with DynamoDB](#create-a-tododb-application-with-dynamodb)\n    * [Create a dynamo table](#create-a-dynamo-table)\n    * [Read todos](#read-todos)\n    * [Create todo](#create-todo)\n    * *[Extend with Services](#extend-the-example-with-Services) - optional*\n* [Run and Deploy with CLI](#run-and-deploy-with-cli)\n* [AWS deployment](#aws-deployment)\n* [Examples](#examples)\n\n## Create an empty Functionly project\nIt's a simple npm project.\n### Dependencies\n- functionly\n```sh\nnpm install --save functionly\n```\n\n### Dev dependencies\nFunctionly uses webpack with babel for compile the code.\n- babel-core\n- babel-loader\n- babel-preset-functionly-aws\n```sh\nnpm install --save-dev babel-core babel-loader babel-preset-functionly-aws\n```\n\n### Babel configuration\nDefault `.babelrc`\n\n```js\n{\n    \"presets\": [ \"functionly-aws\" ]\n}\n```\n\n### Functionly configuration\nDefault `functionly.json`\n```js\n{\n    \"awsRegion\": \"us-east-1\",\n    \"main\": \"./src/index.js\",\n    \"deployTarget\": \"aws\",\n    \"localPort\": 3000,\n    \"stage\": \"dev\",\n    \"watch\": true,\n    \"compile\": \"babel-loader\"\n}\n```\n\n## Create a Hello world service\nWe need to create a `FunctionalService` to implement the business logic of hello world application\n```js\nimport { FunctionalService } from 'functionly'\n\nexport class HelloWorld extends FunctionalService {\n    static async handle() {}\n}\n```\nIf you want your service to be accessible with a web request over a rest interface then you have to decorate  it with the [rest]() decorator. We have to set the `path` property to define the rest endpoint.\nIf we do not set the `methods` property that means it will accept `GET` requests. (default: `methods: ['get']`)\n```js\n@rest({ path: '/hello-world' })\n```\nDefine a [description]() for the `HelloWorld`, which will make it easier to find in the AWS Lambda list.\n```js\n@description('hello world service')\n```\nNow we have to create the business logic.\n```js\nimport { FunctionalService, rest, description } from 'functionly'\n\n@rest({ path: '/hello-world' })\n@description('hello world service')\nexport class HelloWorld extends FunctionalService {\n    static async handle() {\n        return `hello world`\n    }\n}\n```\nWe are almost done, we just have to export our service from the main file.\n```js\nexport const helloworld = HelloWorld.createInvoker()\n```\n### Resolve parameter values\nIn the `handle` method if you use the `@param` property decorator for a parameter then it resolves the value from a request context.\n```js\nimport { FunctionalService, rest, description, param } from 'functionly'\n\n@rest({ path: '/hello-world' })\n@description('hello world service')\nexport class HelloWorld extends FunctionalService {\n    static async handle(@param name = 'world') {\n        return `hello ${name}`\n    }\n}\n\nexport const helloworld = HelloWorld.createInvoker()\n```\n\n## Create a TodoDB application with DynamoDB\nDefine a base class for FunctionalService to set basic Lambda settings in the AWS environment.\n```js\nimport { FunctionalService, aws } from 'functionly'\n\n@aws({ type: 'nodejs20.x', memorySize: 512, timeout: 3 })\nexport class TodoService extends FunctionalService { }\n```\n\n### Create a dynamo table\nWe need a DynamoTable, called `TodoTable` because we want to store todo items.\n```js\nimport { DynamoTable, dynamoTable, injectable } from 'functionly'\n\n@injectable()\n@dynamo()\nexport class TodoTable extends DynamoTable { }\n```\n\n### Read todos\nWe need to create a service to read todo items.\n```js\nexport class GetAllTodos extends TodoService {\n    static async handle() {}\n}\n```\nIf you want your service to be accessible with a web request over a rest interface then you have to decorate it with the [rest]() decorator. We have to set the `path` property to define the rest endpoint.\nIf we do not set the `methods` property that means it will accept `GET` requests. (default: `methods: ['get']`)\n```js\n@rest({ path: '/getAllTodos' })\n```\nDefine a [description]() for the `TodoService`, which will make it easier to find in the AWS Lambda list.\n```js\n@description('get all Todo service')\n```\nNow we have to create the business logic. We want to read the todo items, so we need to inject the `TodoTable`. Get the items from it and return from our service.\n```js\nimport { rest, description, inject } from 'functionly'\n\n@rest({ path: '/getAllTodos' })\n@description('get all Todo service')\nexport class GetAllTodos extends TodoService {\n    static async handle(@inject(TodoTable) db) {\n        let items = await db.scan()\n        return { ok: 1, items }\n    }\n}\n```\nWe are almost done, we just have to export our service from the main file.\n```js\nexport const getAllTodos = GetAllTodos.createInvoker()\n```\n\n### Create todo\nWe need a service to create todo items, so let's do this. We will also define a [rest]() endpoint and a [description]().\n```js\nimport { rest, description } from 'functionly'\n\n@rest({ path: '/createTodo', methods: ['post'] })\n@description('create Todo service')\nexport class CreateTodo extends TodoService {\n    static async handle() {}\n}\n```\nWe need some values to create a new todo item: `name`, `description` and `status`. Expect these with the [param]() decorator, and it will resolve them from the invocation context.\n```js\nimport { rest, description, param } from 'functionly'\n\n@rest({ path: '/createTodo', methods: ['post'] })\n@description('create Todo service')\nexport class CreateTodo extends TodoService {\n    static async handle(@param name, @param description, @param staus) {}\n}\n```\nThe business logic: save a new todo item. [Inject]() the `TodoTable` and save a new todo item with the `put` function. We need an id for the new todo, in the example, we'll use [shortid](https://www.npmjs.com/package/shortid) to generate them.\n```js\nimport { generate } from 'shortid'\nimport { rest, description, param } from 'functionly'\n\n@rest({ path: '/createTodo', methods: ['post'] })\n@description('create Todo service')\nexport class CreateTodo extends TodoService {\n    static async handle(@param name, @param description, @param status, @inject(TodoTable) db) {\n        let item = {\n            id: generate(),\n            name,\n            description,\n            status\n        }\n\n        await db.put({ Item: item })\n\n        return { ok: 1, item }\n    }\n}\n\nexport const createTodo = CreateTodo.createInvoker()\n```\n\n## Extend the example with Services\n\u003e **Optional**\n\nCreate two services: validate and persist todo items. Then the CreateTodo has only to call these services.\n\n### Validate todo\nIt will be an [injectable]() service and expect the three todo values, then implement a validation logic in the service.\n```js\nimport { injectable, param } from 'functionly'\n\n@injectable()\nexport class ValidateTodo extends Service {\n    static async handle( @param name, @param description, @param status) {\n        const isValid = true\n        return { isValid }\n    }\n}\n```\n\n### Persist todo\nIt will be an [injectable]() service and expect the three todo values and [inject]() a `TodoTable` then implement a persist logic in the service.\n```js\nimport { injectable, param, inject } from 'functionly'\n\n@injectable()\nexport class PersistTodo extends Service {\n    static async handle( @param name, @param description, @param status, @inject(TodoTable) db) {\n        let item = {\n            id: generate(),\n            name,\n            description,\n            status\n        }\n        await db.put({ Item: item })\n        return item\n    }\n}\n```\n\n### Changed CreateTodo FunctionalService\n[inject]() the two new services(`ValidateTodo`, `PersistTodo`) and change the business logic\n```js\nimport { rest, description, param, inject } from 'functionly'\n\n@rest({ path: '/createTodo', methods: ['post'] })\n@description('create Todo service')\nexport class CreateTodo extends TodoService {\n    static async handle(\n        @param name,\n        @param description,\n        @param status,\n        @inject(ValidateTodo) validateTodo,\n        @inject(PersistTodo) persistTodo\n    ) {\n        let validateResult = await validateTodo({ name, description, status })\n        if (!validateResult.isValid) {\n            throw new Error('Todo validation error')\n        }\n        let persistTodoResult = await persistTodo({ name, description, status })\n        return { ok: 1, persistTodoResult }\n    }\n}\n```\n\n### The source code of this example is available [here](https://github.com/jaystack/functionly-examples/tree/master/todoDB-es6)\n\n# Install\n```sh\nnpm install\n```\n\n# Run and Deploy with CLI\nThe CLI helps you to deploy and run the application.\n1. CLI install\n```sh\nnpm install functionly -g\n```\n\n## Local deployment\n1. Create DynamoDB with Docker\n```sh\ndocker run -d --name dynamodb -p 8000:8000 peopleperhour/dynamodb\n```\n2. Deploy will create the tables in DynamoDB\n\u003e Note: Create the [functionly.json](#functionly-configuration) in the project for short commands. Also, you don't have to pass all arguments.\n```sh\nfunctionly deploy local\n```\n## Run in local environment\nDuring development, you can run the application on your local machine.\n```sh\nfunctionly start\n```\n\n## AWS deployment\n\u003e Disclaimer: As functionly provisions AWS services, charges may apply to your AWS account. We suggest you to visit https://aws.amazon.com/pricing/services/ to revise the possible AWS costs.\n\n\u003e [Set up](http://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/setup-credentials.html) AWS Credentials before deployment.\n\n\u003e Note: Create the [functionly.json](#functionly-configuration) in the project for short commands. Also, you don't have to pass all arguments. As the `deployTarget` is configured as `aws` (the default value configured) then the deploy command will use this as deployment target.\n\nFunctionly will create the package and deploy the application to AWS. The package is a [CloudFormation](https://aws.amazon.com/cloudformation/) template, it contains all the AWS resources so AWS can create or update the application's resources based on the template.\n```sh\nfunctionly deploy\n```\n\n\u003e Congratulations! You have just created and deployed your first `functionly` application!\n\n# Examples\n- https://github.com/jaystack/functionly-examples\n\n## Javascript\n- [greeter](https://github.com/jaystack/functionly-examples/tree/master/greeter)\n- [todoDB-es6](https://github.com/jaystack/functionly-examples/tree/master/todoDB-es6)\n\n## Typescript\n- [todoDB](https://github.com/jaystack/functionly-examples/tree/master/todoDB)\n- [todoDB-mongo](https://github.com/jaystack/functionly-examples/tree/master/todoDB-mongo)\n- [todoDBAdvanced](https://github.com/jaystack/functionly-examples/tree/master/todoDBAdvanced)\n- [eventSource](https://github.com/jaystack/functionly-examples/tree/master/eventSource)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjaystack%2Ffunctionly","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjaystack%2Ffunctionly","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjaystack%2Ffunctionly/lists"}