Skip to content
Swizec Teller - a geek with a hatswizec.com

Using YouTube as a data source in Gatsbyjs

This is a Livecoding Recap โ€“ an almost-weekly post about interesting things discovered while livecoding. Usually shorter than 500 words. Often with pictures. Livecoding happens almost every Sunday at 2pm PDT on multiple channels. You should subscribe to My Youtube channel to catch me live.

Gatsby is hands down the best static site generator out there. Builds sites so fast it looks like a bug.

The magic trick Gatsby uses to make sites so fast is binding data build time. Most modern server-side-rendered (ssr) sites build an HTML page of your initial view, load data into the browser, and build the rest of the page live. Or they render a skeleton and fill it with dynamic data.

Gatsby does all that at build time. Run gatsby build, and it loads your dynamic data and builds static HTML pages with data-dependent parts already baked in.

How you use it

At a high level, Gatsby uses various source plugins to read data and make it available via a GraphQL interface. You write GraphQL queries to load this data and render React components.

Gatsby handles the rest. I'm not really sure how, but smart people assure me it works, and I've never seen it not work. ๐Ÿคทโ€โ™€๏ธ

Let's say you want to build pages with markdown files. I do that for my workshop materials. I use a bunch of markdown files published as a website, and it works great.

You have to enable the gatsby-source-filesystem plugin. It lets you read local files as a data source. Then you enable the gatsby-transformer-remark plugin, which lets you parse markdown files.

When that's set up, you can get a list of page titles with a query like this:

// anywhere in your JS
export const query = graphql`
allMarkdownRemark {
edges {
node {
frontmatter {
title
}
fields {
slug
}
}
}
}
`;

You're looking for all nodes of the markdownRemark type, collecting their edges and nodes, and plucking the title and slug value of each.

This shows up in your page component as a data prop. So to list all those page titles, you'd do something like this ๐Ÿ‘‡

const allPages = _.sortBy(
data.allMarkdownRemark.edges,
({ node }) => node.fields.slug
);
<ul>
{allPages.map(({ node: { frontmatter: { title } } }) => (
<li>{title}</li>
))}
</ul>;

There's a lot of looping and destructuring because our data comes in the shape of a graph, and I'm bad at writing GraphQL queries. I'm sure this looks terrible to someone who knows what they're doing.

Adding a custom data source โ€“ YouTube

The fun part is building your own data sources, which you can do, and it's easier than I thought.

Your basic approach goes like this:

  1. Search far and wide for an existing plugin
  2. Give up and realize you'll have to build it yourself
  3. Strongly consider building a releasable plugin
  4. Decide you're better off building a one-off integration
  5. Spend the next hour fiddling inside gatsby-node.js

Your goal is to read your data source and create Gatsby data nodes for each object you want to make available.

The general template goes like this:

// gatsby-node.js
exports.sourceNodes = async ({ boundActionCreators }) => {
const { createNode } = boundActionCreators;
// get data
// call createNode for each entry
};

You can see my full code on GitHub.

My goal was a list of videos from a YouTube playlist. I used youtube-playlist-info to fetch a list of videos from a public playlist. Google's official node.js library is too hard to use.

// gatsby-node.js
// require library
const ypi = require("youtube-playlist-info");
// read my API key
const YT_KEY = require("./client_secrets.json")["yt_key"];
// hardcode ID of my playlist for now
const LWyP = "PLF8WgaD4xmjWuh7FTYTealxehOuNor_2S";
exports.sourceNodes = async ({ boundActionCreators }) => {
const { createNode } = boundActionCreators;
const items = await ypi(YT_KEY, LWyP);
// build Gatsby nodes
};

When Gatsby builds my site, it goes into gatsby-nodes and calls my node builder function. This uses ypi to fetch a list of videos from the hardcoded Learn While You Poop playlist.

Yes, that means building the site is now slow. It has to talk to YouTube's API every time, but itโ€™s worth it.

Your next step is to build Gatsby nodes for each video in the playlist.

