Scrypath fits Phoenix best when your context is the application-facing boundary for search orchestration.

What Belongs In The Context

Keep these responsibilities in the context:

What Stays Out Of Controllers And LiveView

Do not teach controllers or LiveView modules to compose raw Repo and Scrypath.* calls as the main pattern.

That boundary matters because Phoenix web modules should stay focused on HTTP or UI concerns, while the context owns feature logic and operational decisions.

Example

defmodule MyApp.Content do
  alias MyApp.Blog.Post
  alias MyApp.Repo

  def search_posts(query, opts \\ []) do
    Scrypath.search(Post, query,
      Keyword.merge([backend: Scrypath.Meilisearch, repo: Repo], opts)
    )
  end

  def publish_post(post, attrs) do
    with {:ok, post} <- update_post(post, attrs),
         {:ok, _sync} <-
           Scrypath.sync_record(Post, post,
             backend: Scrypath.Meilisearch,
             sync_mode: :inline
           ) do
      {:ok, post}
    end
  end
end

MyApp.Content.search_posts/2 is the shared read path for controllers and LiveView. MyApp.Content.publish_post/2 is the write path that owns the sync decision.

Why This Boundary Holds Up

  • controllers stay thin
  • LiveView state stays UI-focused
  • sync failures and rebuild decisions stay close to the feature that owns them
  • docs and snippets can derive from one canonical example instead of teaching competing architectures