Live Demo

Hello World Demo

Fetching resources served with Resgate can either be done through HTTP(s):

Or by using ResClient, a javascript library that gets both data and updates through WebSocket:

<pre id="output"></pre>

  
const ResClient = resclient.default;

// Creating a client instance.
let client = new ResClient('wss://api.resgate.io');

// Get the resource from the service.
// Try changing 'example.model' to 'example.users'.
client.get('example.model').then(data => {
  let json = JSON.stringify(data, null, 2);
  output.textContent = json;
}).catch(err => {
  output.textContent = err.message;
});

Edit Text Demo

This sandboxed version of the Edit Text Example lets you edit and synchronize a text between multiple browser clients.

<p>Try running the demo in two separate tabs to observe client synchronization.</p>
<input id="input"></input>
<div id="err"></div>

  
const ResClient = resclient.default;

// Creating the client instance.
let client = new ResClient('wss://api.resgate.io');

// Get sandbox ID then from localStorage,
// or generate and store a random one.
let sandbox = localStorage.getItem('sandboxId')
  || Math.random().toString(36).substr(2, 8);
localStorage.setItem('sandboxId', sandbox);

// Get the model from the service.
client.get(`demo.${sandbox}.model`).then(model => {
  // Create an input element
  let input = document.getElementById('input');
  input.value = model.message;

  // Call set to update the remote model
  input.addEventListener('input', () => {
    model.set({ message: input.value });
  });

  // Listen for model change events.
  // The model will be unsubscribed after calling model.off
  model.on('change', () => {
    input.value = model.message;
  });
}).catch(err => {
  document.getElementById('err').textContent = err.message;
});

Book Collection Demo

A sandboxed version of the Book Collection Example, using Modapp for component rendering. React and Vue.js versions of the example client are available as well.

Run the client in two separate browser tabs to observe real time client synchronization.

<header class="shadow">
  <h1>Book Collection Demo</h1>
  <button id="reset">Reset</button>
</header>
<div class="top">
  <div class="new-container">
    <label for="new-title"><span>Title</span><input id="new-title" /></label>
    <label for="new-author"><span>Author</span><input id="new-author" /></label>
    <button id="add-new">Add</button>
  </div>
  <div id="error-msg"></div>
</div>
<hr>
<div id="books"></div>
body {
  background: #eee;
  font-family: Arial, Helvetica, sans-serif;
  padding: 0;
  margin: 0;
}
header {
  background: #000;
  color: #fff;
  padding: 16px 1em;
}
h1 {
  margin: 0;
  padding: 0;
  line-height: 32px;
  font-size: 24px;
}
.top {
  margin: 1em 1em;
}
.shadow {
  box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
}
#books {
  max-width: 800px;
  margin: 0 1em;
}
.new-container {
  margin: 1em 0;
  line-height: 2em;
}
.new-container label {
  margin-right: 10px;
}
label {
  display: inline-block;
  font-weight: bold;
}
label span {
  display: inline-block;
  width: 64px;
}
#error-msg { color: #800 }
button {
  display: inline-block;
  border: none;
  background: none;
  color: #006;
}
button:hover { background: rgba(0,0,0,0.12); }
#reset {
  position:absolute;
  right: 20px;
  top: 24px;
  color: #fff;
}
#reset:hover { background: rgba(255,255,255,0.25); }
.list-item { padding: 8px 0; }
.card {
  background: #fff;
  padding: 1em 1em;
  box-sizing: border-box;
}
.action { float: right }
.editing > .card { background: #eaeaff; }
.edit { display: none; }
.editing .edit { display: inherit; }
.editing .view { display: none; }
.card h3 { margin: 0 0 8px 0; }
.card .author { font-style: italic; }
.edit-input { display: inline-block; }
.edit-input label { display: block; }
.edit-input label + label { margin-top: 6px; }
const { Elem, Txt, Button, Input } = window["modapp-base-component"];
const { CollectionList, ModelTxt } = window["modapp-resource-component"];
const ResClient = resclient.default;

// Creating the client instance.
let client = new ResClient('wss://api.resgate.io');

// Get sandbox ID then from localStorage, or generate a random one.
let sandbox = localStorage.getItem('sandboxId')
  || Math.random().toString(36).substr(2, 8);
localStorage.setItem('sandboxId', sandbox);

// Error handling
let errMsg = new Txt();
let errTimer = null;
errMsg.render(document.getElementById('error-msg'));
let showError = (err) => {
  errMsg.setText(err && err.message ? err.message : String(err));
  clearTimeout(errTimer);
  errTimer = setTimeout(() => errMsg.setText(''), 7000);
};

// Add reset click callback
document.getElementById('reset').addEventListener('click', () => {
  client.call(`library.${sandbox}.books`, 'reset').catch(showError);
});

// Add new click callback
document.getElementById('add-new').addEventListener('click', () => {
  let newTitle = document.getElementById('new-title');
  let newAuthor = document.getElementById('new-author');
  client.call(`library.${sandbox}.books`, 'new', {
    title: newTitle.value,
    author: newAuthor.value
  }).then(() => {
    // Clear values on successful add
    newTitle.value = "";
    newAuthor.value = "";
  }).catch(showError);
});

// Get the collection from the service.
client.get(`library.${sandbox}.books`).then(books => {
  // Here we use modapp components to render the book list.
  // It is a protest against all these frameworks with virtual DOMs.
  // Why use virtual DOMs when it is faster with vanilla javascript?
  new Elem(n =>
    n.component(new CollectionList(books, book => {
      let c = new Elem(n =>
        n.elem('div', { className: 'list-item' }, [
          n.elem('div', { className: 'card shadow' }, [
            n.elem('div', { className: 'view' }, [
              n.elem('div', { className: 'action' }, [
                n.component(new Button(`Edit`, () => {
                  c.getNode('titleInput').setValue(book.title);
                  c.getNode('authorInput').setValue(book.author);
                  c.addClass('editing');
                })),
                n.component(new Button(`Delete`, () => books.call('delete', { id: book.id }).catch(showError)))
              ]),
              n.elem('div', { className: 'title' }, [
                n.component(new ModelTxt(book, book => book.title, { tagName: 'h3'}))
              ]),
              n.elem('div', { className: 'author' }, [
                n.component(new Txt("By ")),
                n.component(new ModelTxt(book, book => book.author))
              ])
            ]),
            n.elem('div', { className: 'edit' }, [
              n.elem('div', { className: 'action' }, [
                n.component(new Button(`OK`, () => {
                  book.set({
                    title: c.getNode('titleInput').getValue(),
                    author: c.getNode('authorInput').getValue()
                  })
                    .then(() => c.removeClass('editing'))
                    .catch(showError);
                })),
                n.component(new Button(`Cancel`, () => c.removeClass('editing')))
              ]),
              n.elem('div', { className: 'edit-input'}, [
                n.elem('label', [
                  n.component(new Txt("Title", { className: 'span' })),
                  n.component('titleInput', new Input())
                ]),
                n.elem('label', [
                  n.component(new Txt("Author", { className: 'span' })),
                  n.component('authorInput', new Input())
                ])
              ])
            ])
          ])
        ])
      );
      return c;
    }, { className: 'list' }))
  ).render(document.getElementById('books'));
}).catch(err => showError(err.code === 'system.connectionError'
  ? "Connection error. The demo service seems to be unreachable."
  : err
));

Tip

Add, edit, and delete as much as you like. The list of books will be reset 15 min after last modification.
You can manually trigger a reset by clicking the Reset button.