# EctoHrana

Prototype of an Ecto adapter for libSQL servers (Turso, Bunny Database,
`sqld`), over the Hrana protocol on HTTP, in pure Elixir.

libSQL speaks SQLite's dialect, so the SQL generation, the migrations, the
loaders/dumpers and the constraint errors are `ecto_sqlite3`'s: only the driver
(`EctoHrana.Connection`) is new.

```elixir
defmodule MyApp.Repo do
  use Ecto.Repo, otp_app: :my_app, adapter: Ecto.Adapters.Hrana
end

config :my_app, MyApp.Repo,
  uri: "libsql://my-db.turso.io",
  auth_token: System.fetch_env!("LIBSQL_AUTH_TOKEN")
```

## Motivations

- **Pure Elixir, no native code.** `ecto_libsql` wraps libSQL's Rust library
  in a NIF: it needs a precompiled binary for the target (or a Rust
  toolchain), and a crash in native code takes the whole VM down. Against a
  remote server, the native library is not needed: Hrana is HTTP and JSON,
  which `Req` and `Jason` speak. This adapter builds and runs wherever the
  BEAM does, and a failing connection is an ordinary process crash, restarted
  by `DBConnection`.
- **Reuse rather than rewrite.** libSQL speaks SQLite's dialect, and
  `ecto_sqlite3` already generates it, runs the migrations, loads and dumps
  the types and maps the constraint errors. Only the transport is new, so the
  behaviour is the one SQLite users already know, `ash_sqlite` included.
- **Hosted libSQL, from Elixir.** Turso and Bunny Database expose libSQL over
  Hrana: this lets an Ecto (or Ash) application use them like any other
  database, with transactions and savepoints.

## Limitations

- A prototype: it passes its tests against `sqld` and a full Ash application
  test suite, but has not been used against a remote server in production.
- Each statement is an HTTP round trip to the server: against a remote
  server, the network latency dominates. Keep the application close to the
  database and avoid N+1 queries.
- Streams are emulated: `Repo.stream/2` fetches all the rows at once.
- No embedded replicas (a local copy synchronised with the server), which
  need libSQL's native library.
- The SQL generation comes from `ecto_sqlite3`'s internal modules, so the
  `ecto_sqlite3` versions this library works with are pinned.

As with `ecto_sqlite3`, `query_many/4` is not supported: run the statements
one by one.

## Development

The tests and `bench/compare.exs` need a libSQL server:

    docker run -p 127.0.0.1:8080:8080 ghcr.io/tursodatabase/libsql-server:latest

## License

MIT, see [LICENSE](LICENSE).
