spaceship_helm

A Hono-like HTTP router for Gleam, targeting JS fetch-based runtimes.

Installation

gleam add spaceship_helm

Quick Start

import spaceship_helm
import spaceship_helm/context
import spaceship_helm/response

pub fn main() {
  let app =
    spaceship_helm.new()
    |> spaceship_helm.get("/", fn(_ctx) {
      response.text("Hello, World!")
    })

  // Export for JS runtimes
  app |> spaceship_helm.to_fetch()
}

Features

FeatureDescription
HTTP Methodsget, post, put, delete, patch, head, options, on
Path Params:name syntax for dynamic segments
Wildcards*name to match remaining path
Query ParamsExtract query string values
MiddlewareFunctional pipe composition
Route GroupsNamespace routes with prefixes
Response Helperstext, json, html, redirect
Built-in MiddlewareCORS, logger
Static FilesServe public assets with MIME detection
SessionsCookie-based session management
CookiesRead/write HTTP cookies
Environment VariablesCross-platform env access (Node, Cloudflare, Deno, Bun)

API

Creating an App

let app = spaceship_helm.new()

Registering Routes

let app =
  spaceship_helm.new()
  |> spaceship_helm.get("/", home_handler)
  |> spaceship_helm.post("/users", create_user)
  |> spaceship_helm.put("/users/:id", update_user)
  |> spaceship_helm.delete("/users/:id", delete_user)
  |> spaceship_helm.patch("/users/:id", patch_user)
  |> spaceship_helm.on("CUSTOM", "/custom", custom_handler)

Path Parameters

let app =
  spaceship_helm.new()
  |> spaceship_helm.get("/users/:id", fn(ctx) {
    let id = context.param(ctx, "id")
    response.text(id)
  })

Query Parameters

let app =
  spaceship_helm.new()
  |> spaceship_helm.get("/search", fn(ctx) {
    let query = context.query(ctx, "q") |> option.unwrap("")
    response.text(query)
  })

Route Groups

let app =
  spaceship_helm.new()
  |> spaceship_helm.group("/api/v1", fn(app) {
    app
    |> spaceship_helm.get("/users", list_users)
    |> spaceship_helm.get("/users/:id", get_user)
    |> spaceship_helm.post("/users", create_user)
  })

Middleware

let app =
  spaceship_helm.new()
  |> spaceship_helm.middleware(fn(ctx, next) {
    let resp = next(ctx)
    response.set_header(resp, "x-powered-by", "spaceship_helm")
  })
  |> spaceship_helm.get("/", home_handler)

Built-in Middleware

import spaceship_helm/middleware

let app =
  spaceship_helm.new()
  |> spaceship_helm.middleware(middleware.cors())
  |> spaceship_helm.middleware(middleware.logger(io.println))

Static Files

Serve static files from a directory:

import spaceship_helm/static

let app =
  spaceship_helm.new()
  |> spaceship_helm.get("/api/data", api_handler)
  |> spaceship_helm.middleware(static.directory("public"))

With custom cache duration:

|> spaceship_helm.middleware(static.directory_with_cache("public", 604800))

Supported MIME types: HTML, CSS, JavaScript, JSON, images, fonts, and more.

Sessions

Cookie-based session management:

import spaceship_helm/session

// Create a session store
let store = sessions.new_store()

// Setup middleware
let app =
  spaceship_helm.new()
  |> spaceship_helm.get("/", home_handler)
  |> spaceship_helm.use(sessions.cookie("session_id", "secret-key", store))

// In handler - read session
use session <- sessions.get(ctx)
let username = sessions.get_value(session, "username")

// In handler - write session
let session = sessions.set_value(session, "username", "alice")
sessions.commit(response, session, store)

Cookies

Read and write HTTP cookies:

import spaceship_helm/cookie

// Read cookie from request
let session_id = cookie.get(ctx.req, "session_id")

// Set cookie on response
let resp = cookie.set(response, "session_id", "abc123", 3600)

// Delete cookie
let resp = cookie.delete(response, "session_id")

// Set with custom options
let options = cookie.CookieOptions(
  path: "/api",
  max_age: 3600,
  http_only: True,
  secure: True,
  same_site: "Strict",
)
let resp = cookie.set_with_options(response, "token", "xyz", options)

Response Helpers

// Text
response.text("Hello")

// HTML
response.html("<h1>Hello</h1>")

// JSON
import gleam/json
response.json(json.object([#("name", json.string("Alice"))]))

// Redirect
response.redirect("/login")
response.redirect_permanent("/new-url")

// Status codes
response.bad_request("Invalid input")
response.unauthorized()
response.forbidden()
response.not_found()
response.internal_server_error()
response.no_content()

Custom Not Found Handler

let app =
  spaceship_helm.new()
  |> spaceship_helm.get("/", home_handler)
  |> spaceship_helm.not_found(fn(_ctx) {
    response.new(404) |> response.set_body(<<"Custom 404":utf8>>)
  })

JavaScript Usage

// app.gleam
import spaceship_helm

pub fn main() {
  let app =
    spaceship_helm.new()
    |> spaceship_helm.get("/", fn(_ctx) {
      spaceship_helm/response.text("Hello from Gleam!")
    })

  app |> spaceship_helm.to_fetch()
}
// app.mjs
import handler from "./build/dev/javascript/app.mjs"

// Bun
export default { fetch: handler }

// Cloudflare Workers
export default { fetch: handler }

// Deno
Deno.serve(handler)

// Node.js (18+)
import { createServer } from "node:http"

const server = createServer(async (req, res) => {
  const url = new URL(req.url, `http://${req.headers.host}`)
  const request = new Request(url, { method: req.method, headers: req.headers })
  const response = await handler(request)
  res.writeHead(response.status, Object.fromEntries(response.headers))
  res.end(await response.arrayBuffer())
})

server.listen(3000)

Environment Variables

Access environment variables across different runtimes:

import spaceship_helm/env

// Get a single variable
let value = env.get("MY_VAR")  // Returns Option(String)

// Get with default
let value = env.get_or("MY_VAR", "default")

// Get required (panics if not set)
let value = env.get_required("DATABASE_URL")

// Check if exists
let exists = env.has("MY_VAR")

// Get all variables
let vars = env.all()  // List(#(String, String))

The env module works on:

For Cloudflare Workers, initialize the env in your entry point:

import spaceship_helm/env as helm_env

pub fn main(req, cf_env, ctx) {
  // Initialize env access with Cloudflare's env object
  helm_env.init(cf_env)
  
  // Now you can read variables
  let db_url = helm_env.get("DATABASE_URL")
  
  // Handle request
}

License

Apache-2.0

Search Document