# Espresso ☕
**Espresso** is a lightweight, high-performance web framework for Elixir, inspired by the ergonomics of Express.js.
Unlike traditional frameworks that resolve routes at runtime, Espresso uses **Metaprogramming** to compile your routes directly into native Elixir function headers. This results in **O(1) routing speed** and minimal memory overhead, leveraging the full power of the BEAM virtual machine.
## Key Features
* **Express-style DSL**: Familiar `get`, `post`, `put`, `patch`, and `delete` macros.
* **Compile-Time Routing**: Routes are transformed into pattern-matched function clauses.
* **Dynamic Path Parameters**: Simple syntax for variables (e.g., `/users/:id`).
* **Middleware Pipeline**: Easily plug in standard `Plug` modules or custom logic.
* **Zero-Boilerplate**: Start a web server in a single file.
---
## Installation
Add `espresso` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:espresso, path: "../espresso"}, # While in development
{:plug_cowboy, "~> 2.7"},
{:jason, "~> 1.4"}
]
end
```
---
## Quick Start
Building an API with Espresso is straightforward. Define your routes and start the listener:
```elixir
defmodule MyApp do
use Espresso
# Middleware for JSON, form-data, urlencode parsing
use_middleware Plug.Parsers,
parsers: [:urlencoded, :multipart, :json],
pass: ["*/*"],
json_decoder: Jason
# Enable logger middleware for request logging
use_middleware Espresso.Logger
# Root Route
get "/" do
conn |> send("Welcome to Espresso")
end
# Scoped API
scope "/api/v1" do
# GET with dynamic param
get "/users/:id" do
conn |> json(%{user_id: id, status: "active"})
end
# POST with JSON response
post "/users" do
# Accessing req.body via conn.body_params
user_data = conn.body_params
conn |> status(201) |> json(%{message: "User created", data: user_data})
end
# DELETE
delete "/users/:id" do
conn |> status(204) |> send("")
end
end
end
# To start the server:
# MyApp.listen(4000)
```
---
## Architecture: Why Espresso?
Most web frameworks store routes in a list and iterate through them for every request. Espresso is different.
When you write a route in Espresso, the **Macro Engine** performs a "Code Merge." It takes your logic and injects it into a private dispatch function. At runtime, the BEAM uses its highly optimized pattern matching to jump directly to the correct code block.
### Pipeline Flow
1. **Request** enters via Cowboy/Bandit.
2. **Middlewares** are executed in sequence (the `conn` is passed through).
3. **Halt Check**: If a middleware halts the connection (e.g., Auth failure), the pipeline stops.
4. **Dispatch**: The request matches a compiled function based on Method and Path.
---
## Current Status (v0.1.0)
This project is currently a **Proof of Concept**.
* [x] Basic HTTP Verbs
* [x] Middleware Support
* [x] Dynamic Path Segments
* [x] Scoped Routes (Planned)
* [ ] Built-in Error Handling (Planned)
## License
MIT
---
## Installation
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
by adding `espresso` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:espresso, "~> 0.1.0"}
]
end
```
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
be found at <https://hexdocs.pm/espresso>.