to_json(obj)

Converts obj to JSON. Will strip recursion and things that can not be encoded.

from_json(str)

Converts JSON to table, a direct wrapper around Lua CJSON’s decode.

time_ago_in_words(date, [parts=1], [suffix="ago"])

Returns a string in the format “1 day ago”.

parts allows you to add more words. With parts=2, the string
returned would be in the format 1 day, 4 hours ago.

Encoding Methods

Encoding functions are found in:

localencoding=require("lapis.util.encoding")

encoding=require"lapis.util.encoding"

encode_base64(str)

Base64 encodes a string.

decode_base64(str)

Base64 decodes a string.

hmac_sha1(secret, str)

Calculates the hmac-sha1 digest of str using secret. Returns a binary
string.

encode_with_secret(object, secret=config.secret)

Encodes a Lua object and generates a signature for it. Returns a single string
that contains the encoded object and signature.

decode_with_secret(msg_and_sig, secret=config.secret)

Decodes a string created by encode_with_secret. The decoded object is only
returned if the signature is correct. Otherwise returns nil and an error
message. The secret must match what was used with encode_with_secret.

autoload(prefix, tbl={})

Makes it so accessing an unset value in tbl will run a require to search
for the value. Useful for autoloading components split across many files.
Overwrites __index metamethod. The result of the require is stored in the
table.

CSRF Protection

CSRF protection provides a way to prevent fraudulent requests that originate
from other sites that are not your application. The common approach is to
generate a special token when the user lands on your page, then resubmit that
token on a subsequent POST request.

In Lapis the token is a cryptographically signed message that the server can
verify the authenticity of.

Before using any of the cryptographic functions it’s important to set your
application’s secret. This is a string that only the application knows about.
If your application is open source it’s worthwhile to not commit this secret.
The secret is set in your configuration like so:

Now that you have the secret configured, we might create a CSRF protected form
like so:

locallapis=require("lapis")localcsrf=require("lapis.csrf")localcapture_errors=require("lapis.application").capture_errorslocalapp=lapis.Application()app:get("form","/form",function(self)localcsrf_token=csrf.generate_token(self)self:html(function()form({method="POST",action=self:url_for("form")},function()input({type="hidden",name="csrf_token",value=csrf_token})input({type="submit"})end)end)end)app:post("form","/form",capture_errors(function(self)csrf.assert_token(self)return"The form is valid!"end))

csrf=require"lapis.csrf"classextendslapis.Application[form:"/form"]:respond_to{GET:=>csrf_token=csrf.generate_token@@html=>formmethod:"POST",action:@url_for("form"),->inputtype:"hidden",name:"csrf_token",value:csrf_tokeninputtype:"submit"POST:capture_errors=>csrf.assert_token@"The form is valid!"}

If you’re using CSRF protection in a lot of actions then it might be helpful
to create a before filter that generates the token automatically.

The following functions are part of the CSRF module:

localcsrf=require("lapis.csrf")

csrf=require"lapis.csrf"

generate_token(req, key=nil, expires=os.time() + 28800)

Generates a new CSRF token using the session secret. key is an optional piece
of data you can associate with the request. The token will expire in 8 hours by
default.

validate_token(req, key)

Valides the CSRF token located in req.params.csrf_token. If the token has a
key it will be validated against key. Returns true if it’s valid, or nil
and an error message if it’s invalid.

assert_token(...)

First calls validate_token with same arguments, then calls assert_error if
validation fails.

Making HTTP Requests

Lapis comes with a built-in module for making asynchronous HTTP requests. The
way it works is by using the Nginx proxy_pass directive on an internal
action. Because of this, before you can make any requests you need to modify
your Nginx configuration.

This code ensures that the correct headers are set for the new request. The
$_url variable is used to store the target URL. It must be defined using
set $_url "" directive in your default location.

Now we can use the lapis.nginx.http module. There are two methods. request
and simple. request implements the Lua Socket HTTP request API (complete
with LTN12).

simple is a simplified API with no LTN12:

localhttp=require("lapis.nginx.http")localapp=lapis.Application()app:get("/",function(self)-- a simple GET requestlocalbody,status_code,headers=http.simple("http://leafo.net")-- a post request, data table is form encoded and content-type is set to-- application/x-www-form-urlencodedhttp.simple("http://leafo.net/",{name="leafo"})-- manual invocation of the above requesthttp.simple({url="http://leafo.net",method="POST",headers={["content-type"]="application/x-www-form-urlencoded"},body={name="leafo"}})end)

