DEV Community

Fernando Correa de Oliveira
Fernando Correa de Oliveira

Posted on • Edited on

Crolite: filling the "Mojolicious::Lite" gap in the Cro ecosystem

Introduction

When I start something new in Raku with Cro, I almost always begin with a mental sketch: two or three routes, a response shape, maybe a typed segment. In the Perl world I leaned heavily on Mojolicious::Lite for that prototyping phase. In Cro—powerful and modular as it is—I missed an immediate “lite mode”: no manual wiring of server, pipeline, and router just to test a thought. Out of that friction came Crolite: a thin layer that re‑exports Cro's routing keywords and adds a multi MAIN with quick exploration commands.

What Crolite Is

  • Goal: Make minimal HTTP prototypes trivial while ideas mature.
  • Approach: Reuse Cro's router directly; no new DSL.
  • Deliverable: A collected RouteSet + a tiny embedded CLI.
  • Philosophy: “Start now; graduate later to a full Cro app.”

Installation

From a local checkout (inside the project directory):

zef install .
Enter fullscreen mode Exit fullscreen mode

Once published:

zef install Crolite
Enter fullscreen mode Exit fullscreen mode

First Example

File example.raku:

use Crolite;

get -> $any {
    content 'text/plain', "Hello: $any";
}

delete -> 'thing', Int $id {
    content 'application/json', %( :$id );
}
Enter fullscreen mode Exit fullscreen mode

List derived routes:

raku example.raku routes
Enter fullscreen mode Exit fullscreen mode

Run a development server:

raku example.raku --host=127.0.0.1 --port=3000 daemon
Enter fullscreen mode Exit fullscreen mode

Test a route without a persistent daemon (ephemeral in‑memory request):

raku example.raku GET /thing/42
Enter fullscreen mode Exit fullscreen mode

Suggested Workflow

  1. Sketch routes and response formats.
  2. Run raku app.raku routes to confirm patterns.
  3. Fire single requests: raku app.raku GET /foo/123.
  4. Iterate until it stabilizes.
  5. Promote to a full Cro project if you need richer middleware, structured logging, TLS, etc.

CLI Options

Command Purpose
routes Print summary of registered endpoints
[--host=0.0.0.0] [--port=10000] daemon Start simple Cro server
GET <path> Single in‑memory GET
POST <path> Single POST (no custom body)
PUT <path> Single PUT
DELETE <path> Single DELETE
--method=<VERB> http <path> Generic form for any method

Stop the daemon with Ctrl+C (SIGINT is trapped for graceful shutdown).

Dynamic Segments & Typing

use Crolite;

get -> 'greet', Str $name {
    content 'text/plain', "Hi $name!";
}

post -> 'sum', Int $a, Int $b {
    content 'application/json', %( total => $a + $b );
}
Enter fullscreen mode Exit fullscreen mode

Test:

raku app.raku GET /greet/Ana
raku app.raku POST /sum/2/5
Enter fullscreen mode Exit fullscreen mode

Returning JSON

Just produce a Hash or Map:

get -> 'status' {
    content 'application/json', %( service => 'ok', ts => DateTime.now );
}
Enter fullscreen mode Exit fullscreen mode

Hooks (Before / After)

If you manually add before or after handlers to the underlying RouteSet, Crolite includes them when composing the application for daemon:

use Crolite;

$*CRO-ROUTE-SET.before: {
    # Simple logging / auth stub
    proceed;
}

get -> 'ping' { content 'text/plain', 'pong' }
Enter fullscreen mode Exit fullscreen mode

More Complete Example

use Crolite;

get -> 'health' {
    content 'application/json', %( ok => True );
}

put -> 'echo', Str $msg {
    content 'text/plain', $msg.uc;
}

post -> 'calc', Int $x, Int $y {
    content 'application/json', %( sum => $x + $y, prod => $x * $y );
}

delete -> 'soft', Int $id {
    content 'application/json', %( deleted => $id, soft => True );
}
Enter fullscreen mode Exit fullscreen mode

Quick ping:

raku app.raku GET /health
Enter fullscreen mode Exit fullscreen mode

Server:

raku app.raku --port=4000 daemon
Enter fullscreen mode Exit fullscreen mode

Ergonomics While Prototyping

  • Rapid changes: Save & re‑run; no auto‑reloader (yet).
  • Inspection: routes surfaces path typos immediately.
  • Atomic requests: Avoids opening another terminal for curl just to see a body.

Current Limitations

  • No structured logging out of the box.
  • No built‑in TLS / websockets / streaming presets.
  • No hot reload.
  • Experimental API (may shift).

When to Migrate to a Full Cro App

  • You need chained middleware (auth, tracing, rate limiting).
  • You require richer body parsing / serialization customization.
  • You integrate multiple services or supervised components.
  • You need observability (metrics, distributed tracing, advanced logs).

Possible Future Roadmap

  • Optional reload in daemon mode.
  • Alternate tabular output for routes.
  • Lightweight latency / metrics helper.
  • Test skeleton generator for promotion phase.

Quick Testing Tips

  • Use the embedded CLI to validate the contract before formal tests.
  • When formalizing, reuse Cro::HTTP::Test (mirrors what the CLI verbs do).

Wrapping Up

Crolite does not compete with the full flexibility of a structured Cro project; it lowers the time to first useful response when exploring HTTP ideas in Raku. If you also miss the lightness of Mojolicious::Lite, try making the first step of each spike just:

use Crolite;
Enter fullscreen mode Exit fullscreen mode

Then, once the shape hardens, graduate to something more robust.


Suggestions, issues, and PRs welcome.

Top comments (2)

Collapse
 
p6steve profile image
p6steve

very nice!

thought I would try adding some HTML...

use Crolite;
use Air::Functional :CRO;

get -> 'greet', Str $name {
    content 'text/html', h1 "Hi $name!";
}
Enter fullscreen mode Exit fullscreen mode

PS. I think you need daemon after the port in your example
raku app --port=4000 daemon

Collapse
 
fco profile image
Fernando Correa de Oliveira • Edited

Thank you

(The port after the command works here to me.)
Update: sorry, for some reason I assumed it was working here, but I just tested and you are right. I tried a few ways to fix it with no success. I’ll fix the post as soon as possible.