Framework
leafo/lapis avatar
leafo/lapis

leafo/lapis: a Lua web framework for OpenResty

A web framework for Lua and OpenResty written in MoonScript

3,345 stars244 forksMoonScriptMIT

At a glance

What is it?
Lapis is a web framework for Lua and MoonScript that runs on OpenResty or lua-http. It is a small, opinionated tool aimed at developers who already work in the OpenResty stack; its documentation lives on the project homepage rather than in the README.
Who is it for?
Adopt Lapis if you already run OpenResty and want routing, models and views in Lua or MoonScript without leaving that stack; the project's own sites, including luarocks.org and itch.io, are the strongest evidence it holds up under real traffic. Skip it if you need a framework whose README alone tells you how to install and deploy, or if your team has no Lua experience, because the entry documentation sits on leafo.net/lapis rather than in the repository.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly MoonScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What Lapis is for, and who it is for

Lapis is a web framework for Lua and MoonScript that supports OpenResty or lua-http, according to the README. That sentence defines both its audience and its boundary. If you are building an HTTP service on OpenResty, Lapis gives you the application layer that OpenResty itself does not ship: request routing, a model layer, template rendering and a request/response abstraction over the nginx request object. If you are not on OpenResty, the second backend, daurnimator's lua-http, is the escape hatch, and the repository keeps a dedicated test suite for it under spec_cqueues.

The framework is written in MoonScript, a language that compiles to Lua, and the top level of the repository carries both example.lua and example.moon. That pairing is the clearest statement of intent you will find in the repository: the framework is authored in MoonScript but consumed from Lua as well, and the Makefile's build target runs moonc over the lapis directory before tests.

The README's own claim is "Lapis is production ready, use it on your next huge project." Treat that as marketing copy from the maintainer, not as a measured result. The more useful signal is the Lapis Powered list, which names luarocks.org, itch.io, streak.club, sightreading.training and the Ludum Dare game browser as sites built on it. Those are real deployments, and two of them link to source, which is the kind of evidence a framework's README can actually provide.

How Lapis sits on top of OpenResty

The architecture is a layering, not a rewrite. OpenResty supplies nginx plus LuaJIT, and Lapis runs inside that request lifecycle. The repository layout reflects this: lapis/ holds the framework source in MoonScript, and spec_openresty/ holds integration tests that the Makefile runs with busted spec_openresty, requiring an installed OpenResty and databases. The README lists that suite alongside spec_postgres, spec_mysql and spec_cqueues, which tells you the project treats each server and database combination as a separate integration surface rather than assuming one.

Because the framework is compiled from MoonScript, the build step is part of the data flow. The Makefile's build target runs moonc lapis, moonc spec_openresty/s2 and moonc spec_mysql/models.moon, so the Lua that actually executes is generated. If you install from LuaRocks rather than from a checkout, that compilation has already happened for you; if you work from the repository, it has not.

The second backend matters for how you think about portability. lua-http with cqueues is a pure Lua HTTP stack, so a Lapis application can run without nginx in front of it. The README does not describe how much application code has to change between the two, and the existence of a separate spec_cqueues suite suggests the maintainers test them independently rather than as one interchangeable target. That is a limitation worth knowing before you assume a Lapis app is server-agnostic in practice.

Installing Lapis and running the test suite

The README does not contain install instructions. It links to the Getting Started Tutorial at leafo.net/lapis/reference/getting_started.html and to the reference documentation at leafo.net/lapis/reference.html, so that is where the project says to go. What the repository does document is how to build and test a checkout, which is the path you need if you intend to modify the framework itself.

The Makefile's local target builds the MoonScript and installs the development rockspec into your local LuaRocks tree. Run it from the repository root after cloning:

bash
make local

That target depends on build, which runs moonc over the lapis directory, and then installs lapis-dev-1.rockspec with --force --local. You should see LuaRocks report an install into your local tree rather than a system path.

The test suites need Busted and MoonScript, and the database suites need a running server. The README states that a lapis_test database is created on each, and the Makefile provides targets for the initial creation:

bash
make test_db
make mysql_test_db

The README notes that the PostgreSQL suite expects the postgres user to log in with no password, and the MySQL suite expects the same of root. Those are test-harness assumptions, not production guidance.

If you would rather not set up OpenResty and both databases by hand, the repository ships a Dockerfile that installs Busted, lpeg, moonscript, luaposix, luasql-mysql and http against Lua 5.1. The README gives the two commands:

bash
docker build -t lapis-test .
docker run lapis-test

The image's entrypoint is ci.sh, so running the container executes the full suite. The README warns that docker build pulls in files from the current directory including any changes, and that you must rebuild before running the suite again to test modified code.

Where Lapis is the wrong tool

The clearest limitation is documentation placement. The README tells you the framework exists, lists production users and describes the test setup, but it does not tell you how to install the framework for use in your own project or how to deploy it. Everything a new user needs sits behind two links to leafo.net. That is a deliberate split, and it means the repository is not self-contained for onboarding.

