README.md

# SpaPlug

[![Version](https://img.shields.io/hexpm/v/spa_plug.svg)](https://hex.pm/packages/spa_plug)
[![Hex Docs](https://img.shields.io/badge/hex-docs-purple.svg)](https://hexdocs.pm/spa_plug/)
[![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg)](https://opensource.org/license/apache-2-0/)

A [plug](http://github.com/elixir-lang/plug) for serving single page apps directly from your Elixir application.

## Installation

Add `spa_plug` to your dependencies in `mix.exs`:

```elixir
def deps do
  [
    # ...
    {:spa_plug, "~> 1.0.0"}
  ]
end
```
## Usage

 If using Phoenix, you can put it in `lib/your_app/endpoint.ex`. Order is important for plugs or middlewares in other frameworks. The same applies to `SpaPlug`, as it rewrites the `Plug.conn` as necessary. Also, keep in mind that potentially it can take over other routes. By default, `"/api"`, `"/dev"` and `"/metrics"` are already excluded, adjust `:except` for your specific needs or rearrange your plugs.

```elixir
  plug SpaPlug,
    at: "/",
    from: :your_app,
    only: YourAppWeb.static_paths(),
    # example: always up-to-date index page with immutable assets
    # ref: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#up-to-date_contents_always

    # your assets should be requested with the query param "vsn", or include the headers below
    # headers: [{"cache-control": "max-age=31536000, immutable"}],

    # this assumes your index.html is in the default priv/static directory as well, otherwise pass :from in :index_merge_opts
    index_merge_opts: [headers: [{"cache-control", "no-cache"}]]
```

## Documentation

See https://hex.pm/packages/spa_plug.

## Compatibility

The plug should be tested against the plug version listed in `mix.exs`.

## Rationale

Without the SSR on Node.js platform you will need to host your React or Vue.js apps as static assets. The typical solution would be to put a reverse proxy like Nginx in front. However, if you don't want to do it for some reason, there are no built-in or third party libraries for doing this. Plain `Plug.Static` would only serve the whole directory as it is.

Alternatively you can also just use catch all requests and call `Plug.Conn.send_file/5` or `Phoenix.Controller.render/2` in your router or controller, but it is not as performant or flexible despite the inconvenience. With this plug your phoenix application can keep the same docker image from `mix phx.gen.release --docker`. It works standalone or with a CDN as well, just point it to your servers as origin.