DEV Community

Mathieu Huot
Mathieu Huot

Posted on • Edited on

Requesting YouTube API endpoint using Node and 11ty

The other day, I was asked to import YouTube videos from a channel on a website I maintain for a small local organization. My idea was to import the IDs for every videos on that channel so I could built an iframe element with the right src attribute. I didn't want to import data from YouTube with every site build and since I'm managing the YouTube channel for the client, I was happy with running a script with every updates (which are not frequent).

Note: in order to use the YouTube API you'll need to setup with Google first. Here's an introduction on how to do that.

I usually find Google API confusing. The documentation is straight forward and thorough enough. It's just that there's so many parameters to go through. In the end, all I need is to get the data and then use it in a template for prerendering. My static site generator (SSG) of choice right now is Eleventy (11ty) which runs on Node. For this project, the JamStack architecture really fits well!

11ty is so flexible and simple. I really love working with this tool!

The code

I could have used a library like Axios, Request (now deprecated) or node-fetch, but my requirement was so simple that I decided to go without dependencies! Node's standard library has a https module which gives me access to a get method. Making a get request with the https module is quite simple. Here's the code using the stream.pipeline() method:

youtube-refresh.js

const https = require('https');
const fs = require('fs');
const { pipeline } = require('stream');

//Creating Writable stream to write on _data/youtube.json.
//If the file doesn't exist, it will be created.
const OUTPUT = fs.createWriteStream('./_data/youtube.json');

https.get(`https://www.googleapis.com/youtube/v3/search?...`, res => {
    //Piping res (IncomingMessage: Readable) to OUTPUT (Writable).
    //The pipeline method wilL manage stream flow and errors!
    pipeline(res, OUTPUT, error => {
        if(error) return console.error(error.stack);
        console.log('Transfering YouTube data is done!');
    });  
}).on('error', error => console.error(error.stack));
Enter fullscreen mode Exit fullscreen mode

To come up with this, I had to read the documentation and experiment a bit with the Node's Stream module (v14.5.0 docs). Since the res returned by the get method extends the stream.Readable, it felt really natural to use a stream method (in this case the pipeline method) to manage the response.

Note: unlike the pipe method, pipeline will destroy every streams automatically on error!

Finally, when I need to, I would run this script in the project's root directory like this before I build for production with the new data:

command line interface

node youtube-refresh.js
Enter fullscreen mode Exit fullscreen mode

By running this command, I get a new (or refreshed) youtude.json file in the _data directory which in 11ty exposes data globally through the youtube object! Then, I can use the data to build pages. Here's an example of what I did with the 11ty built-in pagination constructor:

videos.njk

---
layout: base-layout.njk
pagination:
  data: youtube.items
  size: 1
  alias: video
  addAllPagesToCollections: true
tags: videos
eleventyComputed:
  title: Formations {{ video.snippet.title }}
  description: "{{ video.snippet.description }}"
  publishedat: "{{ video.snippet.publishedAt }}"
permalink: formations/{{ video.snippet.title | lower | slug }}/
---
<main class="post-layout default-blog-theme">
    <article>
        <header class="article-header">
            <h1>{{ video.snippet.title }}</h1>
            <p><small>{{ video.snippet.publishedAt | timeFormat }}</small></p>
        </header>
        <div class="youtube">
            <iframe src="https://www.youtube.com/embed/{{ video.id.videoId }}?rel=0" allow="accelerometer; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
        </div>
        <p>{{ video.snippet.description }}</p>
    </article>
</main>
Enter fullscreen mode Exit fullscreen mode

There's a lot going on here. First, I paginate the data in youtube.items, which is the data contained in _data/youtube.json, in order to create one page per item. Then, I use addAllPagesToCollections (new in v0.8.0) to create a collection available through collections.videos which I will use on a listing page (see below)! I also use eleventyComputed (new in v0.11.0) and permalink in order to generate specific title, description, date and url for each page! The timeFormat filter makes date human readable and french canadian locale (only works with Node v13 and up). 😉

Here's the listing page:

formations.njk

---
layout: formations-layout
title: Formations
description: Les formations offertent par l'ADTILCSL!
date: 2019-07-16
tags: pages
pagination:
  data: collections.videos
  size: 9
  alias: videos
  reverse: true
permalink: formations/{% if pagination.pageNumber > 0 %}{{ pagination.pageNumber }}/{% endif %}index.html
---
<section class="listing-section">
  {% for video in videos %}
    {% card
    defaultClass = 'listing-card',
    title = video.data.title,
    detail = video.data.publishedat | timeFormat,
    text = video.data.description,
    link = video.url | url,
    linkText = 'participe à la formation!'
    %}
  {% endfor %}      
</section>
Enter fullscreen mode Exit fullscreen mode

My stack

Software Version
OS Linux Mint 18.2 Sonya
Node v14.x
11ty 0.11.0

The end

Thanks for reading! 😀

Top comments (0)