The second limitation is the OpenResty assumption. Lapis's value proposition is partly that it speaks nginx's request model. If your deployment target is a plain Lua HTTP server, or a platform where you cannot run LuaJIT and nginx together, you are using the lua-http backend and giving up the reason most people pick Lapis in the first place. The README does not claim feature parity between the two backends, and it does not document what differs.

The third is version surface. The Makefile carries explicit targets for Lua 5.1, 5.2 and 5.3, each installing its own LuaRocks tree and running busted spec under that interpreter. That is thorough, but it also means the framework's own test matrix treats Lua versions as distinct environments rather than a single supported range. The Dockerfile pins Lua 5.1 for the packaged test image. If you are on a version outside that set, the repository gives you no statement of support either way.

Finally, MoonScript. Authoring in MoonScript is a choice the maintainers made and it works for them, but it adds a compile step to any workflow that touches framework source, and it means the canonical form of the code in the repository is not the form that executes.

Lapis compared with a plain OpenResty application

The real alternative is not another framework so much as no framework: writing OpenResty handlers directly in Lua, using ngx.location.capture, ngx.say and the rest of the nginx Lua API, with your own routing table. That approach has no build step, no dependency on MoonScript, and no framework version to track. It also leaves you to write request parsing, parameter handling, template rendering and database access yourself, or to assemble them from separate LuaRocks packages.

Lapis's difference is that it supplies those pieces as one coherent layer with a documented model and view story. The trade is that you inherit the framework's opinions about how a request flows and how models are defined, and you inherit its release cadence. The repository shows three releases: v1.17.0 on 2025-11-13, v1.18.0 on 2026-04-24 and v1.19.0 on 2026-08-13. The last push to the repository was on 2026-08-26. That is a steady but not rapid cadence, and it means framework upgrades arrive on the maintainer's schedule.

There is also a middle option the README gestures at: the Supplemental Libraries list. lapis-eswidget, lapis-annotate, lapis-console, lapis-exceptions and lapis-bayes are separate repositories, which suggests the core framework is kept narrow and extensions live outside it. If you need exception reporting or a browser console, you are adding dependencies rather than enabling a built-in feature.

Maintenance, licensing and upgrade cost

The repository is not archived, and the last push was on 2026-08-26, which is recent relative to the release history. The release record shows v1.19.0 on 2026-08-13, v1.18.0 on 2026-04-24 and v1.17.0 on 2025-11-13. That is roughly two to three releases a year, with the most recent one landing shortly before the last push. Nothing in the repository describes a deprecation policy, a long-term support branch or a migration guide for major versions. The README links to a changelog at leafo.net/lapis/changelog.html rather than shipping one in the repository, so upgrade planning means reading that page.

The licence is MIT. For practical purposes that is permissive: you can use Lapis in closed-source and commercial applications, and you can modify and redistribute it, provided the copyright notice and permission notice are preserved. This is a description of the licence text, not legal advice; if your organisation has specific obligations around attribution in distributed binaries, read the LICENSE file at the repository root.

The upgrade cost is shaped by the MoonScript dependency. Because the framework is compiled, any change that touches lapis/ source requires moonc to be available in your build environment. The Makefile's lint target runs moonc -l over every .moon file under lapis, and lint_config.moon is compiled separately, so the project's own tooling assumes a MoonScript compiler is present. If your team writes Lua and not MoonScript, that compiler is a build-time dependency you take on for the framework's sake alone.

Editorial conclusion

Adopt Lapis if you already run OpenResty and want routing, models and views in Lua or MoonScript without leaving that stack; the project's own sites, including luarocks.org and itch.io, are the strongest evidence it holds up under real traffic. Skip it if you need a framework whose README alone tells you how to install and deploy, or if your team has no Lua experience, because the entry documentation sits on leafo.net/lapis rather than in the repository. Before committing, verify that the getting started tutorial's install path still works on your Lua version, and check the changelog for the v1.19.0 changes from 2026-08-13.

Frequently asked questions

How do I install Lapis?

The README does not give install steps for using Lapis in your own project; it points to the Getting Started Tutorial at leafo.net/lapis/reference/getting_started.html. The repository does document building a checkout: make local compiles the MoonScript and installs lapis-dev-1.rockspec into your local LuaRocks tree.

Does Lapis require OpenResty?

No. The README describes Lapis as a web framework for Lua and MoonScript supporting OpenResty or http.server, the latter being daurnimator's lua-http. The repository keeps a separate test suite under spec_cqueues for the lua-http and cqueues combination.

How do I run the Lapis test suite in Docker?

The README gives two commands: docker build -t lapis-test . and docker run lapis-test. The image's entrypoint is ci.sh, so the container runs the full suite. The README notes that docker build pulls in files from the current directory, so you must rebuild to test modified code.

What licence does Lapis use?

The repository is MIT licensed. The LICENSE file is at the top level of the repository.

Official sources

  1. leafo/lapis on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/leafo-lapis.svg)](https://hysenlabs.com/projects/leafo-lapis)