Airtable API has a limit of 5 requests per second per base, which applies to all users, both free and paid.
The benefits of adding a layer of proxy on top of a public API are bypassing rate limits by caching data and securing the API keys.
- First Get Airtable Personal access tokens from here https://airtable.com/create/tokens
- Add a env variable in .env.development for Airtable Token
terminal
CopyAIRTABLE_TOKEN=""
- Open server.js file and add two lines in this file.
JavaScript
Copyconst airtableProxyRoutes = require('./app/routes/airtableProxyRoutes'); app.use('/airtable/proxy', airtableProxyRoutes);
- Create a route file in the app/routes directory named airtableProxyRoutes.js
JavaScript
Copy// app/routes/airtableProxyRoutes.js const express = require('express'); const router = express.Router(); const airtableController = require('../controllers/airtableController'); router.get('/bases', airtableController.getBases); router.get('/bases/:baseId/tables', airtableController.baseSchema); router.get('/:baseId/:tableIdOrName', airtableController.listRecords); router.get('/:baseId/:tableIdOrName/:recordId', airtableController.getRecord); router.patch('/:baseId/:tableIdOrName', airtableController.updateMutipleRecords); router.patch('/:baseId/:tableIdOrName/:recordId', airtableController.updateRecord); router.post('/:baseId/:tableIdOrName', airtableController.createRecords); router.delete('/:baseId/:tableIdOrName', airtableController.deleteMultipleRecords); router.delete('/:baseId/:tableIdOrName/:recordId', airtableController.deleteRecord); module.exports = router;
- Create a controller file in the app/controllers directory named airtableController.js
JavaScript
Copy// app/controllers/airtableController.js const { responseList } = require('../errors/responseList'); const { fetchData } = require('../helpers/functions'); const BASE_URL = 'https://api.airtable.com/v0'; const headers = { Authorization: `Bearer ${process.env.AIRTABLE_TOKEN}`, }; // List airtable all the bases exports.getBases = async (req, res) => { const queryParams = req.query; const url = `${BASE_URL}/meta/bases`; try { const data = await fetchData(url, 'GET', null, headers, queryParams); res.status(200).send(data); } catch (error) { res.status(500).send(responseList(500, error.message)); } }; // Base table schema exports.baseSchema = async (req, res) => { const queryParams = req.query; const { baseId } = req.params; const url = `${BASE_URL}/meta/bases/${baseId}/tables`; try { const data = await fetchData(url, 'GET', null, headers, queryParams); res.status(200).send(data); } catch (error) { res.status(500).send(responseList(500, error.message)); } }; // List records of a base exports.listRecords = async (req, res) => { const queryParams = req.query; const { baseId, tableIdOrName } = req.params; const url = `${BASE_URL}/${baseId}/${tableIdOrName}`; try { const data = await fetchData(url, 'GET', null, headers, queryParams); res.status(200).send(data); } catch (error) { res.status(500).send(responseList(500, error.message)); } }; // List records of a base exports.getRecord = async (req, res) => { const queryParams = req.query; const { baseId, tableIdOrName, recordId } = req.params; const url = `${BASE_URL}/${baseId}/${tableIdOrName}/${recordId}`; try { const data = await fetchData(url, 'GET', null, headers, queryParams); res.status(200).send(data); } catch (error) { res.status(500).send(responseList(500, error.message)); } }; // Update mutiple records exports.updateMutipleRecords = async (req, res) => { const { baseId, tableIdOrName } = req.params; const bodyData = req.body; try { const url = `${BASE_URL}/${baseId}/${tableIdOrName}`; const data = await fetchData(url, 'PATCH', bodyData, headers); res.status(200).send(data); } catch (error) { res.status(500).send(responseList(500, error.message)); } }; // Update a single record exports.updateRecord = async (req, res) => { const { baseId, tableIdOrName, recordId } = req.params; const bodyData = req.body; try { const url = `${BASE_URL}/${baseId}/${tableIdOrName}/${recordId}`; const data = await fetchData(url, 'PATCH', bodyData, headers); res.status(200).send(data); } catch (error) { res.status(500).send(responseList(500, error.message)); } }; // Create records exports.createRecords = async (req, res) => { const { baseId, tableIdOrName } = req.params; const bodyData = req.body; try { const url = `${BASE_URL}/${baseId}/${tableIdOrName}`; const data = await fetchData(url, 'POST', bodyData, headers); res.status(200).send(data); } catch (error) { res.status(500).send(responseList(500, error.message)); } }; // Create records exports.deleteMultipleRecords = async (req, res) => { const { baseId, tableIdOrName } = req.params; const bodyData = req.body; const recordString = bodyData.map((id) => `records[]=${encodeURIComponent(id)}`).join('&'); try { const url = `${BASE_URL}/${baseId}/${tableIdOrName}?${recordString}`; const data = await fetchData(url, 'DELETE', bodyData, headers); res.status(200).send(data); } catch (error) { res.status(500).send(responseList(500, error.message)); } }; // Create records exports.deleteRecord = async (req, res) => { const { baseId, tableIdOrName, recordId } = req.params; try { const url = `${BASE_URL}/${baseId}/${tableIdOrName}/${recordId}`; const data = await fetchData(url, 'DELETE', null, headers); res.status(200).send(data); } catch (error) { res.status(500).send(responseList(500, error.message)); } };
Now your Airtable API proxy is ready to use.
You can add caching layer on GET api routes, if you wants.