Requesting YouTube API endpoint using Node and 11ty
Tags: node, eleventy, javascript, html
Published: 2020-07-15 16:16:31

---
title: Requesting YouTube API endpoint using Node and 11ty
published: true
description: How to request YouTube API endpoint using Nodejs standard API and Eleventy (11ty) in the frontend!
tags: node, eleventy, javascript, html
---
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 `ID`s  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](https://developers.google.com/youtube/v3/getting-started/) 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](https://www.11ty.dev/) (11ty) which runs on [Node](https://nodejs.org/en/). For this project, the [JamStack](https://jamstack.org/) 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](https://github.com/axios/axios/blob/master/README.md), [Request](https://github.com/request/request/blob/master/README.md) (now [deprecated](https://github.com/request/request/issues/3142)) or [node-fetch](https://github.com/node-fetch/node-fetch/blob/master/README.md), 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*
```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));
``` 
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](https://nodejs.org/dist/latest-v14.x/docs/api/stream.html)). 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*
```shell
node youtube-refresh.js
```
By running this command, I get a new (or refreshed) `youtude.json` file in the `_data` directory which in `11ty` [exposes data globally](https://www.11ty.dev/docs/data-global/) 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*
```twig
---
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>
```
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](https://github.com/solution-loisir/ADTILCSL/blob/master/filters/readable-time.js) filter makes date human readable and french canadian locale (only works with Node v13 and up). 😉 

Here's the listing page:

*formations.njk*
```twig
---
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>
```
## My stack
|Software|Version|
|--------|-------|
|OS      |Linux Mint 18.2 Sonya|
|Node    |v14.x|
|11ty    |0.11.0  |
## The end
Thanks for reading! 😀