All Tabilet projects consist of

  • roles: defines access and restrictions around who can login and access different areas in the system
  • components: basic building blocks or object classes, usually tied to database tables.
After creating roles and components, which are generated by answering a few questions and selecting preferred security rules, your project can be spun up. You can download the whole package in your favored programming language i.e. PHP, GO, Java and Perl, and install it in a cloud or on-primise server. From there, it will automatically start to serve REST, GraphQL or gRPC API!

Additionally, a HTML5 website using server-side templates, and a one-page website using Vuejs are also generated for you to use. The latter can be built out into a mobile app.

Security is a huge priority at Tabilet. We protect your data in four ways:

  • Authentication and authorization of all logins,
  • Access Control List for different roles
  • CSRF (Cross-Site Request Forgery) tokens to prevent cheat POST,
  • RLS (Row Level Security) with signed reference key.

You are in control:

Furthermore, because you download the source code and set up all security tokens on your own server, our service is completely separated from your instance and any future development.


URL Format

In Tabilet, URLs use the fixed format:


  • HANDLER: name for handler. A web project should use one and only one handler.
  • ROLE: name for a group of visitors, e.g. public or member. Database account authentication and OAuth.
  • MIME: the mime type of pages, e.g. json and html. Switching mime will turn a page from one mime format to another. For example, to get the JSON view of the same HTML page, replace html by json in the URL.
  • COMPONENT: an object or class name, usually mapping to a database table.
  • action=STRING: defines action on the component. If it is missing, a REST verb will be taken from the HTTP Method:
    • GET: action=topics, list all records
    • GET with ID (http://WEBSITE/handler/role/mime/component/ID): action=edit, read one record by the ID
    • POST: action=insert, create a new record
    • PUT: action=update, update a record
    • DELETE: action=delete, delete a record

    Explicit input of action=STRING will take over the REST behavior.

    Every component inherits the 5 REST verbs from the Genelet library, so you don't need to program them again. In most cases, the inheritances alone are all that is needed for data processing. If needed however, you can always create or override an action myVerb, by simply making function (or method) named myVerb in the class.

  • query: additional query parameters.

A normal API has always the URL format:


where action may be replaced by HTTP Request Method, and query could be in the HTTP Body.

This will return:

  • HTTP code 3xx, 4xx or 5xx, indicating a system redirect, error or downtime.
  • HTTP code 200, with JSON body

    {"success":false, "error_code":CODE, "error_string":STRING}

    indicating an application error and reason.

  • HTTP code 200, with JSON body

    {"success":true, "data":ARRAY, "incoming":HASH, "included":HASH_DATA}

    indicating a successful request. The main data is returned in ARRAY (of hash), and supplementary ones in HASH_DATA (a hash of data array). For your convenience, the incoming query is also returned in HASH.


To login to role ROLE, send a POST to


with the username and password in the body. If successful, you will receive the access token in the cookie. You should attach the token in future requests in the cookie header or as a variable.


This is only necessary in the web flow. Sending GET to the following will expire the browser cookie and enable logout.


Client Programming


Inside the document root, there is a Javascript client genelet.js which can be used to access the API.

var $scope = new Genelet({
	handler: HANDLER, // API handler name, e.g. "/app.php"
	mime: MIME,       // template file type, e.g. "vue"

The reeturned API data will be saved as values of the following 4 keys

  • names: the main data, as an array of objects
  • single: an object usually the first element of names, returned from insert, update and delete
  • OBJECT: supplementary data, key value pairs with values as array of objects.
Function go()

$scope.go(role, component, action, query, landing)

Navigates to the page defined by {role, component, action, query}:

<a href=”” onclick=”$scope.go(role, component, action, query, landing)”>this link</a>

How the browser handles it depends on landing:

  • null: It simulates the traditional anchor, i.e. jump to new page:


  • STRING: No anchor jump nor page refresh. The returned data is saved as the value of the key landing.
  • {operator: STRING, id_name: STRING}: Change the existing data on the spot, without refreshing the page. operator must be "insert", "update" or "delete". id_name is the primary key of the component.
  • {role: string, component: string, action: string, query: object}: After running the original API, it will redirect to the following, second page: {landing["role"], landing["component"], landing["action"], landing["query"]}.

    For example, if you were developing a flow to redirect a user who updates her credit card back to the original payment page, you would need two API calls for this click: (1) update her credit card and (2) refresh the payment page, presumably with the payment information on it.

Function send()

$scope.send(role, component, action, query, landing)

This function is the same as $scoe.go(), except that it uses POST.

Function login()

$scope.login(role, component, action, query, provider)

login to role using the username and password in query. After a successful login, it will land on the page defined by {role, component, action}.

Function start()

Dynamic one-page websites can be served this way


where ROLE, COMPONENT and ACTION represent the page (and API). To launch it, call the following function immediately after constructing $scope:

$scope.start(role, component, action)

where role, component and action are the defaults when ROLE, COMPONENT and ACTION are missing in the URL.

With these, your Javascript client can use the 4 functions above to navigate from one page to another. Retrieving templates and displaying HTML5 using the AP data depends on the Javascript framework.

Using Vuejs

Here is a sample page to use start() and Vuejs:

<!doctype html>
<html lang="en">
    <script src=""></script>
    <script src=""></script>
    <script src="/genelet.js"></script>

<div id="app">
<component v-bind:is="headerComponent"></component>
<component v-bind:is="currentComponent" v-bind:names="names"></component>
<component v-bind:is="footerComponent"></component>

var $scope = new VueGenelet({
    handler: "/app.php", html: "vue",
$scope.start("p", "question", "topics");

var vm = new Vue({
  el: '#app',
  data : $scope,
  components: {
    'a-header': httpVueLoader('./a/header.vue'),
    'a-footer': httpVueLoader('./a/footer.vue'),
    'a-login': httpVueLoader('./a/login.vue'),
    'a-choice-startnew': httpVueLoader('./a/choice/startnew.vue'),
    'a-choice-topics': httpVueLoader('./a/choice/topics.vue'),
    'a-choice-edit': httpVueLoader('./a/choice/edit.vue'),
    'a-question-startnew': httpVueLoader('./a/question/startnew.vue'),
    'a-question-edit': httpVueLoader('./a/question/edit.vue'),
    'a-question-topics': httpVueLoader('./a/question/topics.vue'),
    'p-header': httpVueLoader('./p/header.vue'),
    'p-footer': httpVueLoader('./p/footer.vue'),
    'p-question-topics': httpVueLoader('./p/question/topics.vue'),
    'p-question-edit': httpVueLoader('./p/question/edit.vue'),
    'p-choice-topics': httpVueLoader('./p/choice/topics.vue'),

vm.$scope = $scope;



It creates object $scope and passes it to Vuejs as a global variable. All Vuejs components with HTML5 templates and scripts are preloaded for future usage. Calling those Vuejs components and API and adjusting the URL are both handled by the 4 Genelet functions and Vuejs above.

Server Development

About Auto-generated Code

The 5 standard REST verbs on components can handle the majority of all business needs.

Tabilet allows you to jump between components and actions, internally called nextpages, very similarly to GraphQL. So Tabilet essentially issues its own GraphQL schema, enabling clients to send more complex queries and get customized data, that is superior to REST.

Occasionally, you may need to make new actions other than REST, or override an existing one. Here's how to do server programming in PHP:

Development Flow
Model: model.php
Controller: filter.php
View: templates
Privacy Policy       Terms of Use