Parsley + Basil
A small language and a web server. Nothing else to install.
Parsley is a programming language for working with data and making web pages. Basil is a web server that runs Parsley files. Put a file in a folder and it becomes a page.
- no build step
- no npm
- no framework
- no ORM
- one binary
let Page = fn({title, users}) {
<html>
<body>
<h1>title</h1>
<ul>
for (user in users) {
<li><b>user.name</b> " — " user.email</li>
}
</ul>
</body>
</html>
}
let emailList <== CSV(@./email-list.csv)
<Page title="Active Users" users={emailList}/>
What is it?
Parsley is a scripting language. It reads files, talks to databases, and writes HTML. HTML tags are part of the language, so a page is just a function that returns markup. The program above reads a CSV file and turns it into a web page. That is the whole program.
Basil is the server. It serves static files, runs .pars files as pages or API endpoints, and comes with a database, user login, search, and an image server built in. You can also run Parsley on its own, from the command line, as a scripting tool.
Together they cover the common case: a website with some data behind it, built by one person or a small team, without a large toolchain.
What can you do with it?
- Build a website with pages, forms, and a database.
- Read a CSV file, filter and sort it, and show it as a table.
- Write a small API that returns JSON.
- Add passkey login and protected pages without a third-party service.
- Write scripts that clean up data and print it as a table, CSV, or Markdown.
Get started walks through the first one. The Parsley manual and the Basil manual cover the rest.
Data is built in
Most languages leave data to libraries. Parsley has it in the language. Four ideas do most of the work:
- Schema — the shape of your data. Field names, types, defaults, and rules. One schema drives validation, the database table, and the HTML form.
- Record — one row of data, checked against a schema. Bad data is kept, but the errors come with it, so a form can show them.
- Table — rows and columns. Filter, sort, group, and total with methods like
.where(),.orderBy(), and.sum(). - Database — SQLite is built in; Postgres and MySQL connect with one line. A schema becomes a database table. Queries come back as tables.
The same table methods work on a CSV file, a database query, and a list you typed in. There is no ORM layer in between. Read about the data model.
Types that match the real world
Dates, money, and measurements are values in their own right, not strings or floats. You can write them directly, do arithmetic on them, and print them properly:
@2024-01-15a date$99.99money#5ft + #10inunits@./data.csva file pathhttps://herbaceous.neta URL@1h30ma duration
Money keeps its currency and rounds correctly. Units convert between metric and imperial. Dates and durations add and subtract. See dates and times, money, and units.
CSV like a spreadsheet
Read a CSV file and you get a table. Numbers are numbers, not text. Then work on it the way you would in a spreadsheet or SQL:
let sales <== CSV(@./sales.csv)
sales
.where(fn(r) { r.region == "South" })
.orderBy("amount", "desc")
.select(["product", "amount"])Send the result out as HTML, CSV, Markdown, or JSON with one method call. In the terminal it prints as a lined-up table. See data formats and tables.
What Basil gives you
- Routing — folders and files are routes. No route table to maintain.
- Database — SQLite built in, with a browser-based inspector in dev mode.
- Login — passkeys, users, roles, and protected paths.
- Parts — page sections that update without a reload, written in Parsley.
- Search — full-text search over your own content.
- Images — resize, crop, and serve responsive images.
- Deploy — push to the server over git and it goes live.
- Dev tools — hot reload and readable error pages.
Install
One command installs basil (the server) and pars (the language and REPL):
curl -fsSL https://herbaceous.net/install.sh | shOr download a binary from the releases page. macOS, Linux, and Windows; Apple Silicon and Intel.
Why another one?
Most web tools are built for large teams and large sites. They are powerful, but they ask for a lot of setup before you see a page. Parsley and Basil are built for the other case: one person, one idea, one afternoon.
The aim is that a web page is a file, data is a table, and the server is one program you can understand. More on why we built it.