Airtable & Gatsby Apollo GraphQL with Typescript

Part one: Reading in Airtable data with Gatsby’s implementation of Apollo GraphQL server.

Image for post
Image for post

I’ve spent way too hours trying to find the right toolchain and starters just to create a playground to try out some features. So I’m hoping that this article will help others get to coding and combining components more efficiently.

Where to start?

You can create a react app from many different starter files. A good question I saw on the React site is to ask up front is “do I need a toolchain?” But in reality, most experiences are easier to create, test, publish, update, integrate when you find the right tools. Folks are working on these amazing tools to optimize designing and coding, so it’s worth learning about them.

I chose Gatsby because it provided nice UI for a static site, and even more with V3, looks like it wants to be a total solution. But if I wanted a robust app, I found that the starters with tools all along the stack contained packages I didn’t want. (Thank goodness, I found Ben Awad’s simple video, Gatsby Dynamic App Tutorial, and starter package that demonstrates how to add a dynamic app to in a Gatsby-style static project.)

Just because it’s promoted on Gatsby’s home page, I’m going to try out a workflow starting with a Gatsby Cloud instance and add the tools I know I want.

On the cloud

There’s no reason to set your app up on the cloud to begin with. But isn’t it going to be up there at some point anyway? So I decided to check out the Gatsby Cloud. I didn’t even have a project ready. I just signed up for the Cloud with my GitHub account, and let the site drag me along as it created a container project, a repo, and default settings for previewing, building, and deploying.

But, voila! There’s my GitHub repo all ready to clone locally:

>> git clone https://github.com/theship/test-org-logos.git
>> cd test-org-logos
>> npm install
>> gatsby develop
>> code .

I’ll stop the server with Ctrl-C and add some more tools.

Other tools

I need to add to make sure I have the dev playground I need to test out different features.

Using GraphQL

GraphQL is a specification for how to query, filter, and sort data. Turns out Gatsby includes some basic GraphQL features built in, including methods for querying and sourcing data, and customizing GraphQL. Gatsby allows you to pull in data from anywhere, for example from a REST API, and use GraphQL to query that data.

When I make a GraphQL query, a JSON object is returned, but Gatsby handles all of the JSON parsing in the app code.

Gatsby’s implementation of GraphQL is really about server-side compiling of data pulled into the the app. This means you may have content in a database, like a CMS, or a bunch of *.md files. Gatsby can pull all of that in to combine it on the server before sending it to the client. Airtable updates don’t show up until you re-build. You can still implement hooks, but I’ll be looking at GraphQL Subscriptions and GraphQL Live in Part two.

Ultimately, I’m going to want to make an app that reads and updates data back to Airtable. And so besides the Gatsby GraphQL server instance, I’m going to want to set up my own Apollo service. (That’ll be in Part two.)

Using TypeScript

TypeScript, a superset of JavaScript, is a smart choice when writing modern applications.

gatsby-plugin-typescript “allows Gatsby to build TypeScript and TSX files. It does NOT run type checking during build (see Caveats). This plugin is automatically included in Gatsby. The only reason you would need to explicitly use this plugin is if you need to configure its options.”

As it tells me on the newly created typescript page for my app, for type checking I want to install typescript via npm and run tsc -- to create a .tsconfig.js file.

>> npm install --save typescript
>> tsc --init

And just to make sure I transpile everything, I added the following to the tsconfig.js file after the compilerOptions inside the new file:

"include": ["./src/**/*"]

Then, I’ll add some basic types:

npm install --save-dev @types/react @types/react-dom @types/node

That should work. But I’m not too happy with relying on Visual Code to do all of my type checking. For more information on TypeScript support, see the issue https://github.com/gatsbyjs/gatsby/issues/18983.

I’m going to go ahead and add a full TypeScript compiler:

>> npm install -D typescript
>> npm install -D ts-node

I’m going to add the plugin to Gatsby’s gatsby-config.js file:

plugins: [
`gatsby-plugin-typescript`,

Using SASS

I’m going to add SASS for preprocess for styling in my Gatsby project.

>> npm install --save node-sass gatsby-plugin-sass

Adding SASS to the gatsby-config.js file under plugins, e.g.:

    resolve: 'gatsby-plugin-sass',
options: {
includePaths: `${__dirname}/src/styles`,
},
},Calling on Airtable

