Build a Course-Tracking App with React, Netlify Functions, and Airtable
The Jamstack—JavaScript, APIs, and Markup—isn't built on new technology but on a fresh way of combining existing tools. Its appeal lies in simple deployment and hosting. By pairing React with Netlify Functions for server-side logic and Airtable for data storage, you can build a full CRUD application that deploys as a static site. Here's how to build a course tracker to manage your online learning backlog.
Why Airtable?
Airtable serves as the database layer, and it's a practical choice for several reasons:
- No separate database deployment or hosting is required.
- Data is viewable and editable in an Excel-like GUI.
- A JavaScript SDK is available.
Airtable Setup
Airtable refers to databases as "bases." To begin:
- Sign up for a free account.
- Create a new base using the "Start From Scratch" option.
- Define a table for course data.
Name the base something reusable, like "JAMstack Demos." Inside the base, you'll see a spreadsheet-like interface. Add the following columns with specific types:
name(single line text)link(single line text)tags(multiple select)purchased(checkbox)
Populate a few tags like "node," "react," "jamstack," and "javascript." You can also add sample courses for testing. Finally, rename the default table from "Table 1" to courses.
Locating Airtable Credentials
You'll need three pieces of information from Airtable:
- API Key: Found on your account page.
- Base ID: Found on the Airtable API page by clicking your base.
- Table name: The name you chose, e.g.,
courses.
Project Setup
The starter project contains a standard create-react-app application plus a functions directory for serverless functions and a netlify.toml config file. This config tells Netlify where the functions live and includes a redirect so you can call them with an /api/* path. To get started:
- Fork and clone the repository.
- Check out the starter branch with
git checkout starter. - Install the
dotenvpackage.
Create a .env file in the repo root with your API key and base ID, then install the Netlify CLI.
Building Serverless Functions
Serverless functions in Netlify are JavaScript files in the /functions directory. The core exports a handler function that receives an event parameter and returns a response. The starter code includes a formattedReturn helper for consistent status and body responses, plus specific helper functions for interacting with Airtable. The main courses.js file routes requests based on HTTP method:
- HTTP GET →
getCourses - HTTP POST →
createCourse - HTTP PUT →
updateCourse - HTTP DELETE →
deleteCourse
If the method isn't recognized, return a 405 status code.
Airtable Configuration
Set up Airtable once in a shared config file. Use the API key and base ID to create a base reference, then grab the courses table reference and export it. Each helper function imports this config.
Getting Courses
The getCourses function calls table.select().firstPage() to retrieve records (20 by default). Wrap the call in a try/catch for error handling. Airtable returns records with extra metadata, so map them into simplified objects with id and fields.
To test locally, run netlify dev in the project directory. This command:
- Runs your serverless functions.
- Serves your site.
- Creates a proxy on port 8888 so the frontend and functions can communicate.
Because of the netlify.toml redirect, you can access your functions via paths like /api/courses.
Creating Records
The createCourse handler parses the incoming event.body, which is a string, into a JavaScript object. Then it calls table.create(), passing an array of objects with the four field names. After creating, return the createdCourse.
To test POST (and PUT/DELETE) requests, you can't just paste a URL into the browser; use a tool like Postman or Insomnia. For a POST to localhost:8888/api/courses, send a JSON body with name, link, and tags. The function returns the new record, and you can verify it in the Airtable GUI. Save the returned record ID for future operations.
Updating Records
For PUT requests, grab the id from the request body via destructuring, then use the rest operator to collect the other fields. The update() function takes an array of objects with id and fields properties.
Test with a Postman PUT request to the same URL, passing the record ID and any fields to update (e.g., appending "Updated!!!" to the name). Verify the change appears in Airtable.
Deleting Records
For DELETE requests, get the id from the request body, then call the destroy() function. Send a DELETE request in Postman with the record ID as a JSON body to remove the course. Confirm deletion in the Airtable dashboard.
Frontend: React Integration
Displaying Courses
In App.js, complete the loadCourses function by making a fetch GET request to the relative endpoint /api/courses. Thanks to netlify dev, relative paths work locally and in production with no further changes. Store the returned array in the courses state variable. Wrap everything in try/catch.
Load the app at localhost:8888 and verify the course list renders.
Adding Courses
In CourseForm.js, the submitCourse function must make a POST request. The Fetch API defaults to GET, so pass an options object with:
method: POSTbody: the stringified form data
After submitting, the form resets, and the course list automatically updates with the new entry.
Marking as Purchased
The app splits courses into purchased and unpurchased sections. In Course.js, the markCoursePurchased function sends a PUT request. Pass all course properties using the spread operator, overriding purchased to true. Click the button to see the course move sections.
Deleting Courses
Also in Course.js, complete the deleteCourse function with a DELETE request that passes the course ID. The course is removed from the list and from Airtable.
This combination—static hosting with serverless functions and a third-party database—gives you a full-stack app that's easy to deploy and scale, starting as a local project with netlify dev and ending with a simple deploy to Netlify.
Go Live With Netlify
With CRUD working locally, the final step is shipping to Netlify. Before starting, make sure the code is pushed to GitHub. If you don't have a Netlify account yet, sign up for free (it's like Airtable in that respect). From the dashboard, choose “New site from Git,” connect GitHub, and pick the project repo.

You'll then select the deploy branch. Going with starter is fine for validating the deployment works, though master holds the final version. For the build setup, point Netlify at the right commands and output folder:
- Build command:
npm run build - Publish directory:
build
One caveat: Netlify now treats React warnings as build errors. To get around that, use CI = npm run build as the build command, which is already adjusted in the project.

Next, open the “Show Advanced” section and add the environment variables exactly as they appear in the local .env file.

Save and Netlify kicks off the build automatically. You can watch the progress under the “Deploys” tab—it moves quickly. Once complete, the app is live for anyone to use.

Living in the Jamstack
The Jamstack makes full-stack, production-ready apps simple to build and host. The combination of React, serverless functions, and Airtable shows how a front-end developer can handle data persistence and API logic without managing a backend server.
None of this requires these exact tools—Airtable, React, or Netlify—but together they're free, easy to set up, and pick up the slack where traditional static sites fall short. For other services, resources, and ideas in this space, check out Chris' serverless site, and drop questions or feedback in the comments.



