ContextKit is a modular toolkit for building robust Phoenix/Ecto contexts with standardized CRUD operations. It helps reduce boilerplate code while providing powerful querying capabilities and built-in pagination support.
- 🚀 Automatic CRUD operation generation
- 🔍 Dynamic query building with extensive filtering options
- 📄 Built-in pagination support
- 🔧 Flexible and extensible design
- 🎯 Custom query options for complex filtering
The package can be installed by adding context_kit
to your list of dependencies in mix.exs
:
def deps do
[
{:context_kit, "~> 0.2.0"}
]
end
defmodule MyApp.Accounts.User do
use Ecto.Schema
schema "users" do
field :email, :string
field :name, :string
field :status, :string
timestamps()
end
end
2. Create a queries module (optional, functions can be defined directly in context module, and configured as queries: __MODULE__
):
defmodule MyApp.Accounts.UserQueries do
def apply_query_option({:with_active_posts, true}, query) do
query
|> join(:inner, [u], p in assoc(u, :posts))
|> where([_, p], p.status == "active")
end
end
defmodule MyApp.Accounts do
use ContextKit.CRUD,
repo: MyApp.Repo,
schema: MyApp.Accounts.User,
queries: MyApp.Accounts.UserQueries
end
By setting queries: __MODULE__
, you can define your custom query functions (apply_query_option/2
) directly in your context module,
eliminating the need for a separate queries module. This is particularly convenient for simpler contexts
where you don't need to share query logic across multiple modules.
# List all users
Accounts.list_users()
# List with filters and pagination
{users, pagination} = Accounts.list_users(
status: "active",
paginate: [page: 1, per_page: 20]
)
# Get single user
user = Accounts.get_user(123)
user = Accounts.get_user!(123) # Raises if not found
# Get one user by criteria
user = Accounts.one_user(email: "[email protected]")
# Create a new user
MyApp.Accounts.create_user(%{email: "[email protected]"})
# Update a user
MyApp.Accounts.update_user(user, %{email: "[email protected]"})
# Get a changeset for updates
MyApp.Accounts.change_user(user, %{email: "[email protected]"})
# Delete user
Accounts.delete_user(user)
Accounts.delete_user(email: "[email protected]")
All fields from the schema can be filtered on automatically.
Accounts.list_users(
filters: [
%{field: :email, op: :ilike, value: "@gmail.com"},
%{field: :status, op: :in, value: ["active", "pending"]},
%{field: :name, op: :like_or, value: ["john", "jane"]}
]
)
Any option not recognized as a field filter or standard query option is treated as a custom query option and passed to
the queries module's apply_query_option/2
function.
# Define custom query in your queries module
def apply_query_option({:with_recent_activity, true}, query) do
query
|> where([u], u.last_active_at > ago(1, "day"))
end
# Use in your context
Accounts.list_users(with_recent_activity: true)
When using ContextKit.CRUD
, you can configure:
repo
: Your Ecto repository moduleschema
: The Ecto schema modulequeries
: Module containing custom query functionsexcept
: List of operations to exclude (:list
,:get
,:one
,:delete
,:create
,:update
,:change
)plural_resource_name
: Custom plural name for list functions
- Create separate query modules for complex filtering logic
- Override generated functions when you need custom behavior
- Use pagination for large datasets
- Leverage custom query options for reusable query logic
Full documentation can be found at https://hexdocs.pm/context_kit.
- Fork it
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create new Pull Request
MIT License. See LICENSE for details.