PlumeKit

Build web apps in Swift.
Run them anywhere.

Write your app once (the pages, the data, the logic) and run the exact same code on your own server, on Cloudflare or on AWS. There is nothing to rewrite and no versions to keep in sync. And it's a genuine delight to build with.

Models are plain Swift

Describe a table as a class and query it through typed columns. A typo or a type mismatch is a compile error, not a surprise in production. The same model runs on SQLite, Postgres and Cloudflare D1.

The ORM
Models/Post.swift
@Model final class Post: Model {
    var id: Int
    var title: String
    var published = false
}

let latest = try await Post
    .where(Post.published == true)
    .order(by: Post.id, .descending)
    .all()

Handlers turn requests into responses

A route is a function from a request to a response. Forms, sessions, validation and flash messages are built in, so the everyday paths stay short.

Routing
Sources/App/Routes.swift
app.get("/posts") { _ in
    let posts = try await Post.where(Post.published == true).all()
    return .view(postsPage(posts: posts))
}

app.post("/posts") { request in
    let post = Post(title: request.form["title"] ?? "")
    _ = try await post.save()
    return .redirect(to: "/posts").flash("Post created")
}

Views compile with your app

Plume templates are components with arguments and slots. They compile to Swift render functions: fast, escaped by default and the same output on every target.

Plume views
Views/PostsPage.plume
@component PostsPage(posts: [Post]) {
  @Layout(title: "Posts") {
    <ul>@for post in posts {
      <li>{post.title}</li>
    }</ul>
  }
}

Deploying is one command

The app you ran on your laptop runs unchanged on your own server, on Cloudflare Workers and on AWS Lambda. plumekit deploy migrates, builds and ships it.

Deploying
Terminal
$ plumekit new myapp
$ ./plumekit dev
  → http://127.0.0.1:8080
$ ./plumekit deploy
  → Cloudflare Worker live

Start where it suits you