Iwf Guide

Iwf Guide

Your First Project

This guide creates the smallest useful Iwf app. You will generate a project, change the first page, add a route, add a table, and learn where generated code goes.

1. Generate The App

From the Iwf repository root:

iwf new my-app
cd my-app
iwf build
./build/exec/my-app

Open:

http://127.0.0.1:8080

The generated project contains:

Path Purpose
src/Main.idrx The first page, route list, and server entry point.
my-app.ipkg The Idris package file for the app.
Application/Schema.sql PostgreSQL schema source of truth.
Application/Fixtures.sql Development and test seed data.
Application/Migrations/ Ordered SQL migrations.
build/generated/ Generated Idris source. Do not edit this by hand.

2. Change The First Page

Open src/Main.idrx.

Look for the page view and change the heading:

homeView : Html
homeView =
  fragment
    [ <title>My App</title>
    , <main id="main-content" class="mx-auto flex max-w-3xl flex-col gap-6 p-6">
        <section class="card">
          <h1>Hello from Iwf</h1>
          <p>Direct .idrx HTML is compiled into checked Idris.</p>
        </section>
      </main>
    ]

Rebuild and restart the executable:

iwf build
./build/exec/my-app

The build script preprocesses:

src/Main.idrx -> build/generated/Main.idr

Edit .idrx files. Do not edit generated .idr files under build/generated/.

3. Add A Route

Add another view and handler in src/Main.idrx:

aboutView : Html
aboutView =
  fragment
    [ <title>About</title>
    , <main id="main-content" class="mx-auto max-w-3xl p-6">
        <h1>About</h1>
        <p>This page is rendered by Idris on the server.</p>
      </main>
    ]

about : ControllerContext -> Response
about context =
  render context aboutView

Add it to the route list:

export
htmlHomeRoute : RouteSpec NoInput NoInput Response
htmlHomeRoute =
  get "/"

export
htmlHomeBinding : BoundRoute
htmlHomeBinding =
  handledBy htmlHomeRoute home

export
htmlAboutRoute : RouteSpec NoInput NoInput Response
htmlAboutRoute =
  get "/about"

export
htmlAboutBinding : BoundRoute
htmlAboutBinding =
  handledBy htmlAboutRoute about

export
htmlRoutes : Routes
htmlRoutes =
  [ htmlHomeBinding
  , htmlAboutBinding
  ]

appRoutes : Routes
appRoutes =
  htmlRoutes

Now visit:

http://127.0.0.1:8080/about

Internal links and forms are boosted by default. A direct browser request gets the framework shell plus this page content. An htmx request gets only the #main-content fragment.

Use the route value instead of spelling the URL again:

<a class="btn-primary" href={pathTo htmlAboutRoute}>About</a>

In a larger app, keep handlerless route specs in Web/Routes.idr, keep executable handledBy bindings in Web/RouteBindings.idr, and use pathTo from views. That way route changes update links at compile time instead of becoming broken URLs.

The canonical app shows this style:

homePath = pathTo htmlHomeRoute
articlePath = pathTo htmlArticleRoute (MkArticleSlugInput article.slug)

5. Add A Table

Open Application/Schema.sql and add:

CREATE TABLE articles (
  id BIGSERIAL PRIMARY KEY,
  slug TEXT NOT NULL UNIQUE,
  title TEXT NOT NULL,
  body TEXT NOT NULL,
  created_at TIMESTAMP NOT NULL DEFAULT now()
);

Rebuild:

iwf build

The starter keeps the schema files in place even before the app uses the database. The canonical app shows the full schema/typegen flow where Application/Schema.sql generates Generated.Schema before compilation.

6. What To Study Next

After this starter works, build one full slice with First CRUD Feature. It keeps the code in the app-facing interface while adding a path route, form, table, TypedSQL query, validation, auth, and live fragment.

Then study examples/canonical. It shows the normal larger app shape:

  • Application/ for schema, queries, mail, jobs, fixtures, and domain code.
  • Web/Routes.idr for route declarations.
  • Web/Controller/* for request handlers.
  • Web/View/*.idrx for HTML.
  • build/generated/ for generated Idris modules.

Next

Read First CRUD Feature.