ExAirtable (ExAirtable v0.2.9) View Source
Provides an interface to query Airtable bases/tables, and an optional server to cache the results of a table into memory for faster access and to avoid Airtable API access limitations.
The preferred mode of operation is to run in rate-limited and cached mode, to ensure that no API limitations are hit. The functions in the ExAirtable
base module will operate through the rate-limiter and cache by default.
If you wish to skip rate-limiting and caching, you can simply hit the Airtable API directly and get nicely-wrapped results by using the ExAirtable.Table
endpoints directly (see below for setup).
In either the direct-to-API or cached-and-rate-limited case, this library provides an %ExAirtable.Airtable.Record{}
and %ExAirtable.Airtable.List{}
struct which mirror the API results that Airtable provides. All results will be wrapped in those structs. We (currently) make no effort to convert Airtable's generic data representation into an app's local data domain, but instead provide a sane default that can be easily extended (via Ecto schemas for example). We also provide some convenience methods for things like record field retrieval and relationship matching (see below).
In addition to this page, be sure to check out the ExAirtable.Table
documentation page for more details about how to correctly import the Table behaviour.
Setup
Generally, you'll need to do two things:
- Add a
%ExAirtable.Config.Base{}
into your application configuration for each Airtable base that you intend to reference. - For each table within a base that you intend to query, define a module in your codebase that implements the
ExAirtable.Table
behaviour (by runninguse ExAirtable.Table
and implementing thebase()
andtable_name()
callbacks).
For example:
defmodule MyApp.MyAirtable do
use ExAirtable.Table
def base, do: %ExAirtable.Config.Base{
id: "your base ID",
api_key: "your api key"
}
def name, do: "Table Name"
end
With this module defined, you can hit the API directly (skipping rate-limiting and caching) like so:
iex> MyApp.MyAirtable.list()
%Airtable.List{}
iex> MyApp.MyAirtable.retrieve("rec12345")
%Airtable.Record{}
Rate-Limiting and Caching
Activating the ExAirtable.Supervisor
in your supervision tree (passing it any tables you've defined as above) confers a few advantages:
- All tables are automatically synchronized to a local (ETS) cache for much faster local response times.
- All requests are automatically rate-limited (5 requests per second per Airtable base) so as not to exceed Airtable API rate limits.
- Record creation requests are automatically split into batches of 10 (Airtable will reject larger requests).
To run a local caching server, you can include a reference to the ExAirtable
supervisor in your supervision tree, for example:
defmodule My.Application do
use Application
def start(_type, _args) do
children = [
# ...
# Configure caching and rate-limiting processes
{ExAirtable.Supervisor, {[MyApp.MyAirtable, MyApp.MyOtherAirtable, ...], sync_rate: :timer.seconds(15)}},
# ...
]
opts = [strategy: :one_for_one, name: MyApplication.Supervisor]
Supervisor.start_link(children, opts)
end
# ...
end
Note that the :sync_rate
(the rate at which tables are refreshed from Airtable) is optional and will default to 30 seconds if omitted.
Once you have configured things this way, you can call ExAirtable
directly, and get all of the speed and reliability benefits of caching and rate-limiting.
iex> ExAirtable.list(MyApp.MyAirtable)
{:ok, %ExAirtable.Airtable.List{}}
iex> ExAirtable.retrieve(MyApp.MyAirtable, "rec12345")
{:ok, %ExAirtable.Airtable.Record{}}
Examples + Playing Around
The codebase includes an example Table
(ExAirtable.Example.EnvTable
) that you can use to play around and get an idea of how the system works. This module uses environment variables as configuration. The included Makefile
provides some quick command-line tools to run tests and a console with those environment variables pre-loaded. Simply edit the relevant environment variables in Makefile
to point to a valid base/table name, and you'll be able to interact directly like this:
# first, run `make console`, then...
# retrieve data directly from Airtable's API...
iex> EnvTable.list
%ExAirtable.Airtable.List{records: [%Record{}, %Record{}, ...]}
iex> EnvTable.retrieve("rec12345")
%ExAirtable.Airtable.Record{}
# start a caching and rate-limiting server
iex> ExAirtable.Supervisor.start_link([EnvTable])
# get all records from the cache (without hitting the Airtable API)
iex> ExAirtable.list(EnvTable)
%ExAirtable.Airtable.List{}
Convenience Methods
Because certain tasks such as retrieving fields and finding related data happen so often, we put in a few convenience functions to make those jobs easier.
# grab a field from a record
iex> ExAirtable.Airtable.Record.get(record, "Users")
["rec1234", "rec3456"]
# find related table data based on a record ID
# (ie find all records in `list` where `"Users"` matches `"rec1234"`)
iex> ExAirtable.Airtable.List.filter_relations(list, "Users", "rec1234")
[%Record{fields: %{"Users" => ["rec1234", "rec3456"]}}, ...]
# convert Airtable field names into local field names
defmodule MyTable do
use ExAirtable
def schema do
%{"AirtableField" => "localfield"}
end
end
iex> record = %ExAirtable.Airtable.Record{id: "1", fields: %{"AirtableField" => "value"}}
iex> MyTable.to_schema(record)
%{"airtable_id" => "1", "localfield" => "value"}
# 👆 handy for ecto schema conversion
See the ExAirtable.Airtable.List
and ExAirtable.Airtable.Record
module documentation for more information about field, list, and schema retrieval, filtering and conversion.
Link to this section Summary
Functions
Create one or more records in your Airtable from an %Airtable.List{} request.
Delete a single record (by ID) from an Airtable.
Get all records from the given table module's cache
Same as list/0
, but raises on error.
Retrieve a single record from the table module's cache.
Same as retrieve/1
, but raises on error.
Update a record in your Airtable.
Link to this section Functions
Create one or more records in your Airtable from an %Airtable.List{} request.
If your list includes more than 10 records, the request will be split so as not to be rejected by the Airtable API.
This call is asynchronous, but the local cache will be automatically updated with any new records when the callback is successful.
See Service.create/2
for more details.
Delete a single record (by ID) from an Airtable.
If successful, the record will be deleted from the cache as well.
This call is asynchronous, but the local cache will be automatically updated when the callback is successful.
Get all records from the given table module's cache
Examples
iex> list(EnvTable)
{:ok, %Airtable.List{}}
Same as list/0
, but raises on error.
Retrieve a single record from the table module's cache.
Examples
iex> retrieve(EnvTable, "recLIY1WLOs8ocOAq")
{:ok, %Airtable.Record{}}
Same as retrieve/1
, but raises on error.
Update a record in your Airtable.
This call is asynchronous, but the local cache will be automatically updated when the callback is successful.
One particular thing to note is that Airtable won't allow updates for records that pass calculated fields. The objectionable_fields: ["list", "of", "fieldNames"]
option will allow you to point those out so that your update goes through.
See Service.create/2
for more details about options that can be passed.