http=require"lapis.nginx.http"classextendslapis.Application"/":=>-- a simple GET requestbody,status_code,headers=http.simple"http://leafo.net"-- a post request, data table is form encoded and content-type is set to-- application/x-www-form-urlencodedhttp.simple"http://leafo.net/",{name:"leafo"}-- manual invocation of the above requesthttp.simple{url:"http://leafo.net"method:"POST"headers:{"content-type":"application/x-www-form-urlencoded"}body:{name:"leafo"}}

simple(req, body)

Performs an HTTP request using the internal /proxy location.

Returns 3 values, the string result of the request, http status code, and a
table of headers.

If there is only one argument and it is a string then that argument is treated
as a URL for a GET request.

If there is a second argument it is set as the body of a POST request. If
the body is a table it is encoded with encode_query_string and the
Content-type header is set to application/x-www-form-urlencoded

If the first argument is a table then it is used to manually set request
parameters. It takes the following keys:

url – the URL to request

method – "GET", "POST", "PUT", etc…

body – string or table which is encoded

headers – a table of request headers to set

request(url_or_table, body)

Caching

Lapis comes with a simple memory cache for caching the entire result of an
action keyed on the parameters it receives. This is useful for speeding up the
rendering of rarely changing pages because all database calls and HTML methods
can be skipped.

The Lapis cache uses the shared dictionary
API from HttpLuaModule.
The first thing you’ll need to do is create a shared dictionary in your Nginx
configuration.

The first request to /hello/world will run the action and store the result in
the cache, all subsequent requests will skip the action and return the text
stored in the cache.

The cache will remember not only the raw text output, but also the content
type and status code.

The cache key also takes into account any GET parameters, so a request to
/hello/world?one=two is stored in a separate cache slot. Multiple parameters
are sorted so they can come in any order and still match the same cache key.

When the cache is hit, a special response header is set to 1,
x-memory-cache-hit. This is useful for debugging your application to make
sure the cache is working.

Instead of passing a function as the action of the cache you can also pass in a
table. When passing in a table the function must be the first numerically
indexed item in the table.

The table supports the following options:

dict_name – override the name of the shared dictionary used (defaults to "page_cache")

exptime – how long in seconds the cache should stay alive, 0 is forever (defaults to 0)

cache_key – set a custom function for generating the cache key (default is described above)

when – a function that should return truthy a value if the page should be cached. Receives the request object as first argument (defaults to nil)

For example, you could implement microcaching, where the page is cached for a
short period of time, like so:

A validation exists for ensuring that a param is an uploaded file, it’s called
is_file:

localapp=lapis.Application()app:post("/my_action",function(self)assert_valid(self.params,{{"uploaded_file",is_file=true}})-- file is ready to be usedend)

classextendslapis.Application"/my_action":capture_errors=>assert_valid@params,{{"uploaded_file",is_file:true}}-- file is ready to be used...

An uploaded file is loaded entirely into memory, so you should be careful about
the memory requirements of your application. Nginx limits the size of uploads
through the
client_max_body_size
directive. It’s only 1 megabyte by default, so if you plan to allow uploads
greater than that you should set a new value in your Nginx configuration.

Application Helpers

The following functions are part of the lapis.application module:

localapp_helpers=require("lapis.application")

application=require"lapis.application"

fn = respond_to(verbs_to_fn={})

verbs_to_fn is a table of functions that maps a HTTP verb to a corresponding
function. Returns a new function that dispatches to the correct function in the
table based on the verb of the request. See
Handling HTTP verbs

If an action for HEAD does not exist Lapis inserts the following function to
render nothing:

function()return{layout=false}end

->{layout:false}

If the request is a verb that is not handled then the Lua error function
is called and a 500 page is generated.

A special before key can be set to a function that should run before any
other action. If @writeself.write is called inside the before function then
the regular handler will not be called.

safe_fn = capture_errors(fn_or_tbl)

Wraps a function to catch errors sent by yield_error or assert_error. See
Exception Handling for more information.

If the first argument is a function then that function is called on request and
the following default error handler is used:

function()return{render=true}end

->{render:true}

If a table is the first argument then the 1st element of the table is used as
the action and value of on_error is used as the error handler.

When an error is yielded then the @errorsself.errors variable is set on the current request and
the error handler is called.

safe_fn = capture_errors_json(fn)

A wrapper for capture_errors that passes in the following error handler:

function(self)return{json={errors=self.errors}}end

=>{json:{errors:@errors}}

yield_error(error_message)

Yields a single error message to be captured by capture_errors

obj, msg, ... = assert_error(obj, msg, ...)

Works like Lua’s assert but instead of triggering a Lua error it triggers an
error to be captured by capture_errors

wrapped_fn = json_params(fn)

Return a new function that will parse the body of the request as JSON and
inject it into @params if the content-type is set to application/json.