This documentation is not yet done.


You'll need the following tools installed on your computer

npm install foxxy -g

You'll have to update your ~/.foxxrc file with 


Get Started

Fasty is based on Openresty, Lua & ArangoDB. You'll have the full stack installed via Docker. Pull the repository and inside it run :

docker-compose build
docker-compose up cms

The application should run on and . The database is running on (login : root, password: password)

You'll need to create first a database called db_demo and run within the foxxy folder

foxxy upgrade settings --server fasty --database db_demo
foxxy upgrade --server fasty --database db_demo

You should be able now to access the admin URL with the following credentials : 

[email protected] :: 977cebdd


Layouts allow you to define the global template of your application. You can have multiple layouts. Each pages can be linked to any layout.

<!DOCTYPE html>
  <head lang="{{ lang }}">
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link rel="stylesheet" href="@css_vendors" />
    <link rel="stylesheet" href="@css" />
    {{ partial | nav }} 
    {{ partial | footer }}
    <script src='@js_vendors'></script> 
    <script src='@js'></script> 

In this basic sample we can see interesting things :

@css & @css_vendors are CSS content defined in the layout
@js & @js_vendors are JS content defined in the layout

@headers will be replaced by generated headers

will display the content of your page

you can also notice the en to display the current language & some {{ partials | ... }} to display shared parts

@css_vendors :

{{ external | }}
{{ external | }}


{{ external | }}
{{ external | }}
{{ external | }}

You can define as many external resources you need.


Pages are the pages of your website. You can create as many pages you want.

Notice that the slug must be unique across all pages. The default url schema is : /:lang/:all/:slug(/*)

:lang is the current lang (e.g. en, fr, etc.)
:all can be anything you want (e.g. -, my, anything, useful, etc.)
is your page's slug (e.g. home, page, etc)

Any additional path element will be stored in the splat params.
Sample : /en/my/home/page/10

splat params will looks like { page: 10 }



Display any page content via it's slug

{{ page | <slug_or_field> (| <dataset>) }}

{{ page | my_famous_page }} << Display page content of the my_famous_page page

{{ page | slug | posts }} << This one will display the html field from your posts dataset using the slug defined in the URL


Display partials via it's slug

{{ partial | <partial_name> (| <dataset> | <options>) }}

But a partial can have a dataset (arango, rest) and you can also use do_not_eval as dataset if you just want to include etlua file without evaluating it directly.

{{ partial | demo | arango | aql/FOR user IN users RETURN user }}


Display the current lang

{{ lang }}


Display an elemnt set in the settings/home section (json) 

{{ settings | <mailgun_api> }} will fetch data from json data


DIsplay a single page application

{{ spa | <my-app> }}


Mount a riot tag.

{{ riot | tag1 }} Load the widget but don't mount it

{{ riot | tag1 | mount }} Load and mount the widget

{{ riot | tag1#tag2 }} Load tag1 & tag2 (reduce http calls)


Display a translated text. If the key don't exist it will create an entry in the traductions list.

{{ tr | welcome }}

It will display a missing message if the translation is not existing:  Missing translation welcome


Display a value from the splat params (see pages section)

{{ splat | <splat_key> }}


Display a helper (AQL + partial). You can now add options

{{ helper | <helper_name> (| key1#value1#key2#value2...) }}


If you need to execute a request just use

{{ aql | <your_aql_request> }}


You can define a request per page to define headers dynamically. 

Let say we have this AQL request defined : 

FOR doc IN datasets
  FILTER doc.type == "articles" AND doc.slug == @slug
  RETURN { title: doc.title }

You could use then this shortcut in any header fields to get the content.

{{ og_data | title }}


To display a content from a dataset within the page partial.

{{ html | <key> | <field> }}

You can also use the og:aql request to get the json object to be transformed within the page partial

{{ html | <key_defined_in_og:aql> }}


Components are JS components (RiotJS for now)


  <!-- layout -->
  <h3>{ opts.title }</h3>

    <li each={ item, i in items }>{ item }</li>

  <form onsubmit={ add }>
    <input ref="input">
    <button>Add #{ items.length + 1 }</button>

  <!-- style -->
    h3 {
      font-size: 14px;

  <!-- logic -->
    this.items = []

    add(e) {
      var input = this.refs.input
      input.value = ''



Partials are .etlua files ... It can be a simple raw HTML content or can embed some logic via LUA code.

You can call a partial with a specific dataset (AQL request, or external JSON data)

<div class="field is-grouped is-grouped-multiline">
  <% for k, item in pairs(dataset.results) do %>
  <div class="control">
    <div class="tags has-addons">
      <span class="tag"><%=[lang] %></span>
      <span class="tag is-primary"><%= item.state[lang] %></span>
  <% end %>

With the following AQL request

FOR doc IN datasets
FILTER doc.type == 'features'
SORT doc.order ASC

You can define a helper called features to load this partial & AQL and then call this helper with {{ helper | features }}

Single Page Applications

Create as many SPA you want. An application has on HTML tag to load components and one JS tag to set the router
{{ riot | account#plans#tchins#offer_tchins#tchins_history#friends#pending_invitations#pending_approvals#blocked }}

<div id="app"></div>
document.addEventListener('DOMContentLoaded', function() {
  route('/', function(name) { riot.mount('div#app', 'account') })
  route('/plans', function(name) { riot.mount('div#app', 'plans') })
  route('/tchins', function(name) { riot.mount('div#app', 'tchins') })
  route('/offer_tchins', function(name) { riot.mount('div#app', 'offer_tchins') })
  route('/tchins_history', function(name) { riot.mount('div#app', 'tchins_history') })
  route('/friends', function(name) { riot.mount('div#app', 'friends') })
  route('/pending_invitations', function(name) { riot.mount('div#app', 'pending_invitations') })
  route('/pending_approvals', function(name) { riot.mount('div#app', 'pending_approvals') })
  route('/blocked', function(name) { riot.mount('div#app', 'blocked') })


Store your AQL requests and then use them within Helpers

Datatypes & Datasets

Datatypes allow you to create dynamic forms for the content editors.

  "model": [
    { "r": true, "c": "1-1", "n": "name", "t": "string", "j": "joi.string().required()", "l": "Name", "tr": true },
    { "r": true, "c": "1-1", "n": "state", "t": "string", "j": "joi.string().required()", "l": "State", "tr": true }
  "columns": [ { "name": "name", "tr": true } ],
  "sortable": true

Once a dataset defined; a new dataype will appear and you'll be able to create & manage your data


Choose a partial, a request ... Then you can call it anywhere you want using the helper shortcut


Redirections allow you to define a specific route for a specific helper

API Builder

The API Builder allow you to write your Foxx service directly via Fasty. You can also deploy it with one click !

Server side scripts

You can edit your Nodejs server side scripts and restart them directly from Fasty. You'll have first to set the docker-compose file :
    context: .
    dockerfile: Dockerfile_node
  command: /bin/bash -c "yarn && yarn start"
  restart: always
    - 8000:8000
    - ./scripts/your_folder/chatroom/:/workspace
    - arangodb:arangodb