The Elm-Pages Architecture

Let's look at the lifecycle of a request in an elm-pages app. page

This will walk through 3 different interactions.

1. Open Page 1. Click Button 1. Submit Form

These 3 interactions show the key components of The elm-pages Architecture.

Interaction 1 - Open Page. page

If we open our browser to a server-rendered elm-pages Route https://my-app.com/feed, it will render from our Route module app/Route/Feed.elm.

1a - Resolving data. page

Resolve BackendTask, elm-pages Engine, custom-backend-task.ts, Backend App (Server)

1. data is resolved first 1. The elm-pages Engine resolves our data, performing HTTP requests, reading data, and running any custom tasks we've defined. 1. BackendTask's BackendTask.Custom.run are executed by running our custom-backend-task Node module, which elm-pages transpiles using ESBuild.

[…]

The Code page

We could define our Route Module like this: app/Route/View/Slug_/SPLAT__.elm

Compare our view function with the Feed example:

view : RouteBuilder.App Data ActionData RouteParams -> Shared.Model -> Model -> View.View (PagesMsg Msg) view app shared model = { title = "Feed" , body = [ navbarView app.data.user ToggleMenu model.showMenu , postsView app ] }

?: navbarView, postsView

~

elm-pages makes building websites fun again. i've (Dwight Doane) throughly enjoyed spinning up my latest website. I really hope elm on the frontend adopts a BackendTask style api for FFI instead of ports. i love being able to grab just a bit from a custom-backend-task and combine that with another BackendTask, keeping most of the business in Elm. Building a blog with elm-pages has been way easier than with next.js imho Im pulling in a list of markdown files' names with node in a custom backend task, but then i transform and combine them into a List of backendtasks to load all those markdown files' data to turn into a list of blog posts. (idk if thats the right way, but it works great) post

data : BackendTask FatalError Data data = (BackendTask.Custom.run "blogPosts" Encode.null (Decode.list Decode.string) |> BackendTask.allowFatal ) |> BackendTask.map (\files -> files |> List.filter (String.contains ".md") |> List.map (\file -> [ "./posts/", file ] |> String.concat |> BackendTask.File.bodyWithFrontmatter BlogPost.decoder |> BackendTask.allowFatal ) ) |> BackendTask.andThen BackendTask.combine |> BackendTask.map Data

script/custom-backend-task.ts

export async function blogPosts() { const dir = path.join("posts"); const files = await readdir(dir); return files; }