{"id":17266912,"url":"https://github.com/poga/spacer","last_synced_at":"2025-03-22T18:34:30.065Z","repository":{"id":57578869,"uuid":"42514811","full_name":"poga/spacer","owner":"poga","description":"🚀Serverless function platform for Lua","archived":false,"fork":false,"pushed_at":"2019-04-07T04:23:27.000Z","size":12960,"stargazers_count":52,"open_issues_count":0,"forks_count":2,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-03-18T14:04:58.152Z","etag":null,"topics":["golang","kappa-architecture","lua","nginx","openresty","serverless"],"latest_commit_sha":null,"homepage":"","language":"Lua","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/poga.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}},"created_at":"2015-09-15T11:24:29.000Z","updated_at":"2024-12-26T09:26:47.000Z","dependencies_parsed_at":"2022-09-26T19:11:42.240Z","dependency_job_id":null,"html_url":"https://github.com/poga/spacer","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/poga%2Fspacer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/poga%2Fspacer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/poga%2Fspacer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/poga%2Fspacer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/poga","download_url":"https://codeload.github.com/poga/spacer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245003631,"owners_count":20545639,"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":["golang","kappa-architecture","lua","nginx","openresty","serverless"],"created_at":"2024-10-15T08:08:06.836Z","updated_at":"2025-03-22T18:34:27.939Z","avatar_url":"https://github.com/poga.png","language":"Lua","funding_links":[],"categories":[],"sub_categories":[],"readme":"![](./github_assets/logo.png)\n\n# Spacer\n\n![stability-experimental](https://img.shields.io/badge/stability-experimental-orange.svg)\n[![Go Report Card](https://goreportcard.com/badge/github.com/poga/spacer)](https://goreportcard.com/report/github.com/poga/spacer)\n\nServerless function platform for Lua\n\n## Features\n\n* High-performance **non-blocking** functions.\n* Fast **edit-save-reload** development cycle: No redeployment or rebuilding image is needed.\n* **Platform agnostic**: From a simple [PostgreSQL](https://www.postgresql.org/) database to a complex [Kubernetes](https://kubernetes.io/) clusters with [Apache Kafka](https://kafka.apache.org/). Spacer can run on most platforms.\n\n## Synopsis\n\nHere's a minimal spacer project.\n\nYou write a function:\n\n```lua\n-- app/hello.lua\nlocal G = function (args)\n    return \"Hello from Spacer!\"\nend\n\nreturn G\n```\n\n...and define how to expose it to the internet:\n\n```lua\n-- app/gateway.lua\nlocal R = {\n    -- HTTP Method, Path, Function Name\n    {\"GET\", \"/hello\", \"hello\"}\n}\n\nreturn R\n```\n\n...then start the project.\n\n```\n$ ./bin/dev.sh\n```\n\nTest the function.\n\n```\n$ curl localhost:3000/hello\n{\"data\":\"Hello from Spacer!\"}\n```\n\nThat's it!\n\n## Install\n\nThe easiest way would be using the provided [Dockerfile](./Dockerfile).\n\nTo install from source:\n\n1. Install Dependencies\n\n* [OpenResty](https://openresty.org/)\n* [librdkafka](https://github.com/edenhill/librdkafka)\n\nIf you're on a Mac, `brew install openresty/brew/openresty librdkafka` should get everything you need.\n\n2. Install Spacer\n\nSpacer is written in [Go](https://golang.org/). It can be installed via `go get`.\n\n```\n$ go get -u github.com/poga/spacer\n```\n\n## Quick Start\n\nCreate a spacer project:\n\n```\n$ spacer init ~/spacer-hello\n```\n\nStart the development server:\n\n```\n$ cd ~/spacer-hello\n$ ./bin/dev.sh\n```\n\nOpen `http://localhost:3000/hello` and you should see spacer working.\n\n### Hello World\n\nFunctions in spacer are written in Lua, a simple dynamic langauge. Here's a hello world function:\n\n```lua\n-- app/hello.lua\nlocal G = function (args)\n    return \"Hello from Spacer!\"\nend\n\nreturn G\n```\n\nEvery function takes one argument: `args`. For detail, check the **Functions** section.\n\n### Test\n\nSpacer have built-in test framework. Run all tests with command `./bin/test.sh`.\n\n```\n$ ./bin/test.sh\n1..1\n# Started on Mon Feb 12 17:46:48 2018\n# Starting class: testT\nok     1\ttestT.test_ret\n# Ran 1 tests in 0.000 seconds, 1 success, 0 failures\n```\n\nSee `test/test_hello.lua` for example.\n\n## Functions\n\nCode in spacer are organized by functions. For now, spacer only support [Lua](https://www.lua.org/) as the programming language.\n\nFunctions are the main abstraction in spacer. Functions are composed together to build a complex application.\n\nThere are 2 way to invoke other functions from a function. The first is the simplest: just call it like a normal lua function.\n\n```lua\n-- app/bar.lua\nlocal G = function (args)\n  return params.val + 42\nend\n\n-- app/foo.lua\nlocal bar = require \"bar\"\n\nlocal G = function (args)\n  return 100 + bar({val = 1}) -- returns 143\nend\n```\n\nThe second way is use `flow.call`, which emulate a http request between two function. It's useful when you want to create seperated tracings for two function.\n\n```lua\nlocal flow = require \"flow\"\n\nlocal G = function (args)\n  return flow.call(\"bar\", {val = 1}) -- returns 143\nend\n```\n\nIt's called **flow** since the primary usage of it is to trace the flow between functions.\n\n### Exposing Functions to the Public\n\nFunctions are exposed to the public through a gateway. You can define gateway in `gateway.lua`.\n\n```lua\nlocal _R = {\n    -- HTTP Method, Path, Function Name\n    {\"GET\", \"/hello\", \"hello\"},\n\n    -- users resources\n    {\"GET\", \"/users\", \"controllers/users/get\"},\n    {\"PUT\", \"/users/:id\", \"controllers/users/put\"},\n\n    -- teams resources\n    {\"POST\", \"/teams\", \"create_teams\"},\n    {\"GET\", \"/teams/:id\", \"get_team\"},\n    {\"PUT\", \"/teams/:id\", \"put_team\"},\n    {\"DELETE\", \"/teams/:id\", \"delete_team\"},\n\n    -- registration\n    {\"POST\", \"/register\", \"controllers/users/create\"}\n}\n\nreturn _R\n```\n\nWhen invoking a function via HTTP request, query params, route params, and post body(json) are all grouped into an **args** table and passed to the function.\n\n#### Error handling\n\nAn **error** is corresponding to HTTP 4xx status code: something went wrong on the caller side. In this case, return the error as the second returned value\n\n```lua\nlocal G = function (args)\n  return nil, \"invalid password\"\nend\n```\n\nIf an unexpected exception happepend, use the `error()` function to return it. Spacer will return the error with HTTP 500 status code.\n\n```lua\nlocal G = function (args)\n  local conn = db.connect()\n  if conn == nil then\n    error(\"unable to connect to DB\")\n  end\nend\n```\n\n## Event, Trigger, and Kappa Architecture\n\nServerless programming is about events. Fuctions are working together through a series of events.\n\nSpacer use [Kappa Architecture](http://kappa-architecture.com) to provide a unified architecture for both events and data storage.\n\n#### Event and Topic\n\nEvents are organized with topics. Topics need to be defined in the config `config/application.yml`.\n\nTo emit a event, use the built-in `topics` library.\n\n```lua\nlocal topics = require \"topics\"\n\nlocal G = function (args)\n  topics.append(\"EVENT_NAME\", { [EVENT_KEY] = EVENT_PAYLOAD })\nend\n\nreturn G\n```\n\n#### Trigger\n\nTriggers are just functions. You can set a function as a trigger by setting them in the config `config/application.yml`.\n\n#### Log, Kappa Architecture, and Replay\n\nEvents in spacer are permanently stored in the specified storage (by default we use PostgreSQL as storage).\n\n## Libraries Search Path\n\nYou can require lua module under `app/` and `lib/` directly.\n\n```lua\n-- requiring app/foo.lua\nlocal m = require \"foo\"\n```\n\nDirectories can be used to organize your modules.\n\n```lua\n-- requiring app/models/foo.lua\nlocal m = require \"models/foo\"\n```\n\n## Contribute\n\nTo build spacer from source:\n\n```\n$ git clone git@github.com:poga/spacer.git\n$ make\n$ go install  // if you want to put it into your path\n```\n\n## License\n\n* `lib/luaunit.lua`: BSD. [https://github.com/bluebird75/luaunit](https://github.com/bluebird75/luaunit)\n* `lib/uuid.lua`: MIT. [https://github.com/thibaultcha/lua-resty-jit-uuid](https://github.com/thibaultcha/lua-resty-jit-uuid)\n* `lib/router.lua`: MIT. [https://github.com/APItools/router.lua](https://github.com/APItools/router.lua)\n* `lib/resty/hmac.lua`: BSD. [https://github.com/jkeys089/lua-resty-hmac](https://github.com/jkeys089/lua-resty-hmac)\n* `lib/resty/eve.lua, lib/resty/jwt-validators.lua, lib/resty/jwt.lua`: Apache License. [https://github.com/SkyLothar/lua-resty-jwt](https://github.com/SkyLothar/lua-resty-jwt)\n* Everything else: [MIT](./LICENSE)\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpoga%2Fspacer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpoga%2Fspacer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpoga%2Fspacer/lists"}