A set of sensible defaults for using ExAirtable with Phoenix, using Ecto's embedded schemas for validation.
The overall goal is to make working with ExAirtable in Phoenix projects similar enough to working with Ecto to provide a migration pathway should your app outgrow Airtable as a back-end. At the same time, many (most?) apps will never come close to outgrowing Airtable, so you might as well have a nice time using it :)
Because ExAirtable
returns general-purpose structs that conform to the Airtable API specifications, it's often nice to be able to further validate those structs to domain-specific models. For example, ExAirtable
might return something like this for your "blogging" app:
%ExAirtable.Airtable.Record{
createdTime: "2016-05-20T01:00:23.000Z",
fields: %{
"Email" => "[email protected]",
"Comments" => ["recg6cWUjaSxrShNy", "recE2sCYzvbgbDUAZ",
"recJyXQOKlQRS38n7"],
"Name" => "Some App User",
"Useless Field" => "Whatever"
},
id: "recIbeH41sLn4nF6t"
}
...when what you really want is something more like this:
%MyApp.User{
name: "Some App User",
email: "[email protected]",
comments: [%MyApp.Comment{}, %MyApp.Comment{}, ...]
}
The thinking is, since Ecto has such nice embedded schema support, why reinvent the wheel? This library allows you to do a relatively small amount of setup by defining models that link to Airtable, optionally filter the resulting values, and then use Ecto embedded schemas to validate and cast them into domain models.
Continuing the above "blogging" app example, you might have a model defined like this:
defmodule MyApp.User do
use ExAirtable.Phoenix.Model
# Details about your Base
def base do
%ExAirtable.Config.Base{
api_key: "your key",
id: "your base ID"
}
end
# This would be the name of the table in your Airtable base
@impl ExAirtable.Table
def name, do: "Users"
# Convert the Airtable field names to your app's field names.
@impl ExAirtable.Table
def schema do
%{
"Email" => :email,
"First Name" => :first_name,
"Last Name" => :last_name
}
end
# This is Ecto, doing the work of casting and schema definition
embedded_schema do
field(:airtable_id, :string)
field(:email, :string)
field(:first_name, :string)
field(:last_name, :string)
end
@doc """
Returns a `%User{}` given a valid set of attributes.
On failure, returns `{:error, %Ecto.Changeset{}}`
"""
@impl ExAirtable.Phoenix.Model
def validate(attrs \\ %{}) do
%__MODULE__{}
|> cast(attrs, [:airtable_id, :email, :first_name, :last_name])
|> validate_required([:airtable_id, :email, :first_name])
|> apply_action(:save)
end
end
Once you've defined your model object this way, you can use the functions in ExAirtable.Phoenix.Repo
to automatically retrieve, convert, etc. items out of your ExAirtable cache:
defmodule MyApp.UserContext do
alias ExAirtable.Phoenix.Repo
alias MyApp.User
@doc """
Find a User by email
Returns `{:ok, %User{}}` on success.
Returns `{:error, :not_found}` on failure.
"""
def find_by_email(email) do
if user = Repo.one(User, :email, email) do
{:ok, user}
else
{:error, :not_found}
end
end
@doc """
List all valid users
"""
def list_users do
Repo.all(User)
end
end
Be sure to check out ExAirtable.Phoenix.Repo
for more details and examples. Also, make sure to look at the ExAirtable
docs themselves for more information about how you configure ExAirtable
for your app.
If available in Hex, the package can be installed
by adding ex_airtable_phoenix
to your list of dependencies in mix.exs
:
def deps do
[
{:ex_airtable_phoenix, "~> 0.1.0"}
]
end
If you prefer to use the latest build, point straight to github:
[
{:ex_airtable_phoenix, git: "https://github.com/dmerand/ex_airtable_phoenix.git"}
]
More documentation can be found at https://hexdocs.pm/ex_airtable_phoenix.