Skip to content
This repository has been archived by the owner on Sep 18, 2021. It is now read-only.
/ exile Public archive

πŸ–₯️ ⚑ πŸ–₯️ A Realtime Database-as-a-Service with REST-style resource location and Time Traveling Feature support. Spawnfest 2019 project winner πŸ†

Notifications You must be signed in to change notification settings

spawnfest/exile

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

70 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Exile

A Realtime Database-As-A-Service with REST style resource location and Time Traveling Feature support.

See it live @ https://exile-web.gigalixirapp.com

Philosophy

An extremely simple to use NoSQL store which follows REST-like resource locators known as paths to locate records and define relations between data.

Getting Started

Development

Run Tests

mix tests

Run Dialyzer

mix dialyzer

Generate and View Docs

mix docs && open doc/index.html

Run App Locally

mix do deps.get, compile
(cd apps/exile_web/assets && npm install)
iex -S mix phx.server
open http://localhost:4000

πŸŽ‰ You will be presented with an interact REPL to play with the RT Database via JS API!

πŸŽ‰ You can also interact directly with the store using the Exile Elixir API Directly via the IEX session. See Docs: doc/Exile.html#content

Real Time Database πŸ–₯️ ⚑ πŸ–₯️

  • Goto https://exile-web.gigalixirapp.com/ (or run locally)
  • Copy the URL (which contains the sandbox token) into another browser window
  • Click subscribe which will listen for changes on the "posts" domain path
  • Create posts in the other window
  • See the Magic ✨ as the changes are relayed to all subscribers

An example scenario:

Using Exile to create a blog completely in client JS, submit and edit posts and have them visible to all sessions, and have comments instantly appear in a live fashion in all sessions, WITHOUT ANY SERVER SIDE CODE 😍

API

POST

Create Records

elixir

Exile.post("posts", {author: "holsee", title: "Hello World", body: "...", comments: []})
#=> {:ok, "614e1637-5af1-4bd4-8432-e5ccd3b5cd90"}

javascript

exile.post('posts', {author: 'holsee', title: 'Hello World', body: '...', comments: []})
//=>
{
  reference: 'posts',
  result: 'ok',
  value: '04c7fecd-9e3f-408d-8799-fef8d120d93c'
}
Nested

Create a nested Record

elixir: create a comment on a post

Exile.post("posts/#{post_id}/comments", %{author: "evadne", body: "nice!"})
#=>
{:ok, "cd3c2478-cc50-4010-b54c-7e6e2f534695"}

javascript: create a comment on a post

exile.post(`posts/${post_id}/comments`, {author: 'evadne', body: 'nice!'})
//=>
{
  reference: 'posts/04c7fecd-9e3f-408d-8799-fef8d120d93c/comments',
  result: 'ok',
  value: 'a7cc9516-59d0-4095-970d-25eeb7d0b5be'
}

GET

πŸ”₯ Using REST style locator paths you are able to dig into resources which have been dynamically created! πŸ”₯

elixir: get all posts

Exile.get('posts')
# =>
{:ok,
 [
   %{
     id: "88d903d4-4b7e-4735-926d-a54bce4b94cf",
     ts: 1569186483608933000,
     value: %{
       "author" => "holsee",
       "comments" => [
         %{
           id: "cd3c2478-cc50-4010-b54c-7e6e2f534695",
           ts: 1569186483608929000,
           value: %{"author" => "bran", "body" => "Lorem ipsum"}
         }
       ],
       "tags" => ["bill", "ted", "rufus"]
     }
   }
 ]}

elixir: get a specific comment

Exile.get("posts/#{post_id}/comments/#{comment_id}")
#=>
{:ok, %{"author" => "bran", "body" => "Lorem ipsum"}}

elixir: get a specific comment author

Exile.get("posts/#{post_id}/comments/#{comment_id}")
#=>
{:ok, "bran"}

javascript: get all posts

exile.get('posts')
//=>
{
  reference: 'posts',
  result: 'ok',
  value: [
    {
      id: '4874b3dd-6f67-48dd-8b3f-182ead6362a6',
      ts: 1569186089957471700,
      value: {
        author: 'holsee',
        body: '...',
        comments: [
          {
            id: '6748759c-896b-4e5b-93e9-3f49fa4d06cd',
            ts: 1569186089957465000,
            value: {
              author: 'evadne',
              body: 'nice!'
            }
          }
        ],
        title: 'Hello World'
      }
    }
  ]
}

javascript: get all comments on a post

exile.get('posts/${post_id}/comments')
//=>
{
  reference: 'posts/4874b3dd-6f67-48dd-8b3f-182ead6362a6/comments',
  result: 'ok',
  value: [
    {
      id: '6748759c-896b-4e5b-93e9-3f49fa4d06cd',
      ts: 1569186089957465000,
      value: {
        author: 'evadne',
        body: 'nice!'
      }
    }
  ]
}

PUT

Unlike POST operations which create new records, PUT allows you to update values at a locator path.

elixir: update post body

Exile.put("posts/#{post_id}/body", "new body!")
# => :ok

Exile.get("posts/#{post_id}/body")
# =>
{:ok, "new body!"}

DELETE

You can also delete values at the path:

elixir: delete all posts

Exile.delete("posts")
# =>
:ok

javascript: delete all posts

exile.delete('posts')

Subscribe to path (Realtime features πŸ–₯️ ⚑ πŸ–₯️)

elixir: subscribe to posts, get update event

subscriber_address = self()
Exile.subscribe("posts", subscriber_address)
#=>
:ok

#when post is created or updated receive event:
flush()
#=>
{:exile_event,
 {:update, "posts/e5373a79-8b76-464f-b72a-8182a1ed6230/body",
  {"e5373a79-8b76-464f-b72a-8182a1ed6230", 1569187403304193274,
   %{
     "author" => "holsee",
     "body" => "new body!",
     "comments" => [
       %{
         id: "787b20f6-643f-454d-9a87-f9fd985b4458",
         ts: 1569186933359652898,
         value: %{"author" => "bran", "body" => "Lorem ipsum"}
       }
     ],
     "tags" => ["bill", "ted", "rufus"]
   }}}}

javascript: subscribe to posts, get new item event

exile.subscribe('posts')
#=>
{
  result: 'ok'
}

# when post is created receive event
#=>
{
  event_type: 'new',
  path: '37469d37-21f9-4ea5-b2a9-ebe136d8c3cc:posts',
  record: {
    id: 'd9f0db63-2f3d-422d-b9d7-bccc549d3e65',
    timestamp: 1569187287039046000,
    value: {
      comments: [],
      title: 'Hello World'
    }
  }
}

Time Traveling πŸ•“πŸ•’πŸ•‘

At a storage level records are immutable and every change results in a new version. By default the latest version is returned.

There is no public API support for this (ironically we ran out of time πŸ₯πŸ€£) but as we have the foundations in place as detailed, we envision an API like the following being completely possible:

get last 10 revisions of a post:

exile.get('posts/${post_id}', {last: 10})

get versions between timestamps:

exile.get('posts/${post_id}', {from: 1569186663, to: 1569187763})

About

πŸ–₯️ ⚑ πŸ–₯️ A Realtime Database-as-a-Service with REST-style resource location and Time Traveling Feature support. Spawnfest 2019 project winner πŸ†

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •