Minimal API-only Phoenix app that depends on Scrypath via path: "../.." from the monorepo root. It shows the same patterns as the main README (searchable schema, Scrypath.sync_record/3, Scrypath.search/3 with repo:) against real Postgres and real Meilisearch, plus Oban wired for a second integration path (sync_mode: :oban) so the example matches queue-backed production apps. It also demonstrates the related-data fan-out path via Scrypath.sync_related/3, showing how an Author rename propagates to that author's Post search documents through both inline and Oban-backed fan-out modes.
This README is the proof/runbook surface for the real-service path. The HexDocs guides teach the public request-edge boundary and API shape; this example proves the operational path, CI parity, env vars, and local smoke commands.
From the repository root, the canonical maintainer entrypoint for this live path is mix verify.adopter --live. That root task checks for SCRYPATH_EXAMPLE_INTEGRATION, PGPORT, and SCRYPATH_MEILISEARCH_URL, verifies Postgres and Meilisearch are reachable, then shells into this example with cd examples/phoenix_meilisearch && mix deps.get && mix test (the same sequence CI uses).
- Elixir 1.17+ and Erlang/OTP as in the parent project
- Docker with Compose v2
compose.yaml starts:
- Postgres 16 on host port 5433 (avoids colliding with a local Postgres on 5432). Override with
PGPORTif you change the mapping. - Meilisearch v1.15 on host port 7700 (same image tag as library CI).
Both services attach to an explicit bridge network scrypath_phx_meili_net so you can add an app container later without reworking the layout.
docker compose up -d| Variable | Purpose | Default |
|---|---|---|
PGPORT |
Postgres TCP port on localhost |
5433 (config/dev.exs, config/test.exs) |
SCRYPATH_MEILISEARCH_URL |
Meilisearch base URL | http://127.0.0.1:7700 |
SCRYPATH_EXAMPLE_INTEGRATION |
When 1 / true, ExUnit runs @moduletag :integration smoke tests (inline + Oban paths, including fan-out smokes) |
unset (those tests excluded) |
CI uses the same Meilisearch v1.15 image tag as this compose.yaml (see root CONTRIBUTING.md for which GitHub Actions jobs run live Meilisearch). This README is the single detailed runbook for the example; the golden path links here instead of duplicating the table.
On pull requests and pushes to main, job phoenix-example-integration starts Postgres 16 and Meilisearch v1.15 as workflow services, sets SCRYPATH_EXAMPLE_INTEGRATION=1, PGPORT=5433, and SCRYPATH_MEILISEARCH_URL=http://127.0.0.1:7700, then runs (from the repository root):
cd examples/phoenix_meilisearch
mix deps.get
mix testCI uses that mix deps.get then mix test sequence as the test driver for this example. ./scripts/smoke.sh is local orchestration (Docker Compose + the same env defaults); it is not what GitHub Actions invokes in place of the mix steps above.
From this directory (with Compose running or let the script start it):
./scripts/smoke.shCI-shaped default: the script always runs docker compose down on exit (trap), so machines do not leak containers—best for automation and first-time contributors.
Local iteration: pass --keep-up to skip teardown and reuse the same Postgres/Meilisearch while you edit tests (./scripts/smoke.sh --keep-up). When finished, run docker compose down from this directory.
The script:
- Runs
docker compose up -dand fails fast if Postgres or Meilisearch is not healthy within 60s. - Sets
SCRYPATH_EXAMPLE_INTEGRATION=1so ExUnit includes@moduletag :integrationsmoke tests. - Runs
mix deps.getandmix test(thetestalias creates the test DB and migrates, including Oban migrations).
Integration coverage:
- Inline — insert row,
Scrypath.sync_recordwithsync_mode: :inline, thenScrypath.searchwith hydration viaScrypathDemo.Repo. - Oban — same shape with
sync_mode: :obanandScrypathDemo.Oban; tests useconfig :scrypath_demo, Oban, testing: :inlineso jobs run in-process deterministically while still exercising enqueue metadata andScrypath.Oban.UpsertWorkeragainst live Meilisearch. - Fan-out Inline — insert Author + Post, rename the Author via
ScrypathDemo.Blog.update_author/3withsync_mode: :inline(exercisesScrypath.sync_related/3), then assert the Post search document reflects the newauthor_name. Verifies the inline resolver-arity path (resolver receives Author structs). - Fan-out Oban — same Author rename via
update_author/3withsync_mode: :oban, asserting{:ok, %{mode: :oban, status: :accepted}}. Undertesting: :inline, theScrypath.Sync.RelatedWorkerruns in-process andawait_searchconfirms the renamedauthor_namereaches the Post document. Verifies the oban resolver-arity path (resolver receives Author document IDs).
With Compose Postgres still required (Sandbox + ConnCase):
docker compose up -d
export PGPORT=5433
mix deps.get
mix testIntegration tests are excluded unless SCRYPATH_EXAMPLE_INTEGRATION=1 (or true). That loop is what you want when iterating quickly without tearing Compose down each time (contrast with ./scripts/smoke.sh default, which always stops Compose on exit).
docker compose up -d
export PGPORT=5433
mix deps.get
mix ecto.create
mix ecto.migrate
mix phx.server- Phoenix guides
- Scrypath README in the repository root