I have an Airtable Base (with some Base ID that looks like appABunchOfNumbersAndLtrs) that has a table Orgs, with Org Name and Org Logo fields. The Org Logo field is of type attachment in Airtable. But that doesn’t seem to matter, because as I found out, a query of the field returns several children of Org_Logo that include a URL. I’ll select that subfield when building my project for this Part one. (It’s recommended that you populate the table with at least one org to begin with.)

If I go to api.airtable.com, I’m redirected to https://airtable.com/api, where I’ll select the Airtable (data)Base to that I want to use for this project. As I scroll down, I see the cURL value of my REST API:

https://api.airtable.com/v0/app56znB40HogXVeJ/General%20projects?api_key=AIRTABLE_KEY

Pulling Airtable into my Gatsby project

I’ll install Airtable into my codebase:

>> npm install airtable --save

And the Gatsby airtable package:

>> npm install --save gatsby-source-airtable

Next, I’ll follow the steps to integrating the Airtable package from the Gatsby site.

I’ll create the environment var files in which to place my keys (without prepending GATSBY_ to the values as that will expose them client-side).

Then, I’ll populate a .env file at the root of the project with my API endpoint URL, the secret API key and Base ID:

API_URL=https://api.airtable.com
AIRTABLE_KEY=♨𝓈𝔼℃R𝔼𝐓𝕒𝕀ŘTάbLẸⓚ𝕖𝔂
AIRTABLE_BASE_ID=𝒶Įᖇ𝔱𝒶BŁє𝐁Ã𝓢𝓔𝔦ᵈ

In the gatsby-config.js file, I’m going to:

  • require dotenv at the top of the file:
require("dotenv").config({
path: `.env.${process.env.NODE_ENV}`,
})
  • add the API URL to the siteMetaData:
apiUrl: process.env.API_URL,
  • and add the plugin:
{
resolve: 'gatsby-source-airtable',
options: {
apiKey: process.env.AIRTABLE_KEY,
tables: [
{
baseId: process.env.AIRTABLE_BASE_ID,
tableName: 'Orgs',
tableView: `Grid view`,
defaultValues: {
// currently does not accept null/undefined.
// use empty string instead and perform
// conditional logic on
// name_of_field.length > 0 ? condition_1 : condition_2
FIELD_THAT_OTHERWISE_IS_NOT_RETURNED_IF_ALL_VALS_BLANK:
"",
// ... etc
},
mapping: { Image: "fileNode" },
},
],
},
},

Now, I’ll run the server again:

>> gatsby develop

and go to http://localhost:8000/___graphql, where I see an allAirtable node. I’ll build my query by selecting edges>node>data>Org_Name & Org_Logo > url & type. When I run that query, it looks like this:

query MyQuery {
allAirtable {
edges {
node {
data {
Org_Name
Org_Logo {
url
type
}
}
}
}
}
}

I see my orgs, which mostly don’t have any logos.

Notice that in the Airtable UI, the field has a space in the field name. This has been transliterated into underscores. So Org Name becomes Org_Name.

MyQuery is the name of my query. edges is a collection of connected items with relationships. The node is the single Airtable object that represents my Airtable base. And that node has properties of recordId and data of Org_Name and Org_Logo.

Querying my Airtable with GraphQL

The Airtable API is a REST API. What’s cool about Gatsby is that it pretty much handles the typing, type inference, and JSON parsing. And what’s cool about GraphQL is that it doesn’t matter how your database is designed, GraphQL is a query language designed to query just about any database. The Apollo server handles how that query gets interpreted by the database. It only has one endpoint: a POST request with a body that contains contains a query to read, update, etc.

Note that there’s another GraphiQL interface that you can trigger as well: Playground.

Playground gives you a different interface to http://localhost:8000/___graphql. You can run the default by not providing the GATSBY_GRAPHQL_IDE value. Stop the server with Crtl-C . Then restart it with the GATSBY_GRAPHQL_IDE config value. And if you don’t refresh your browser, you can toggle between the different views, depending on how you like. I like Playground for the docs, and the default for the code completion. But pretty much both give you the same information.

