# `BeamExport.Plug`
[🔗](https://github.com/HataluliRandima/beam_export/blob/v0.1.0/lib/beam_export/plug.ex#L2)

Plug and Phoenix integration for CSV downloads.

Requires the optional `:plug` dependency.

## Staged export (recommended): `send_staged_export/4`

Streaming a database cursor straight to a slow HTTP client holds a database
connection for the whole download. `send_staged_export/4` instead writes the
export to a private temporary file first, releases the database connection, and
then serves the file with `Plug.Conn.send_file/3`.

Trade-off: the response starts only after the whole export has been written, so
very large exports keep the request waiting (and can hit proxy or load balancer
timeouts). For those, run `BeamExport.to_file/3` in a background job instead.

## Chunked export: `send_chunked_export/4`

Streams chunks with chunked transfer encoding as they are produced. The database
connection is held until the download finishes, so use it only for trusted,
fast clients (internal tools, service-to-service).

## Errors

Both functions raise when the export fails. `send_staged_export/4` raises
`BeamExport.ExportError` before any response is sent (resulting in a 500 in
Phoenix). `send_chunked_export/4` has already sent the status line when a
mid-stream error happens; the exception aborts the connection without the final
chunk, so HTTP clients detect the download as incomplete rather than receiving a
truncated file that looks complete.

Error messages never contain record data unless `include_record_in_errors: true`
is passed.

# `send_chunked_export`

```elixir
@spec send_chunked_export(
  Plug.Conn.t(),
  String.t(),
  Enumerable.t() | {module(), Ecto.Queryable.t()},
  keyword()
) :: Plug.Conn.t()
```

Streams CSV chunks directly to the client using chunked transfer encoding.

Stops reading the source as soon as the client disconnects. Holds the database
connection (for Ecto sources) for the whole download: prefer
`send_staged_export/4` for public or slow clients.

## Options

  * `:content_type` - defaults to `"text/csv"`.
  * `:charset` - defaults to `"utf-8"`.
  * every option accepted by `BeamExport.to_stream/2`.

# `send_staged_export`

```elixir
@spec send_staged_export(
  Plug.Conn.t(),
  String.t(),
  Enumerable.t() | {module(), Ecto.Queryable.t()},
  keyword()
) :: Plug.Conn.t()
```

Exports `source` to a private temporary file, then sends it as a download.

The temporary file lives in a new directory (mode `0700`) under `:tmp_dir`
and is created with mode `0600`. Both are deleted when the request process
exits, whether it finishes normally, crashes or the client disconnects.

## Options

  * `:tmp_dir` - parent directory for temporary exports. Defaults to
    `System.tmp_dir!()`. Never pass user input here.
  * `:content_type` - the response content type. Defaults to `"text/csv"`, or
    `"application/gzip"` when the export is gzip-compressed (`compress: true` or
    a filename ending in `.gz`).
  * `:charset` - the content type charset. Defaults to `"utf-8"` (none for gzip).
  * every option accepted by `BeamExport.to_file/3`.

`filename` is only used for the `content-disposition` header (sanitized,
RFC 6266 / RFC 5987 encoded) and the temporary file name (reduced to
`[A-Za-z0-9._-]`); it can never influence the temporary file's location.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
