Skip to main content

Introduction

The Airtable data provider enables you to build Refine applications with Airtable, a cloud-based platform that combines the simplicity of a spreadsheet with the power of a database.

Installation

Install the Airtable data provider package:
npm install @refinedev/airtable

Setup

1

Get Airtable credentials

Get your API key and Base ID from your Airtable account:
  • API Key: Account > Generate API Key
  • Base ID: Available in the API documentation for your base
2

Configure data provider

Configure the Refine app with the Airtable data provider:
import { Refine } from "@refinedev/core";
import dataProvider from "@refinedev/airtable";

const App = () => {
  return (
    <Refine
      dataProvider={dataProvider("YOUR_API_KEY", "YOUR_BASE_ID")}
    >
      {/* Your app content */}
    </Refine>
  );
};

Basic Usage

Get List

Fetch records from an Airtable table:
import { useList } from "@refinedev/core";

const { data } = useList({
  resource: "posts",
  pagination: {
    current: 1,
    pageSize: 10,
  },
  sorters: [
    {
      field: "createdAt",
      order: "desc",
    },
  ],
});

Get One

Fetch a single record by ID:
import { useOne } from "@refinedev/core";

const { data } = useOne({
  resource: "posts",
  id: "recABCDEF123456",
});

Create

Create a new record:
import { useCreate } from "@refinedev/core";

const { mutate } = useCreate();

const handleSubmit = (values) => {
  mutate({
    resource: "posts",
    values: {
      title: values.title,
      content: values.content,
      status: "draft",
    },
  });
};

Update

Update an existing record:
import { useUpdate } from "@refinedev/core";

const { mutate } = useUpdate();

const handleUpdate = (id, values) => {
  mutate({
    resource: "posts",
    id,
    values: {
      title: values.title,
      content: values.content,
    },
  });
};

Delete

Delete a record:
import { useDelete } from "@refinedev/core";

const { mutate } = useDelete();

const handleDelete = (id) => {
  mutate({
    resource: "posts",
    id,
  });
};

Filtering

Airtable supports filtering using formulas:
import { useList } from "@refinedev/core";

const { data } = useList({
  resource: "posts",
  filters: [
    {
      field: "status",
      operator: "eq",
      value: "published",
    },
    {
      field: "views",
      operator: "gte",
      value: 100,
    },
  ],
});

Sorting

Sort records by one or more fields:
import { useList } from "@refinedev/core";

const { data } = useList({
  resource: "posts",
  sorters: [
    {
      field: "createdAt",
      order: "desc",
    },
    {
      field: "title",
      order: "asc",
    },
  ],
});

Working with Attachments

Handle file attachments in Airtable:
import { useCreate } from "@refinedev/core";

const { mutate } = useCreate();

const handleSubmit = (values, files) => {
  // Upload files first and get URLs
  const attachments = files.map((file) => ({
    url: file.url,
    filename: file.name,
  }));

  mutate({
    resource: "posts",
    values: {
      title: values.title,
      content: values.content,
      attachments, // Airtable attachment field
    },
  });
};

Linked Records

Work with linked records (foreign keys):
import { useCreate } from "@refinedev/core";

const { mutate } = useCreate();

const handleSubmit = (values) => {
  mutate({
    resource: "posts",
    values: {
      title: values.title,
      content: values.content,
      author: ["recAuthorID123"], // Link to author record
      categories: ["recCat1", "recCat2"], // Link to multiple categories
    },
  });
};

Views

Query data from specific Airtable views:
import { useList } from "@refinedev/core";

const { data } = useList({
  resource: "posts",
  meta: {
    view: "Published Posts", // Use a specific view
  },
});

Field Selection

Select specific fields to retrieve:
import { useList } from "@refinedev/core";

const { data } = useList({
  resource: "posts",
  meta: {
    fields: ["title", "status", "createdAt"],
  },
});

Complete Example

import { Refine } from "@refinedev/core";
import dataProvider from "@refinedev/airtable";
import routerProvider from "@refinedev/react-router";
import { BrowserRouter } from "react-router";

const API_KEY = process.env.REACT_APP_AIRTABLE_API_KEY!;
const BASE_ID = process.env.REACT_APP_AIRTABLE_BASE_ID!;

const App = () => {
  return (
    <BrowserRouter>
      <Refine
        dataProvider={dataProvider(API_KEY, BASE_ID)}
        routerProvider={routerProvider}
        resources={[
          {
            name: "posts",
            list: "/posts",
            create: "/posts/create",
            edit: "/posts/edit/:id",
            show: "/posts/show/:id",
          },
          {
            name: "categories",
            list: "/categories",
          },
        ]}
      >
        {/* Your routes and pages */}
      </Refine>
    </BrowserRouter>
  );
};

export default App;

Environment Variables

Store your Airtable credentials securely:
# .env
REACT_APP_AIRTABLE_API_KEY=your_api_key_here
REACT_APP_AIRTABLE_BASE_ID=your_base_id_here
Then use them in your app:
const App = () => {
  return (
    <Refine
      dataProvider={dataProvider(
        process.env.REACT_APP_AIRTABLE_API_KEY!,
        process.env.REACT_APP_AIRTABLE_BASE_ID!
      )}
    >
      {/* Your app content */}
    </Refine>
  );
};

Handling Rate Limits

Airtable has API rate limits. The data provider handles these automatically, but you can implement additional error handling:
import { useList } from "@refinedev/core";

const { data, error, isLoading } = useList({
  resource: "posts",
});

if (error) {
  if (error.message.includes("rate limit")) {
    // Handle rate limit error
    console.log("Rate limit exceeded. Please try again later.");
  }
}

Supported Operators

The Airtable data provider supports the following filter operators:
  • eq: Equals
  • ne: Not equals
  • lt: Less than
  • lte: Less than or equal
  • gt: Greater than
  • gte: Greater than or equal
  • contains: Contains (text search)

Limitations

  • Airtable API has rate limits (5 requests per second per base)
  • Record IDs must start with “rec”
  • Maximum 100 records per request
  • Some complex formulas may not be supported

Best Practices

  1. Use environment variables for API credentials
  2. Cache frequently accessed data
  3. Use views to pre-filter data in Airtable
  4. Minimize the number of fields retrieved
  5. Handle rate limits gracefully

Next Steps

Data Providers Overview

Learn about other data providers

Airtable API Docs

Explore Airtable API features

Build docs developers (and LLMs) love