Howdy
HowdySay hello to your next idea.

Big ideas.
Happy building.

The full-featured web framework written in Gleam. Typed from end to end and running on the BEAM.

curl -fsSL https://tryhowdy.dev/install.sh | bash
hello_controller.gleam
import howdy/controller
import howdy/param

pub fn controller() {
  controller.new("/hello")
  |> controller.get("/:name", fn(ctx) {
    use name <- param.string(ctx, "name")
    controller.text(ctx, "Howdy, " <> name <> "! 👋")
  })
}

A route is a function.

  • Controllers group routes under a prefix
  • Typed path, query and form parameters
  • Guards, CORS, CSRF and rate limiting as middleware
Read the guide
counter.gleam
pub fn app() {
  lustre.simple(init, update, view)
}

fn update(count: Int, msg: Msg) -> Int {
  case msg {
    Increment -> count + 1
    Decrement -> count - 1
  }
}

fn view(count: Int) {
  ui.card([], [
    ui.h2("Counter"),
    ui.p([text(int.to_string(count))]),
    ui.button(Primary, [event.on_click(Increment)], [text("+")]),
  ])
}

Interactive, without the JavaScript.

  • State and logic stay on the server
  • The browser gets only what changed, over a WebSocket
  • Built from Howdy UI’s themed components
Read the guide
todos.gleam
pub fn open(db: Repo) -> service.Result(List(Todo)) {
  query.from(todo_item.table())
  |> query.where(query.Eq("done", sql.bool(False)))
  |> query.order_by("id", query.Asc)
  |> repo.query_all(db, _)
  |> result.map_error(errors.from_db)
}

Queries you can read.

  • PostgreSQL or SQLite through Gloo
  • Migrations that run when the app starts
  • Transactions, and errors as values
Read the guide
hello_controller_test.gleam
pub fn hello_test() {
  let res =
    testing.get("/hello/world")
    |> testing.send(app())

  assert res.status == 200
  assert testing.text(res) == "Howdy, world! 👋"
}

Tests without a server.

  • Requests go straight through your app
  • Check status, headers, cookies, JSON and HTML
  • A fresh database for every test
Read the guide

This box is running
on our server.

Type your name. Each key press travels up a WebSocket, Gleam runs the update on the BEAM, and only the changed text comes back. No JavaScript was written for it, and no JSON API.

  1. 1You type
  2. 2The server updates
  3. 3The page patches
How live components work
Connected to Howdy

Everything a real app needs,
already on speaking terms.

~/my_appzsh
$ howdy create pages posts post \
    title:string body:string

🤠 Creating files
  + src/my_app/features/posts/post.gleam
  + src/my_app/features/posts/posts.gleam
  + src/my_app/features/posts/posts_controller.gleam
  + src/my_app/features/posts/pages/index_page.gleam
  + src/my_app/features/posts/pages/show_page.gleam
  + src/my_app/features/posts/pages/new_page.gleam
  + src/my_app/features/posts/pages/edit_page.gleam
  + src/my_app/migrations/create_posts.gleam
  + test/my_app/features/posts/posts_test.gleam
✓ Registered the routes and migration

One command.
A whole feature.

The howdy CLI writes a record, its database operations, a migration, pages to list, show, add, edit and delete, and tests for all of it. Then it registers the routes for you.

  • howdy new a project
  • howdy create pages, APIs, live components
  • howdy add auth, mail, flags, OpenAPI
See every generator

Pull up a chair.

Up and running in three steps.

You’ll need Gleam, Erlang and rebar3. The getting started guide covers installing them.

  1. 1

    Install the CLI

    curl -fsSL https://tryhowdy.dev/install.sh | bash
  2. 2

    Create a project

    howdy new my_app
  3. 3

    Start building

    cd my_app && gleam dev

Your next project
starts with a howdy.

Built with Gleam. Made for your imagination.