Frontend-Backend Protocol

Applin frontends and backends communicate with JSON and HTTP. The protocol incorporates the core concepts of REST.

Pages and URLs

Every Applin app is accessible at a particular base URL with the form https://HOST/PATH. The URL must not contain query or fragment components.

Every Applin app page has a URL. All page URLs are relative to the base URL. The default "home page" is at relative URL /.

Applin frontends display pages only from the base URL. They do not fetch pages from other servers or URLs. To display content from another server, have your server fetch the content.

For example, if your app's base URL is https://apps.example.com:1234/travel then the page /hotels has URL https://apps.example.com:1234/travel/hotels.

Frontend Role

Every Applin frontend is a program that makes requests to a particular Applin server, receives page specifications from the server, and provides functions for interacting with pages and navigating between pages.

Request types:

  • GET for a page the frontend has not previously requested
    • Request headers
  • GET to refresh a page that has no variables
    • Request headers
      • Accept: application/vnd.applin_response
  • POST to refresh a page that has variables (user-input widgets).
    • Request headers
      • Accept: application/vnd.applin_response
      • Content-Type: application/json request header
    • Request body is a JSON object with the current page's variables.
  • POST for an rpc action on a button.
    • Request headers
      • Content-Type: application/json request header
    • Request body is a JSON object with the current page's variables.
  • GET for page content (images, etc.)

Server Role

Every Applin server is an HTTP server that handles requests.

  • Requests for pages, when request has the Accept: application/vnd.applin_response header
    • Response headers
      • Content-Type: application/vnd.applin_response
      • Response code: 200 OK
      • Response body is a JSON object with the format described below.
      • Do not return 4xx errors for bad user input. Instead, display problems on the page.
  • Form POST (without Accept: application/vnd.applin_response)
    • Response code: 200 OK
    • No response body
    • When the request is missing a required variable, or a variable has the wrong type
      • Response code: 400 Bad Request
      • Response headers: Content-Type: text/plain
      • Response body: technical details about the problem
    • When the user entered data that is unacceptable
      • Response code: 422 Unprocessable Content
      • Response headers: Content-Type: text/plain
      • Response body: a message for the user
  • When the user is not logged in (session cookie missing or invalid) or does not have permission to view the page
    • Response code: 403 Forbidden
    • Response headers: Content-Type: text/plain
    • Response body: a message for the user
  • When the server failed to process the request
    • Response code: 500 Internal Server Error
    • Response headers: Content-Type: text/plain
    • Response body: technical details about the problem
  • When the server is overloaded
    • Response code: 503 Service Unavailable
    • No response body
    • The frontend will retry with backoff.

Applin frontends receive and send cookies like web browsers. Servers can set and receive cookies for session tokens. See Cookies on MDN.

Applin Request Format

The application/json content-type is a JSON object encoded in a UTF-8 string. It contains key-value pairs for all data-entry widgets on the page.

Applin Response Format

The application/vnd.applin_response content-type is a JSON object encoded in a UTF-8 string. It must include these keys:

  • page is an Applin page specification

Example Exchange

Frontend request:

GET /new_account HTTP/1.1

Server response:

HTTP/1.1 200 OK
Content-Type: application/vnd.applin_response
{
  "page": {
    "typ": "nav_page",
    "title": "New Account",
    "widget": {
      "typ": "scroll",
      "widget": {
        "typ": "form",
        "widgets": [
          {
            "typ": "textfield",
            "label": "Username",
            "var_name": "username"
          },
          {
            "typ": "nav_button",
            "text": "Terms",
            "actions": [
              "push:/terms"
            ]
          },
          {
            "typ": "nav_button",
            "text": "Privacy",
            "actions": [
              "push:/privacy"
            ]
          },
          {
            "typ": "checkbox",
            "text": "I agree",
            "var_name": "agree"
          },
          {
            "typ": "form_button",
            "text": "Create Account",
            "actions": [
              "rpc:/create_account",
              "replace_all:/"
            ]
          }
        ]
      }
    }
  }
}

Frontend shows the page:

Mobile phone showing a New Account page

User enters user1 in the text box, checks the "I agree" box, and clicks the "Create Account button".

Frontend performs the first action on the button, "rpc:/create_account", by sending this request:

POST /create_account HTTP/1.1
content-type: application/json
{"agree":true,"username":"user1"}

Server responds with

HTTP/1.1 200 OK

Frontend receives this successful response and then performs the next action on the button, "replace_all:/", removing all visible pages and showing the home page.