title: Creating a Lapis Application with Lua

Creating a Lapis Application with Lua

Generating a New Project

If you haven’t already, read through the generic getting started guide for information on creating a new project skeleton along with details on OpenResty, Nginx configurations, and the lapis command.

You can start a new Lua project in the current directory by running the following command:

$ lapis new --lua

The default nginx.conf reads a file called app.lua for your application. A basic one is provided with the lapis new command.

app.lua is a regular Lua module that contains the application. You can even require the module like any other in the regular Lua interpreter. It looks like this:

-- app.lua
local lapis = require("lapis")
local app = lapis.Application()

app:get("/", function()
  return "Welcome to Lapis " .. require("lapis.version")
end)

return app

Try it out by starting the server:

lapis server

Visit http://localhost:8080 to see the page.

To change the port we can create a configuration. Create config.lua.

In this example we change the port in the development environment to 9090:

-- config.lua
local config = require("lapis.config")

config("development", {
  port = 9090
})

You can read more about configurations on the Configurations and Environments guide.

The development environment is used and loaded automatically when lapis server is run with no additional arguments. (And the file lapis_environment.lua doesn’t exist)

Lapis uses a handful of fields in the configuration (such as port), other fields can be used to store anything you want. For example:

-- config.lua
local config = require("lapis.config")

config("development", {
  greeting = "Hello world"
})

You can get the current configuration by calling get. It returns a plain Lua table:

-- app.lua
local lapis = require("lapis")
local config = require("lapis.config").get()

local app = lapis.Application()

app:get("/", function(self)
  return config.greeting .. " from port " .. config.port
end)

return app

Creating a View

Now that we can create basic pages we’ll likely want to render something a bit more complex. Lapis comes with support for etlua, an Lua templating language that lets you insert Lua mixed in with text and HTML.

A view is a file that is responsible for generating the HTML. Typically your action will prepare all the data for your view and then tell it to render.

By default Lapis searches for views in views/ directory. Lets create a new view there, index.etlua. We won’t use any of etlua’s special markup just yet, so it will look like a normal HTML file.

<!-- views/index.etlua -->
<h1>Hello world</h1>
<p>Welcome to my page</p>

You’ll notice that <html>, <head>, and <body> tags aren’t there. The view typically renders the inside of the page, and the layout is responsible for what goes around it. We’ll look at layouts further down.

Now lets create the application which renders our view:

-- app.lua
local lapis = require("lapis")

local app = lapis.Application()
app:enable("etlua")

app:get("/", function(self)
  return { render = "index" }
end)

return app

etlua is not enabled by default, you must enable it by calling the enable method on your application instance.

We use the render parameter of our action’s return value to instruct what template to use when rendering the page. In this case "index" refers to the module with the name views.index. etalua injects itself into Lua’s require method and so when the module views.index is loaded, an attempt to read and parse the file views/index.etlua is made.

Running the server and navigating to it in the browser should show our rendered template.

Working with etlua

etlua comes with the following tags for injecting Lua into your templates:

  • <% lua_code %> runs Lua code verbatim
  • <%= lua_expression %> writes result of expression to output, HTML escaped
  • <%- lua_expression %> same as above but with no HTML escaping

Learn more about the etlua integration in the etlua guide.

In the following example we assign some data in the action, then print it out in our view:

-- app.lua
local lapis = require("lapis")

local app = lapis.Application()
app:enable("etlua")

app:get("/", function(self)
  self.my_favorite_things = {
    "Cats",
    "Horses",
    "Skateboards"
  }

  return { render = "list" }
end)

return app
<!-- views/list.etlua -->
<h1>Here are my favorite things</h1>
<ol>
  <% for i, thing in pairs(my_favorite_things) do %>
    <li><%= thing %></li>
  <% end %>
</ol>

Creating a Layout

A layout is a separate shared template that wraps the content of every page. Lapis comes with a basic layout to get you started but you’ll most likely want to replace it with something custom.

We’ll write the layout in etlua just like our views. Create views/layout.etlua:

<!-- views/layout.etlua -->
<!DOCTYPE HTML>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title><%= page_title or "My Page" %></title>
</head>
<body>
  <h1>Greetings</h1>
  <% content_for("inner") %>
</body>
</html>

The content_for function is a special function built into templates that allows you to send data from a view to a layout. Lapis puts the rendered result of the view into the content variable named inner. You’ll note that we don’t need to use any of the etlua tags that write into the page. This is because content_for efficiently puts its result directly into the output buffer.

Any other variables and helper functions that would normally be available in a view are also available in the layout.

Now that the layout is written it can be assigned to the application:

local app = lapis.Application()
app:enable("etlua")
app.layout = require "views.layout"

-- the rest of the application...

The syntax is slightly different than rendering a view. Instead of assigning a template name to the layout field, we assign the actual template object. This can be obtained by just requiring it by the module name: "views.layout". As described above, etlua takes care of converting the .etlua file into something usable by Lua.