Sammy McKay
Sammy McKay

Reputation: 41

Creating a YouTube playlist with React using Google's API

I would like to create a YouTube playlist on a users account, but I have struggled to authenticate a POST to the YouTube v3 api.

I'll start by showing how far I have got with this problem.


YouTube API Documentation

The Youtube API Documentation provides details on creating a playlist, and has a working example in the API Explorer

I entered the following code into the request body:

{
  "snippet":
  {
    "title":"Test Playlist"
  }
}

This successfully created a playlist on my YouTube account with the same title. So from this I could tell that, a title was required within the body and it would require OAuth 2.0 authentication (an error is displayed if it is not enabled) using one the scopes: youtube, youtube.force-ssl, youtubepartner.

First attempt in react

The First thing I tried was similar to this:

fetch('/youtube/v3/playlists', {
    method: 'POST',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'Authorization': 'Bearer' + api.youtube,
    },
    body: JSON.stringify({
      "snippet":
      {
      "title":"Test"
      }
    })
  }).then(response => response.json()).then(data => {
    console.log(data)
  })

api.youtube contains my YouTube api key. Most of the formatting for this came from another API I have in the same program for getting data from spotify which works. The response I got from this would say "Login failed" or "Authentication Error" (something along those lines)

Anyway, this is relevant because I know that my first hurdle is getting authentication.

Authentication

The YouTube API Documentation contains a guide titled Implementing OAuth 2.0 Authorization I followed the guide for client side web apps. The first thing I noticed is that they are using a library, I found this on npm under googleapis and installed it. When I tried to call this in React using

const {google} = require('googleapis');

I won't get deep into the error but react said "Can't convert undefined to object" and found an issue which said that googleapis is intended for server side not client side, I tried building the react app and putting it on herokuapp but got the same error. Someone else suggested using gapi-client on npm which is a node wrapper for googleapis.

The next thing I did was try the example on the npm page, which is very similar to the google example for configuring the client object. I have it so the import part and function are at the top of my app.js and then the gapi.load part activates after a button is pressed (this could be useless info but w/e)

import gapi from 'gapi-client';

//On load, called to load the auth2 library and API client library.
gapi.load('client:auth2', initClient);

function initClient() {
  gapi.client.init({
    discoveryDocs: ["https://www.googleapis.com/discovery/v1/apis/drive/v3/rest"],
    clientId: 'YOUR_CLIENT_ID',
    scope: 'https://www.googleapis.com/auth/drive.metadata.readonly'
  }).then(function () {
    // do stuff with loaded APIs
    console.log('it worked');
  });
}

I copied my client ID in from the API Console and this is the exact response I got:

FireFox

Loading failed for the with source “https://apis.google.com//scs/apps-static//js/k=oz.gapi.en.WcpMzqgmJZU.O/m=auth2,client/rt=j/sv=1/d=1/ed=1/am=AQ/rs=AGLTcCNsTS1p4dx0iMhlrwEpiaXw4iMjOg/cb=gapi.loaded_0”.

Chrome

GET https://apis.google.com//scs/apps-static//js/k=oz.gapi.en.WcpMzqgmJZU.O/m=auth2,client/rt=j/sv=1/d=1/ed=1/am=AQ/rs=AGLTcCNsTS1p4dx0iMhlrwEpiaXw4iMjOg/cb=gapi.loaded_0 net::ERR_ABORTED 404


That's about as far as I got and I'm not sure what to do from here, so any help is much appreciated. I hope this didn't get too convoluted but I've tried to convey my problem as clearly as possible.

Upvotes: 2

Views: 2251

Answers (1)

Sammy McKay
Sammy McKay

Reputation: 41

So I was able to authorize the YouTube API and create a playlist. I have a backend hosted on localhost:8888 (doesn't matter just not what react is hosted on).

here is sample code for what I put in the server.js file (for the backend)

var express = require('express');
var app = express();
var passport = require('passport');
app.use(passport.initialize());
var YoutubeV3Strategy = require('passport-youtube-v3').Strategy;

passport.use(new YoutubeV3Strategy({
    clientID: YOUR_CLIENT_ID,
    clientSecret: YOUR_CLIENT_SECRET,
    callbackURL: 'http://localhost:8888/redirect',
    scope: ['https://www.googleapis.com/auth/youtube']
},
function (accessToken, refreshToken, profile, cb) {
    var user = {
        accessToken: accessToken,
        refreshToken: refreshToken
    };
    return cb(null, user)
}
));

passport.serializeUser(function(user, cb) {
    cb(null, user);
});

passport.deserializeUser(function(obj, cb) {
    cb(null, obj);
});

app.get('/authenticate', passport.authenticate('youtube'))
app.get('/redirect', passport.authenticate('youtube', { failureRedirect: '/login' }),
function(req, res) {
    res.redirect('http://localhost:3000' + '?access_token=' + req.user.accessToken)
})

app.listen(8888)

This is using Passport.js to do oauth for me, lots of documentation can be found on the site.

In react I have it so a button will open localhost:8888/authenticate and then that will redirect back to my application. If you are using this you need to make sure that on your google API credentials you have the javascript origin as http://localhost:8888 and the redirect URI as http://localhost:8888/redirect and the correct scope and application type.

This is the function I use in my app.js (react) to make the POST

getAPIdata() {
    let parsed = queryString.parse(window.location.search);
    let accessToken = parsed.access_token
    fetch('https://www.googleapis.com/youtube/v3/playlists?part=snippet', {
        method: 'POST',
        headers: {
            'Content-type': 'application/json',
            'Authorization': 'Bearer ' + accessToken,
        },
        body: JSON.stringify({
            'snippet':
            {
                'title':this.state.inputTitle
            }
        })
    }).then(response => response.json()).then(data => {
        console.log(data)
        window.alert('https://www.youtube.com/playlist?list=' + data.id)
    })
}

I was actually mostly correct with the first attempt I just had the authorization incorrect.

Here's a couple sources that helped me make my solution:

Hopefully this is helpful to someone, You can use the same code i used in server.js to authenticate most services by just changing the strategy.

A live version of my application can be found here. In the console it shows the response from the POST request, this should help if you have any issues. I know the alert is bad ui but this wasn't the intended use.

Thanks for reading :)

Upvotes: 2

Related Questions