filipecabaco

filipecabaco

Hi everyone,

I’ve been working on Francis, a lightweight framework built on top of Plug and Bandit that tries to get you from idea to a running endpoint with as little ceremony as possible. Think Sinatra for Elixir.

What it is

You use Francis in a module, define routes with get/post/ws/sse, and you have a running server. No routers, no controllers, no context boilerplate, just a function per route.

defmodule MyApp do
  use Francis

  get "/", fn _ -> %{hello: :world} end
  get "/:name", fn %{params: %{"name" => name}} -> "hello #{name}" end
  post "/", fn conn -> conn.body_params end

  ws "/chat", fn
    :join, socket -> {:reply, "welcome"}
    {:received, msg}, _ -> {:reply, msg}
  end

  sse "/events", fn
    :join, socket -> {:reply, %{id: socket.id}}
    {:received, m}, _ -> {:reply, m}
  end

  unmatched fn _ -> "not found" end
end

Return a string and it renders as HTML. Return a map and it renders as JSON. Return a Plug.Conn for full control.

Get started

mix archive.install hex francis
mix francis.new my_app
cd my_app
mix francis.server

Or add to an existing project:

{:francis, “~> 0.3”}

If you use Claude Code, install the Francis skill for route patterns, SSE/WS gotchas, and security guidance:

npx skills add francis-build/francis@francis-thinking

Links


Curious what the community thinks. It is not trying to replace Phoenix, it is for when you want something closer to raw Plug without writing the same boilerplate every time. Happy to answer questions.

Showing Posts 1 to 10

Benjamin-Philip

Benjamin-Philip

How exactly is Francis different from Plug’s built in Router DSL?

filipecabaco

filipecabaco OP

Honestly not much as the idea it’s just to be a boiler plate reducer more than anything. The implementation it’s basically a DSL to simplify the approach with some “sane defaults” focused on reducing as much as we can the boilerplate needed for a quick API

AndyL

AndyL

Curious what the community thinks.

Francis is great! Quick API endpoints, prototypes, admin apps…

Related: FrancisHtmx — Francis HTMX v0.2.2

Thank you @filipecabaco !!

PS Does anyone have experience embedding Francis into a Phoenix app?

I think it would be done in the Phoenix router…

defmodule MyAppWeb.Router do
  use MyAppWeb, :router

  <<< phoenix routes >>>

  scope "/francis" do
    forward "/", MyApp.FrancisRouter
  end
end 
filipecabaco

filipecabaco OP

Thank you :folded_hands:

honestly never tried it but can experiment with it and add as an example later

filipecabaco

filipecabaco OP

Ok I did try out and it does work as you said :+1: since it’s just Plug it’s all good

defmodule PhoenixEmbedWeb.Router do
  use PhoenixEmbedWeb, :router

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_live_flash
    plug :put_root_layout, html: {PhoenixEmbedWeb.Layouts, :root}
    plug :protect_from_forgery
    plug :put_secure_browser_headers
  end

  pipeline :api do
    plug :accepts, ["json"]
  end

  scope "/", PhoenixEmbedWeb do
    pipe_through :browser

    get "/", PageController, :home
  end

  # Forward all /api requests to the Francis API router.
  # Phoenix strips the /api prefix, so Francis routes are relative (e.g. /items).
  forward "/api", PhoenixEmbedWeb.FrancisApi
end
AndyL

AndyL

A new super-power unlocked!

Thanks again for this great library. :heart_eyes:

josefrichter

josefrichter

Thanks for Francis @filipecabaco ! Always loved Sinatra for prototyping and side projects, so I love Francis by definition.

So much so that I just migrated my personal website https://josefrichter.design to it, and added a simple chat widget there using Francis’ SSE, telegram bot and Mac widget.

Also created francis_template | Hex in the process, because I needed it and the francis_htmx didn’t fit my needs.

LostKobrakai

LostKobrakai

It depends only on francis — no phoenix_html, no heavy view layer.

I’d love to know what you feel is heavy. phoenix_html or phoenix_template are maybe like twice the loc of francis_template and most of the additional loc are the additional form abstraction of phoenix_html. None of that is a heavy dependency.

josefrichter

josefrichter

You are right, that was gross overstatement. Fixing! Thank you

filipecabaco

filipecabaco OP

Thank you so much! Really glad to hear that.

I need to rethink fully francis_htmx as it really got way out of hand in complexity which was a sign of bad implementation :sweat_smile:

Will check francis_template. I do like the idea as again follows the same ideas as francis - boilerplate reduction :heart:

— All posts loaded —

Where Next? Top

Trending in Announcing Top

wojtekmach
Hey everyone! Req is an HTTP client for Elixir that I’ve been working on for quite some time. There is already a lot of HTTP clients out...
New
handnot2
Samly can be used to enable SAML 2.0 Single Sign On in a Plug/Phoenix application. This library uses Erlang esaml to provide plug enabl...
New
woylie
Flop is an Elixir library that applies filtering, ordering and pagination parameters to your Ecto queries. offset-based pagination with...
New
MRdotB
I needed to reuse React components from my Chrome extension in my Phoenix/LiveView backend. I noticed that for Svelte/Vue, there are live...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
fuelen
Hi all! I want to present a small library which provides a mix task for generating an Entity-Relationship Diagram for Ecto schemas. You...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
AstonJ
This showed up on my feed.. anyone heard of it? Just hype? Ox Alpha is a reasoning model designed for coding, sustained ag...
New
sergio
It’s not that it’s vocabulary is too advanced. It’s something worse. I get lost trying to follow even a paragraph written by Claude. It’...
New
sorenone
Today we’re releasing Oban for Python. Not an Oban client in Python. Not a pythonx wrapper embedded in Elixir. Nope, it’s a fully operati...
New
akoutmos
@hugobarauna, Dr. Dimitrios Koutmos (my brother) and I (Alex Koutmos) have been hard at work on writing a book on how you can use Elixir ...
New
pferriby
Introductory paragraph I’ll be looking for a keen junior or someone that has a couple of years experience in the real world (so you’ve be...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews