{"id":26586349,"url":"https://github.com/ironbay/riptide","last_synced_at":"2026-04-05T17:37:51.884Z","repository":{"id":48119080,"uuid":"250869577","full_name":"ironbay/riptide","owner":"ironbay","description":"Elixir framework for creating realtime applications","archived":false,"fork":false,"pushed_at":"2021-08-06T17:13:43.000Z","size":2319,"stargazers_count":17,"open_issues_count":6,"forks_count":4,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-02-22T06:02:48.138Z","etag":null,"topics":["elixir","framework","functional","realtime"],"latest_commit_sha":null,"homepage":"https://riptide.ironbay.co","language":"Elixir","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/ironbay.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":"2020-03-28T18:48:09.000Z","updated_at":"2025-01-16T00:19:30.000Z","dependencies_parsed_at":"2022-08-12T19:00:57.618Z","dependency_job_id":null,"html_url":"https://github.com/ironbay/riptide","commit_stats":null,"previous_names":["ironbay/riptide-next"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ironbay%2Friptide","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ironbay%2Friptide/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ironbay%2Friptide/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ironbay%2Friptide/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ironbay","download_url":"https://codeload.github.com/ironbay/riptide/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244972396,"owners_count":20540970,"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":["elixir","framework","functional","realtime"],"created_at":"2025-03-23T11:17:39.624Z","updated_at":"2026-04-05T17:37:46.864Z","avatar_url":"https://github.com/ironbay.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Most frameworks focus on plumbing\n\nThey ship with tools to define a REST API for your frontends, an ORM to transform objects into something your database understands, and if you're lucky, a pub/sub system for some light realtime functionality.\n\nAs your application evolves, your time becomes increasingly devoted to juggling these disparate systems and building the pipes that hold it all together.\n\nRiptide eases this burden by taking the plumbing - how data is moved around, transformed, and saved - and making it invisible. You focus exclusively on the logic of your application which can be implemented as simple cause and effect rules using Riptide's tooling.\n\nRiptide has been enormously productive for us but choosing a framework shouldn't be done without due dilligence. To make this easier, we put together [our best arguments against Riptide](/docs/caveats) and also [where our inspiration came from](/docs/inspiration) so you can make an informed decision.\n\n---\n\n## One data model — everywhere\n\nTraditional frameworks require you to think about your data in numerous ways:\n\n- Objects in your application\n- Relational tables in your database\n- Events in your message queue\n\nRiptide represents all of your data as one big tree no matter where you are: server, client or database\n\n```json\n{\n  \"users\": {\n    \"USR1\": {\n      \"key\": \"USR1\",\n      \"name\": \"Jack Sparrow\",\n    },\n    \"USR2\": {...},\n    \"USR3\": {...}\n  },\n  \"todos\": {\n    \"TOD1\": {\n      \"key\": \"TOD1\",\n      \"user\": \"USR1\",\n      \"created\": 1586068269822,\n      \"text\": \"Find the Black Pearl\",\n    }\n    ...\n  }\n}\n```\n\nData is modified by issuing Mutations. Mutations can merge new fields or delete existing ones. Clients can query and subscribe to parts of the tree they care about and that data will be kept in sync in realtime.\n\nTake a deep dive into [Mutations](/mutations), [Queries](/queries), and [Stores](/stores)\n\n---\n\n## Composable logic\n\nRiptide Interceptors let you define simple rules using Elixir's pattern matching that trigger conditionally when data is written or read. Take the following Mutation that creates a new Todo.\n\n```json\n{\n  \"merge\": {\n    \"todos\": {\n      \"TOD2\": {\n        \"key\": \"TOD2\",\n        \"text\": \"Return cursed Aztec gold\"\n      }\n    }\n  }\n}\n```\n\nIt will trigger the following Interceptor which effectively says whenever a Todo is created, record the current time and user who created it.\n\n```elixir\ndefmodule Todo.Created do\n  use Riptide.Interceptor\n\n  def before_mutation(\n        # Match path being written to\n        [\"todos\", _key],\n        # Match fields being merged\n        %{ merge: %{ \"key\" =\u003e _ }},\n        # Full mutation\n        _full,\n        # Connection state\n        state\n      ) do\n    {\n      :merge,\n      %{\n        \"created\" =\u003e :os.system_time(:millisecond),\n        \"user\" =\u003e state.user\n      }\n    }\n  end\nend\n```\n\nThis results in the following data being written\n\n```json\n{\n  \"todos\": {\n    \"TOD2\": {\n      \"key\": \"TOD2\",\n      \"text\": \"Return cursed Aztec gold\",\n      \"created\": 1586068269822,\n      \"user\": \"USR1\"\n    }\n  }\n}\n```\n\nInterceptors are simple but powerful. Even the most complex business logic can be broken down into a collection of composable, independent and easily digestable Interceptors.\n\n[Learn more about the various interceptors available in Riptide](/interceptors)\n\n---\n\n## Realtime by default\n\nRiptide takes care of shipping data around and ensuring all observers are kept up to date with the latest changes. This happens automatically - there's no pub/sub system to setup or event triggers and handlers to define.\n\n```javascript\n// React example\nRiptide.remote.query_path([\"todos\"], { subscribe: true })\n\nfunction render() {\n  return (\n    \u003cul\u003e\n    {\n      Riptide.local\n        .query_values([\"todos\"])\n        .map(item =\u003e (\n          \u003cli key={item.key}\u003e{item.text}\u003c/li\u003e\n        ))\n    }\n    \u003c/ul\u003e;\n  )\n}\n```\n\nInstead of being an after thought, 100% of the UIs you build will be realtime by default.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fironbay%2Friptide","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fironbay%2Friptide","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fironbay%2Friptide/lists"}