Nodes require a contentDigest so Gatsby can tell whether they've changed. To make that easier, I built a helper function ๐Ÿ‘‡

// gatsby-node.js
const makeNode = (node) => {
node.internal.contentDigest = crypto
.createHash("md5")
.update(JSON.stringify(node))
.digest("hex");
createNode(node);
};

makeNode is a thin wrapper on Gatsby's built-in createNode function that automatically generates an md5 hash of the whole node and saves it in contentDigest. Not sure why this isn't default behavior.

With that set up, you can build your nodes in a loop like this:

// gatsby-node
let lwypNode = {
id: "lwypPlaylist",
parent: "ytPlaylists",
children: [],
internal: {
type: "ytPlaylist",
},
};
lwypNode.children = items.map(
({ title, description, resourceId, thumbnails, position }) => {
const id = `ytVideo-${resourceId.videoId}`;
makeNode({
id,
title,
description,
thumbnails,
position,
resourceId,
internal: {
type: "ytVideo",
},
parent: "lwypPlaylist",
children: [],
});
return id;
}
);
makeNode(lwypNode);

Set up an empty node for the lwypPlaylist of type ytPlaylist. Fill its children with nodes that contain important data from the YouTube API.

When all those nodes are created, then create the main lwypNode. This reverse order is important. You have to build your data graph from the bottom up otherwise you'll get strange errors.

You can see me struggling with that in the video above. Lots of head scratching.

Read your YouTube data source

When your nodes are created, you can read them anywhere inside Gatsby with a GraphQL query like this ๐Ÿ‘‡

export const query = graphql`
query LwypPlaylist {
ytPlaylist(id: { eq: "lwypPlaylist" }) {
childrenYtVideo {
id
title
description
}
}
}
`;

Fetch a node of type ytPlaylist with id equal to lwypPlaylist. Inside that node, get all children ytVideo nodes and their id, title, and description.

You can then render them in a loop.

Component = ({ data }) => {
const videos = data.ytPlaylist.childrenYtVideo;
return (
<div>
{videos.map((video) => (
<div>
<h3>{video.title}</h3>
{video.description}
</div>
))}
</div>
);
};

Full page coming soon.

Should I make a real YouTube source plugin for Gatsby? Ping me on Twitter.

Did you enjoy this article?

Published on April 24th, 2018 in Front End, Livecoding, Technical

Learned something new?
Want to become a high value JavaScript expert?

Here's how it works ๐Ÿ‘‡

Leave your email and I'll send you an Interactive Modern JavaScript Cheatsheet ๐Ÿ“–right away. After that you'll get thoughtfully written emails every week about React, JavaScript, and your career. Lessons learned over my 20 years in the industry working with companies ranging from tiny startups to Fortune5 behemoths.

Start with an interactive cheatsheet ๐Ÿ“–

Then get thoughtful letters ๐Ÿ’Œ on mindsets, tactics, and technical skills for your career.

"Man, love your simple writing! Yours is the only email I open from marketers and only blog that I give a fuck to read & scroll till the end. And wow always take away lessons with me. Inspiring! And very relatable. ๐Ÿ‘Œ"

~ Ashish Kumar

Join over 10,000 engineers just like you already improving their careers with my letters, workshops, courses, and talks. โœŒ๏ธ

Have a burning question that you think I can answer?ย I don't have all of the answers, but I have some! Hit me up on twitter or book a 30min ama for in-depth help.

Ready to Stop copy pasting D3 examples and create data visualizations of your own? ย Learn how to build scalable dataviz components your whole team can understand with React for Data Visualization

Curious about Serverless and the modern backend? Check out Serverless Handbook, modern backend for the frontend engineer.

Ready to learn how it all fits together and build a modern webapp from scratch? Learn how to launch a webapp and make your first ๐Ÿ’ฐ on the side with ServerlessReact.Dev

Want to brush up on your modern JavaScript syntax?ย Check out my interactive cheatsheet: es6cheatsheet.com

By the way, just in case no one has told you it yet today: I love and appreciate you for who you areย โค๏ธ

Created bySwizecwith โค๏ธswizec.com