Skip to content

Commit

Permalink
[feature] multi-node types (#121)
Browse files Browse the repository at this point in the history
* Option to make tables separate node types (#52)

* Option to make tables separate node types

* Revert "Option to make tables separate node types"

This reverts commit b59ffda.

* Removed package/lock from branch

* Added AirtableField node types.

* Added more info for createSeparateNodeType

* Version bump

* Check that separateNodeType option exists

* add option to set mapping fields each as a separate type (#115)

* separateMapTypes config option

* update version

* Feat/multi node types/parent node field (#144)

* prettier all the things

* add separateNodeType option

* spelling mistake

* add separateNodeType

* add example that errors without new features

* bump version number

Co-authored-by: Kevin Miller <[email protected]>
  • Loading branch information
jbolda and kevee authored Feb 15, 2020
1 parent 79af9e2 commit 7c186a6
Show file tree
Hide file tree
Showing 16 changed files with 31,438 additions and 8 deletions.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@ plugins: [
baseId: `YOUR_AIRTABLE_BASE_ID`,
tableName: `YOUR_TABLE_NAME`,
tableView: `YOUR_TABLE_VIEW_NAME`, // optional
queryName: `OPTIONAL_NAME_TO_IDENTIFY_TABLE`, // optional
queryName: `OPTIONAL_NAME_TO_IDENTIFY_TABLE`, // optionally default is false - makes all records in this table a separate node type, based on your tableView, or if not present, tableName, e.g. a table called "Fruit" would become "allAirtableFruit". Useful when pulling many airtables with similar structures or fields that have different types. See https://github.com/jbolda/gatsby-source-airtable/pull/52.
mapping: { `CASE_SENSITIVE_COLUMN_NAME`: `VALUE_FORMAT` }, // optional, e.g. "text/markdown", "fileNode"
tableLinks: [`CASE`, `SENSITIVE`, `COLUMN`, `NAMES`] // optional, for deep linking to records across tables.
createSeparateNodeType: false, // boolean, default is false, see the documentation on naming conflicts for more information
separateMapType: false, // boolean, default is false, see the documentation on using markdown and attachments for more information
},
{
baseId: `YOUR_AIRTABLE_BASE_ID`,
Expand Down Expand Up @@ -108,6 +110,8 @@ For an example of a markdown-and-airtable-driven site using `gatsby-transformer-

If you are using the `Attachment` type field in Airtable, you may specify a column name with `fileNode` and the plugin will bring in these files. Using this method, it will create "nodes" for each of the files and expose this to all of the transformer plugins. A good use case for this would be attaching images in Airtable, and being able to make these available for use with the `sharp` plugins and `gatsby-image`. Specifying a `fileNode` does require a peer dependency of `gatsby-source-filesystem` otherwise it will fall back as a non-mapped field. The locally available files and any ecosystem connections will be available on the node as `localFiles`.

If you are specifying more than one type of `mapping`, you may potentially run into issues with data types clashing and throwing errors. An additional option that you may specify is `separateMapType` which will create a gatsby node type for each type of data. This should prevent issues with your data types clashing.

When using the Attachment type field, this plugin governs requests to download the associated files from Airtable to 5 concurrent requests to prevent excessive requests on Airtable's servers - which can result in refused / hanging connections. You can adjust this limit with the concurrency option in your gatsby-config.js file. Set the option with an integer value for your desired limit on attempted concurrent requests. A value of 0 will allow requests to be made without any limit.

### The power of views
Expand All @@ -120,6 +124,8 @@ For example, if you are creating a blog or documentation site, specify a `publis

You may have a situation where you are including two separate bases, each with a table that has the exact same name. With the data structure of this repo, both bases would fall into allAirtable and you wouldn't be able to tell them apart when building graphQL queries. This is what the optional `queryName` setting is for-- simply to provide an alternate name for a table.

If you would like to have the query names for tables be different from the default `allAirtable` or `airtable`, you may specify `createSeparateNodeType` as `true`.

### Column Names

Within graphql (the language you query information from and that this plugin puts nodes into), there are character limitations. Most specifically we cannot have spaces in field names. We don't want to force you to change your Airtable names, so we will "clean" the keys and replace the spaces with an underscore (e.g. The Column Name becomes The_Column_Name). We use the cleaned name everywhere including `gatsby-config.js` and within your queries. We don't warn you when this happens to cut down on the verbosity of the output.
Expand Down
59 changes: 59 additions & 0 deletions examples/recipes-with-multi-type/gatsby-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
module.exports = {
siteMetadata: {
title: "Gatsby Recipes"
},
plugins: [
{
resolve: `gatsby-source-filesystem`,
options: {
name: `pages`,
path: `${__dirname}/src/pages/`
}
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `assets`,
path: `${__dirname}/src/assets/`
}
},
{
resolve: `gatsby-source-airtable`,
options: {
apiKey: process.env.AIRTABLE_API_KEY_DEV, //(set via environment variable for this example)
tables: [
{
baseId: `appM8D8wmSJX9WJDE`,
tableName: `Recipes`,
queryName: `Recipes`,
tableView: `List`,
mapping: {
Attachments: `fileNode`,
Ingredients: "text/markdown",
Directions: "text/markdown"
},
tableLinks: [`Cooking_Method`, `Style`],
separateNodeType: true,
separateMapType: true
},
{
baseId: `appM8D8wmSJX9WJDE`,
tableName: `Cooking Method`,
tableView: `Main View`,
tableLinks: [`Recipes`]
},
{
baseId: `appM8D8wmSJX9WJDE`,
tableName: `Style`,
tableView: `Main View`,
tableLinks: [`Recipes`]
}
]
}
},
`gatsby-plugin-sharp`,
`gatsby-transformer-sharp`,
`gatsby-plugin-mdx`,
`gatsby-plugin-react-helmet`
]
};
67 changes: 67 additions & 0 deletions examples/recipes-with-multi-type/gatsby-node.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
const path = require(`path`);

exports.onCreateNode = ({ node, actions, getNode }) => {
const { createNodeField } = actions;
let slug;

if (node.internal.type === `AirtableRecipes` && node.table === `Recipes`) {
slug = `/${node.data.Name.replace(/ /g, "-")
.replace(/[,&]/g, "")
.toLowerCase()}/`;

// Add slug as a field on the node.
createNodeField({ node, name: `slug`, value: slug });
}
};

exports.createPages = ({ graphql, actions }) => {
const { createPage, createRedirect } = actions;

return new Promise((resolve, reject) => {
const pages = [];
const atRecipes = path.resolve(`src/templates/recipeTemplate.js`);

// Query for all markdown "nodes" and for the slug we previously created.
resolve(
graphql(
`
{
allAirtableRecipes(filter: { table: { eq: "Recipes" } }) {
edges {
node {
id
data {
Name
}
fields {
slug
}
}
}
}
}
`
).then(result => {
if (result.errors) {
result.errors.forEach(error => {
console.log(error);
});

reject(result.errors);
}

result.data.allAirtableRecipes.edges.forEach(edge => {
createPage({
path: edge.node.fields.slug, // required, we don't have frontmatter for this page hence separate if()
component: atRecipes,
context: {
name: edge.node.data.Name
}
});
});

return;
})
);
});
};
Loading

0 comments on commit 7c186a6

Please sign in to comment.