Foxx {#UserManualFoxx} ====================== @NAVIGATE_UserManualFoxx @EMBEDTOC{UserManualFoxxTOC} Foxx: Build APIs and simple web applications in ArangoDB{#UserManualFoxxIntro} ============================================================================== Foxx is an easy way to create APIs and simple web applications from within ArangoDB. It is inspired by Sinatra, the classy Ruby web framework. If Foxx is Sinatra, @ref UserManualActions are the corresponding `Rack`. They provide all the HTTP goodness. If you just want to install an existing application, please use the @ref UserManualFoxxManager. If you want to create your own application, please continue. So let's get started, shall we? Overview -------- The typical request to a Foxx application will work as follows (only conceptually, a lot of the steps are cached in reality): 1. The request is routed to a Foxx application depending on the mount point 2. The according controller of this application is determined (via something called the manifest file) 3. The request is then routed to a specific handler in this controller The handler will now parse the request. This includes determining all parameters from the body (which is typically JSON encoded) to the path parameters of the URL. It is then up to you to handle this request and generate a response. In this process you will probably access the database. This is done via the **Repository**: This is an entity that is responsible for a collection and specifically: 1. Creating new entries in this collection 2. Modify or delete existing entries in this collection 3. Search for entries in this collection To represent an entry in this collection it will use a **Model**, which is a wrapper around the raw data from the database. Here you can implement helper functions or simple access methods. Now let's get into the details. Creating the application files ------------------------------ An application built with Foxx is written in JavaScript and deployed to ArangoDB directly. ArangoDB serves this application, you do not need a separate application server. So given you want to build an application that sends a plain-text response "Hello YourName!" for all requests to `/dev/my_app/hello/YourName`. How would you achieve that with Foxx? First, create a directory `apps` somewhere in your filesystem. Let's assume from now on that the absolute path for this directory is `/home/user/apps`. After that, create a sub-directory `my_app` in the `apps` directory and save the following content in a file named `app.js` there: (function() { "use strict"; var Foxx = require("org/arangodb/foxx"), controller = new Foxx.Controller(applicationContext) controller.get("/hello/:name", function(req, res) { res.set("Content-Type", "text/plain"); res.body = "Hello " + req.params("name"); }); }()); Beside the `app.js` we need a manifest file. In order to achieve that, we create a file called `manifest.json` in our `my_app` directory with the following content: { "name": "my_app", "version": "0.0.1", "author": "me and myself", "controllers": { "/": "app.js" } } You **must** specify a name and a version number for your application, otherwise it won't be loaded into ArangoDB. You should now have the following files and directories with your application (starting at `/home/user` in our example): apps/ my_app/ manifest.json app.js This is your application. Testing the application ----------------------- Now your application is ready to be tested. Start ArangoDB as follows: $ arangod --javascript.dev-app-path /home/user/apps /tmp/fancy_db This will start the ArangoDB server in a **development mode** using the directory `/home/user/apps` as the workspace and `/tmp/fancy_db` as your database directory. In development mode the server automatically monitors the workspace and detects any change made to the files. Production application are installed using the Foxx manager and changes will not be reloaded automatically. Replace `/home/user/apps` with the apps path that you initially created. This is the path that you created the `my_app` directory in. Replace `/tmp/fancy_db` with the directory your database is located in. Now point your browser to `http://localhost:8529/dev/my_app/hello/YourName` and you should see "Hello YourName". After this short overview, let's get into the details. There are several example apps available on Github. You can install them via Foxx manager (covered in the chapter on Foxx manager) or simply clone them from `https://github.com/arangodb/`. Start with "hello-foxx" (`https://github.com/arangodb/hello-foxx`) as it contains several basic usage examples. "aye-aye" and "fugu" are more advanced apps showing how to use Backbone, Underscore and Jquery together with Foxx. foxx-authentication shows how to register users, login and check permissions. Handling Requests{#UserManualFoxxHandlingRequests} ================================================== In development mode all available applications from the application directory `/home/user/apps` are visible under `http://localhost:8529/dev/DIRNAME` where `DIRNAME` is the name of the directory of your application. When applications are installed in production mode, you can change the `/dev` prefix to whatever you like, see @ref UserManualFoxxManager. If you do not redefine it, all requests that go to the root of your application will be redirected to `index.html`. Details on FoxxController{#UserManualFoxxDetailsController} ============================================================= @copydetails JSF_foxx_controller_initializer HTTP Methods ------------ ### Get @copydetails JSF_foxx_controller_get ### Head @copydetails JSF_foxx_controller_head ### Post @copydetails JSF_foxx_controller_post ### Put @copydetails JSF_foxx_controller_put ### Patch @copydetails JSF_foxx_controller_patch ### Delete @copydetails JSF_foxx_controller_delete Documenting and constraining a specific route --------------------------------------------- If you now want to document your route, you can use JSDoc style comments (a multiline comment block with the first line starting with `/**` instead of `/*`) above your routes to do that: /** Get all Foxes * * If you want to get all foxes, please use this * method to do that. */ app.get("/foxes", function () { //... }); The first line will be treated as a summary (For optical reasons in the produced documentation, the summary is restricted to 60 characters). All following lines will be treated as additional notes shown in the detailed view of the route documentation. With the provided information, Foxx will generate a nice documentation for you. Furthermore you can describe your API by chaining the following methods onto your path definition: ### Path Param @copydetails JSF_foxx_RequestContext_pathParam ### Query Param @copydetails JSF_foxx_RequestContext_queryParam ### Body Param @copydetails JSF_foxx_RequestContext_bodyParam ### Error Response @copydetails JSF_foxx_RequestContext_errorResponse ### onlyIf @copydetails JSF_foxx_RequestContext_onlyIf ### onlyIfAuthenticated @copydetails JSF_foxx_RequestContext_onlyIfAuthenticated Documenting and constraining all routes of a controller ------------------------------------------------------- In addition to documenting a specific route, you can also do the same for all routes of a controller. For this purpose use the `allRoutes` object of the according controller. The following methods are available: ### Error Response @copydetails JSF_foxx_RequestContextBuffer_errorResponse ### onlyIf @copydetails JSF_foxx_RequestContextBuffer_onlyIf ### onlyIfAuthenticated @copydetails JSF_foxx_RequestContextBuffer_onlyIfAuthenticated Before and After Hooks ---------------------- You can use the following two functions to do something before or respectively after the normal routing process is happening. You could use that for logging or to manipulate the request or response (translate it to a certain format for example). ### Before @copydetails JSF_foxx_controller_before ### After @copydetails JSF_foxx_controller_after The Request and Response Objects -------------------------------- When you have created your FoxxController you can now define routes on it. You provide each with a function that will handle the request. It gets two arguments (four, to be honest. But the other two are not relevant for now): * The `request` object * The `response` object These objects are provided by the underlying ArangoDB actions and enhanced by the `BaseMiddleware` provided by Foxx. The Request Object ------------------ Every request object has the `path` method from the underlying Actions. This is the complete path as supplied by the user as a String. ### Body @copydetails JSF_foxx_BaseMiddleware_request_body ### Raw Body @copydetails JSF_foxx_BaseMiddleware_request_rawBody ### Params @copydetails JSF_foxx_BaseMiddleware_request_params The Response Object ------------------- Every response object has the body attribute from the underlying Actions to set the raw body by hand. You provide your response body as a String here. ### Status @copydetails JSF_foxx_BaseMiddleware_response_status ### Set @copydetails JSF_foxx_BaseMiddleware_response_set ### JSON @copydetails JSF_foxx_BaseMiddleware_response_json Details on FoxxModel{#UserManualFoxxDetailsModel} ================================================= The model doesn't know anything about the database. It is just a representation of the data as an JavaScript object. You can add and overwrite the methods of the prototype in your model prototype via the object you give to extend. In your model file, export the model as `model`. var Foxx = require("org/arangodb/foxx"); var TodoModel = Foxx.Model.extend({ }); exports.model = TodoModel; A Foxx Model can be initialized with an object of attributes and their values. There's also the possibility of annotation: The second hash you give to the extend method are properties of the prototype. You can define an attributes property there: var Foxx = require("org/arangodb/foxx"); var PersonModel = Foxx.Model.extend({ // Your instance properties }, { // Your prototype properties attributes: { name: { type: "string", required: true }, age: { type: "integer" }, active: { type: "boolean", defaultValue: true } }); exports.model = TodoModel; This has two effects: On the one hand it provides documentation. If you annotated your model, you can use it in the `bodyParam` method for documentation. On the other hand it will influence the behavior of the constructor: If you provide an object to the constructor, it will only take those attributes that are listed in the attributes object. This is especially useful if you want to to initialize the Model from user input. On the other hand it will set the default value for all attributes that have not been set by hand. An example: var person = new PersonModel({ name: "Pete", admin: true }); person.attributes // => { name: "Pete", active: true } ### Extend @copydetails JSF_foxx_model_extend ### Initialize @copydetails JSF_foxx_model_initializer ### Get @copydetails JSF_foxx_model_get ### Set @copydetails JSF_foxx_model_set ### Has @copydetails JSF_foxx_model_has ### Attributes @copydetails JSF_foxx_model_attributes ### forDB @copydetails JSF_foxx_model_forDB ### forClient @copydetails JSF_foxx_model_forClient Details on FoxxRepository{#UserManualFoxxDetailsRepository} =========================================================== A repository is a gateway to the database. It gets data from the database, updates it or saves new data. It uses the given model when it returns a model and expects instances of the model for methods like save. In your repository file, export the repository as `repository`. Foxx = require("org/arangodb/foxx"); TodosRepository = Foxx.Repository.extend({ }); exports.repository = TodosRepository; ### Initialize @copydetails JSF_foxx_repository_initializer ### Collection @copydetails JSF_foxx_repository_collection ### Prefix @copydetails JSF_foxx_repository_prefix ### ModelPrototype @copydetails JSF_foxx_repository_modelPrototype ### Save @copydetails JSF_foxx_repository_save ### Remove By Id @copydetails JSF_foxx_repository_removeById ### Remove By Example @copydetails JSF_foxx_repository_removeByExample ### Replace @copydetails JSF_foxx_repository_replace ### ReplaceById @copydetails JSF_foxx_repository_replaceById ### By Id @copydetails JSF_foxx_repository_byId ### By Example @copydetails JSF_foxx_repository_byExample ### First Example @copydetails JSF_foxx_repository_firstExample The Manifest File{#UserManualFoxxManifest} ========================================== In the `manifest.json` you define the components of your application. The content is a JSON object with the following keys: * `name`: Name of the application (Meta information) * `version`: Current version of the application (Meta information) * `description`: A short description of the application (Meta information) * `license`: Short form of the license (MIT, GPL...) * `author`: the author name * `contributors`: An array containing objects, each represents a contributor (with `name` and optional `email`) * `thumbnail`: Path to a thumbnail that represents the application (Meta information) * `repository`: An object with information about where you can find the repository: `type` and `url` * `keywords`: An array of keywords to help people find your Foxx app * `engines`: Should be an object with `arangodb` set to the ArangoDB version your Foxx app is compatible with. * `controllers`: Map routes to FoxxControllers * `lib`: Base path for all required modules * `files`: Deliver files * `assets`: Deliver pre-processed files * `isSystem`: Mark an application as a system application * `setup`: Path to a setup script * `teardown`: Path to a teardown script A more complete example for a Manifest file: { "name": "my_website", "version": "1.2.1", "description": "My Website with a blog and a shop", "thumnail": "images/website-logo.png", "controllers": { "/blog": "apps/blog.js", "/shop": "apps/shop.js" }, "lib": "lib", "files": { "/images": "images" }, "assets": { "application.js": { "files": [ "vendor/jquery.js", "assets/javascripts/*" ] } }, "setup": "scripts/setup.js", "teardown": "scripts/teardown.js" } The `setup` and `teardown` scripts ---------------------------------- You can provide a path to a JavaScript file that prepares ArangoDB for your application (or respectively removes it entirely). These scripts have access to `appCollection` and `appCollectionName`. Use the `setup` script to create all collections your application needs and fill them with initial data if you want to. Use the `teardown` script to remove all collections you have created. `controllers` is an object that matches routes to files ------------------------------------------------ * The `key` is the route you want to mount at * The `value` is the path to the JavaScript file containing the `FoxxController` you want to mount You can add multiple controllers in one manifest this way. The `files` ------------ Deliver all files in a certain folder without modifying them. You can deliver text files as well as binaries: "files": { "/images": "images" } The `assets` ------------ The value for the asset key is an object consisting of paths that are matched to the files they are composed of. Let's take the following example: "assets": { "application.js": { "files": [ "vendor/jquery.js", "assets/javascripts/*" ] } } If a request is made to `/application.js` (in development mode), the file array provided will be processed one element at a time. The elements are paths to files (with the option to use wildcards). The files will be concatenated and delivered as a single file. The content-type (or mime type) of the HTTP response when requesting `application.js` is automatically determined by looking at the filename extension in the asset name (`application.js` in the above example). If the asset does not have a filename extension, the content-type is determined by looking at the filename extension of the first file in the `files` list. If no file extension can be determined, the asset will be delived with a content-type of `text/plain`. It is possible to explicitly override the content-type for an asset by setting the optional `contentType` attribute of an asset as follows: "assets": { "myincludes": { "files": [ "vendor/jquery.js", "assets/javascripts/*" ], "contentType": "text/javascript" } } Development Mode ---------------- If you start ArangoDB with the option `--javascript.dev-app-path` followed by the path to a directory containing a manifest file and the path to the database, you are starting ArangoDB in development mode with the application loaded. This means that on every request: 1. All routes are dropped 2. All module caches are flushed 3. Your manifest file is read 4. All files in your lib folder are loaded 5. An app in DIRNAME is mounted at `/dev/DIRNAME` 6. The request will be processed This means that you do not have to restart ArangoDB if you change anything in your app. It is of course not meant for production, because the reloading makes the app relatively slow. Production Mode --------------- To run a Foxx app in production first copy your app code to the directory given in the config variable `--javascript.app-path`. After that use Foxx manager to mount the app. You can also use Foxx manager to find out your current app-path. Controlling Access to Foxx Applications --------------------------------------- Access to Foxx applications is controlled by the regular authentication mechanisms present in ArangoDB. The server can be run with or without HTTP authentication. If authentication is turned on, then every access to the server is authenticated via HTTP authentication. This includes Foxx applications. The global authentication can be toggled via the configuration option @ref CommandLineArangoDisableAuthentication "server.disable-authentication". Since ArangoDB 1.4, there is an extra option to restrict the authentication to just system API calls, such as `/_api/...` and `/_admin/...`. This option can be turned on using the @ref CommandLineArangoAuthenticateSystemOnly "server.authenticate-system-only" configuration option. If it is turned on, then only system API requests need authentication whereas all requests to Foxx applications and routes will not require authentication. This is recommended if you want your frontend to directly talk to your Foxx application. The other option is to turn of the admin and system API completely. If you need more fine grained control over the access to your Foxx application, we built an authentication system you can use (but you can of course roll your own if you want). Deactivate ArangoDB's authentication for your Foxx apps and then use Foxx.Authentication. Currently we only support cookie-based authentication, but we will add the possibility to use Auth Tokens and external OAuth providers in the near future. We also built a small [demo application](https://github.com/arangodb/foxx-authentication) for the authentication functionality. To use the authentication in your app, first activate it: @copydetails JSF_foxx_controller_activateAuthentication ### Adding a login route @copydetails JSF_foxx_controller_login ### Adding a logout route @copydetails JSF_foxx_controller_logout ### Adding a register route @copydetails JSF_foxx_controller_register ### Restricting routes To restrict routes, see the documentation for Documenting and Restraining the routes. Optional Functionality: FormatMiddleware ---------------------------------------- To use this plugin, please require it first: FormatMiddleware = require("org/arangodb/foxx/template_middleware").FormatMiddleware; This Middleware gives you Rails-like format handling via the `extension` of the URL or the accept header. Say you request an URL like `/people.json`: The `FormatMiddleware` will set the format of the request to JSON and then delete the `.json` from the request. You can therefore write handlers that do not take an `extension` into consideration and instead handle the format via a simple String. To determine the format of the request it checks the URL and then the `accept` header. If one of them gives a format or both give the same, the format is set. If the formats are not the same, an error is raised. Use it by calling: FormatMiddleware = require('foxx').FormatMiddleware; app.before(FormatMiddleware.new(['json'])); In both forms you can give a default format as a second parameter, if no format could be determined. If you give no `defaultFormat` this case will be handled as an error. Optional Functionality: TemplateMiddleware ------------------------------------------ To use this plugin, please require it first: TemplateMiddleware = require("org/arangodb/foxx/template_middleware").TemplateMiddleware; The `TemplateMiddleware` can be used to give a Foxx.Controller the capability of using templates. Currently you can only use Underscore Templates. It expects documents in the following form in this collection: { path: "high/way", content: "hello <%= username %>", contentType: "text/plain", templateLanguage: "underscore" } The `content` is the string that will be rendered by the template processor. The `contentType` is the type of content that results from this call. And with the `templateLanguage` you can choose your template processor. There is only one choice now: `underscore`. Which would set the body of the response to `hello Controller` with the template defined above. It will also set the `contentType` to `text/plain` in this case. In addition to the attributes you provided, you also have access to all your view helpers. ### Initialize @copydetails JSF_foxx_TemplateMiddleware_initializer ### Render @copydetails JSF_foxx_TemplateMiddleware_response_render