node package manager
Painless code sharing. npm Orgs help your team discover, share, and reuse code. Create a free org »

super-api

super-api

Define a JSON API with JSON

Quick start

Set up aliases:

alias super-api="coffee ~/Projects/super-api/super-api.coffee"
alias super-service="coffee ~/Projects/super-api/super-service.coffee"

Run the example Hello Service & API:

super-api examples/hello-api
super-service examples/hello-api/services/hello

Test the routes:

somata-repl
#| set url http://localhost:6399
'http://localhost:6399'
#| get $url/hello.json?name=Fred
'Hello, Fred!'
#| {greeting: "Beat it"} | put $url/hello/greeting.json
'Changed to Beat it'
#| get $url/hello.json?name=kid
'Beat it kid!'

Usage

Setup

An app directory requires at least app.json and optionally views/, static/, and services/:

simple-chat/
    app.json
    static/
        js/
            app.coffee
        css/
            app.sass
    views/
        index.jade
    services/
        simple-dynamodb/
            index.coffee
        create-websocket-url/
            index.coffee
            config.json

app.json

The app's name, slug, port, routes, and optionally middleware are defined in app.json.

Routes

Each route is defined as request, steps, and response:

"routes"[
    {
        "request": { ... },
        "steps": [ ... ],
        "response": { ... },
    },
    ...
]
Request

request defines the incoming request's method and path.

"request"{
    "method": "get",
    "path": "/hello.json"
}

You may also define an optional auth, which is a condition that will be expanded to true or false per request, and send a 401 response if false.

"request"{
    "auth": "$res.locals.user.is_admin",
    "method": "get",
    "path": "/secret"
}
Steps

steps is an array of steps which will be called in series. Right now there is step type, "remote", which will call a somata service with the given service name, method, and array of args.

"steps"[
    {
        "type": "remote",
        "service": "hello-service",
        "method": "sayHello",
        "args": [
            "$req.query.name"
        ]
    }
]

When the step returns some data, it is by default set in the response context as res.locals.data. You can specify a custom return key to set it as res.locals[return], for example to multiple steps of data around for some specific template variables.

"steps"[
    {
        "type": "remote",
        "service": "hello-service",
        "method": "sayHello",
        "args": [
            "$req.query.name"
        ]
    },
    {
        "type": "remote",
        "service": "reverse-service",
        "method": "reverseString",
        "args": [
            "$res.locals.data"
        ],
        "return": "reversed"
    }
]

In the above example the first step does not define a return key, so by default the data is set as res.locals.data. The second step uses that data as an argument, but sets a different return key "reversed". Now both res.locals.data and res.locals.reversed are available for the response.

Response

response defines either the content_type of the data to be used as a response (expects res.locals.data to be set from some prior remote step), or a template that will be rendered with an optional context.

Respond with plain text:

"response"{
    "content_type": "text/plain"
}

Render the template hello.jade with a context variable title set by some step:

"response"{
    "template": "hello",
    "context": {
        "title": "$res.locals.title"
    }
}

Render a template depending on some step's return value:

"response"{
    "template": "$res.locals.template",
    "context": {
        "user": "$res.locals.user"
    }
}

Option expansion

Values in request, steps, and response definitions that match "$req" or "$res" will be expanded for each request, for example "$req.body", "$res.locals.data", or "$req.headers.token".

GET /hello?name=Jones
"$req.query.name" -> "Jones"