Skip to main content

README.md

# fcgi

[![Package Version](https://img.shields.io/hexpm/v/fcgi)](https://hex.pm/packages/fcgi)
[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/fcgi/)

A FastCGI Responder server for Gleam, designed to sit behind a reverse proxy such as Caddy. Speaks the FastCGI Responder role over a Unix domain socket and exposes a `gleam/http`-shaped handler API.

## Installation

```sh
gleam add fcgi
```

## Usage with `gleam/http`

```gleam
import fcgi
import gleam/bytes_tree
import gleam/erlang/process
import gleam/http/request.{type Request}
import gleam/http/response.{type Response}

pub fn main() {
  let assert Ok(_) =
    fcgi.new(handle_request)
    |> fcgi.listen_unix("/tmp/fcgi.sock")
    |> fcgi.start

  process.sleep_forever()
}

fn handle_request(
  _request: Request(fcgi.BodyReader),
  _ctx: fcgi.Context,
) -> Response(fcgi.ResponseData) {
  response.new(200)
  |> response.set_header("content-type", "text/plain; charset=utf-8")
  |> response.set_body(fcgi.bytes(bytes_tree.from_string("hello, joe!")))
}
```

## Usage with Wisp

A working Wisp adapter lives in [`examples/wisp_hello`](examples/wisp_hello). Copy
`examples/wisp_hello/src/wisp_fcgi.gleam` into your own project alongside the
`wisp` dependency, then wire it up:

```gleam
import fcgi
import gleam/erlang/process
import wisp
import wisp_fcgi

pub fn main() {
  let secret_key_base = wisp.random_string(64)

  let assert Ok(_) =
    handle_request
    |> wisp_fcgi.handler(secret_key_base)
    |> fcgi.new
    |> fcgi.listen_unix("/tmp/fcgi.sock")
    |> fcgi.start

  process.sleep_forever()
}

fn handle_request(_request: wisp.Request) -> wisp.Response {
  wisp.ok()
  |> wisp.string_body("hello, joe!")
}
```

The socket file is created when the server starts and removed on shutdown.

## Reverse-proxy with Caddy

```caddy
example.com {
    reverse_proxy unix//tmp/fcgi.sock {
        transport fastcgi {
            env PATH_INFO {http.request.uri.path}
        }
    }
}
```

HTTP-shaped FastCGI parameters become a `gleam/http.Request`. Trusted CGI metadata (client address, auth, script name, etc.) is passed separately as `fcgi.Context`, with anything unrecognized in `extra`.