>>GATSBY_GRAPHQL_IDE=playground gatsby develop

So with the simple query above, I’m going create a constant object and then feed it into a Gatsby data object that returns the HTML, just as React does. I’m doing it in a Typescript file, logos.tsx, but I’m not really setting things up completely. I’m just trying to get some data and view it.

I’m actually going to ditch the edges node when I copy my query over into my code:

export const query = graphql`
query {
allAirtable {
nodes {
recordId
data {
Org_Name
Org_Logo {
filename
url
thumbnails {
small {
width
}
}
}
}
}
}
}
`;

And I don’t really need the logo file name or thumbnails right now, but it’s interesting to see how the default Airtable fields filled out for an attachment filed in the UI.

// eslint-disable-next-line react/prop-types
export default ({data}) => {
const allAirtableData = data.allAirtable.nodes;
<div>
return (
{
<ul>
{
// eslint-disable-next-line react/prop-types
allAirtableData.map((node: {
recordId:number;
data:
{ Org_Name: string;
Org_Logo: [{ url: string; }];
};
}, i:number ) => (
<li key={i}>
{node.data.Org_Name} -- {node.recordId}
<br></br>
{node.data.Org_Logo?.map((Org_Logo: {url:string}) => (
<span>{Org_Logo.url}</span>
)) }
</li>
))
}
</ul>
}
</div>
);
};

For the nested array, Org_Logo, I had to map it and couldn’t just use dot notation.

I then use the bang question mark that is the TypeScript optional query:

node.data.Org_Logo?.map…

This is a way of saying that when there’s a logo, map it; but when it’s is null or undefined, stop what we’re doing and just return undefined. This helps with null fields.

So that’s pretty much all the code needed to pull in and display some Airtable fields. And my source is on GitHub.

Back to the Cloud

I’m going to stop here for a while and look back at that Gatsby Cloud instance on my Gatsby dashboard. I’ve been checking in code. Turns out that my builds have been attempted on the cloud by default — and they’re failing. ;-P

Zero errors. Zero warnings. So I look at the raw logs and it looks like yarn is failing. I go back to my source code and apparently my package.json name field can’t have any spaces. Fixed that and checked in changes to GitHub.

I go back to the dashboard and a build has been kicked off. Aaannd failed again. but this time for a good reason: “API key is required to connect to Airtable.” Should be easy enough to fix: in Site Settings, I add some environment variables. Back at the Deploys tab, I trigger another build. And my app has been deployed. My project is live at https://my-project-name-somenumbers.gtsb.io/.

Ohhey. Cool lil’ thing: the deploy scripts noticed I didn’t use a package (yet) and gave me a warning:

The gatsby-source-airtable plugin has generated no Gatsby nodes. Do you need it?

At this point, I could have used something like surge.sh to try out my code, but it’s nice to have a build/deploy site set up from the beginning.

Dynamic Airtable round trip with Apollo in Gatsby

I’ve been looking for a while on a full way to do CRUD in Gatsby using Apollo. And that’ll be Part two. But in the meantime, if you want to geek out and explore this topic yourself, check out the following resources:

Jason Lengstorf and Mikhail Novikov walking through Advanced GraphQL Techniques in Gatsby on YouTube.

Thanks to Jonnie Bigodes for how to integrate a GraphQL server using Airtable as the database on the Gatsby issues: createPages based on query response #12945 and Can I configure apollo server to store information in backend using gatsby? #20391.

Andrew Hyndman has a cool example of using something like Airtable for the Pokeman API. I don’t think they bought the idea, but his implementation is useful to review.

And my new YouTube star, Ben Awad, for his starter & video on how to use Gatsby & Typescript https://github.com/benawad/gatsby-typescript-app-starter/tree/master/src and CRUD example: https://github.com/benawad/typescript-graphql-crud-example and generally just being there for the community.

Let me know if this has been helpful. And if you have any suggestions or ideas about this topic, please share!

...a queer man, Captain Ahab- so some think- but a good one. Oh, thou'lt like him well enough; no fear, no fear. He's a grand, ungodly, god-like man...

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store