Reddit API Application only OAuth in Javascript



Before you begin developing your Reddit application, you will need to make sure that your app is authorized to access the Reddit API. Authorization is done through OAuth tokens that you receive from the Reddit server. The flow is pretty straightforward:

  1. Application Requests Token
  2. App Receives Token that’s valid for an hour
  3. App makes API calls with given token
  4. App request new token when the previous token expires

For this tutorial, we are going to build a simple app that grabs the new posts from a subreddit. Because we do not need any user data (profile, login, etc), we are going to simply use the application only strategy. This allows us to perform the steps above without dealing with the hassle of user login.

Client ID

Before you get started, you need to register your app in order to retrieve a client id.  Go to the Reddit prefs app page and click the link that says are you a developer. It will ask you to fill out some basic information, then you will see the client id (it’s the long string that’s posted underneath your app’s title).

Grabbing Token

Let’s start by defining some constants in our javascript file:

const dataStore = window.localStorage;
const clientID = ''; //IMPORTANT - THIS NEEDS TO BE YOUR CLIENT ID FROM YOUR APP
const subreddits = ['android'];

We are using the HTML5 local storage API so that we can save the user’s token and device id to local storage, and retrieve them on subsequent API requests.

Let’s begin by creating an init function that will be called on page load:

function init() {
 if (!dataStore.getItem('deviceID')) {
 dataStore.setItem('deviceID', getRandomID());
 console.log('Created new deivce ID');
 }
 grabStories();
}

This function checks to see if there’s a device id that already exists in local storage. If there is, we proceed to the grabStories() function. If not, it will call a function to create a random device id and set it as the string value of ‘deviceID’ in local storage.

Here’s the getRandomID() function. This is courtesy of this post on StackOverflow because getting a truly random UID is a difficult task in JavaScript.

function getRandomID() {
 return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
 let r = Math.random() * 16 | 0,
 v = c == 'x' ? r : (r & 0x3 | 0x8);
 return v.toString(16);
 });
}

After you have the init function and getRandomID function in place, create the grabStories() function. The purpose of this function is to trigger the API call to grab the posts from a predefined subreddit.

function grabStories() {
 if (!validToken()) {
 getAccessToken();
 } else {
 for (const i of subreddits) {
 query(i, queryCallback);
 }
 }
}

This function first checks to see if we have a valid token. For that, we use the function called validToken() defined here:

function validToken() {
 if (!dataStore.getItem('accessToken')) {
 return false;
 }

const currentDate = new Date();
 const expires = new Date(dataStore.getItem('expires'));
 const difference = (expires.getTime() - currentDate.getTime());
 const minutesDifference = Math.ceil(difference / (1000 * 60)); // minutes difference
 if (minutesDifference < 5) {
 return false;
 }

return true;
}

The validToken() function essentially checks to see if a valid token exists in local storage. If we do have a valid token, we check the expiration. You’ll see later that I also save the expiration time to location storage. For now, just keep it there and trust that it will work. I return false if it’s about to expire in less than 5 minutes. There’s really no concrete reason for this, I just figure, with less than five minutes left you might as while grab a new one.

Going back to the grabStories() method defined earlier, you will see that we have another function called getAccessToken(). Here’s what the function looks like:

function getAccessToken() {
 $.ajax({
 type: 'post',
 url: 'https://www.reddit.com/api/v1/access_token',
 dataType: 'json',
 headers: {
 Authorization: `Basic ${btoa(`${clientID}:` + '')}`,
 },
 data: { grant_type: 'https://oauth.reddit.com/grants/installed_client', device_id: dataStore.getItem('deviceID') },
 success(data) {
 if (data.access_token) {
 dataStore.setItem('accessToken', data.access_token);
 dataStore.setItem('expires', new Date().addHours((data.expires_in) / 3600));
 grabStories();
 }
 },
 error(err) {
 console.log(err);
 },
 });
}

This function might look complicated, but it’s not. Essentially, it’s just a call to the Reddit API to retrieve an access token. You will get back a json response that will contain the access_token and the amount of seconds that the token is valid for. Usually, this time is 3600 seconds (or one hour). However, it’s never safe to assume that this will always be the case. So I create a new date in the future that’s the current time plus the expiration in seconds time and save this to local storage. For this, I added a prototype function to the Date object called addHours.

Again, thanks to Stackoverflow for this because dealing with dates in Javascript is a mess:

Date.prototype.addHours = function (h) {

this.setTime(this.getTime() + (h*60*60*1000));

return this;

};

So like I mention above, I add the expiration time (that 3600 seconds returned from the redditt json response) to the current time and save that to a key called “expires” in location storage. I then use this value when figuring out if the token is about to expire.

So returning back to my grabStories() function, assuming that we have an access token and it’s valid, we can proceed to the query function:

/**
 * Queries the reddit api for a specific subreddit
 * @param {* string - subrredit name} subreddit 
 * @param {*function - callback function} callback 
 */
function query(subreddit, callback) {
 const url = `https://oauth.reddit.com/r/${subreddit}/new`;
 $.ajax({
 type: 'get',
 url,
 dataType: 'json',
 headers: {
 Authorization: `Bearer ${dataStore.getItem('accessToken')}`,
 },
 success(data) {
 callback(data);
 },
 error(err) {
 console.log(err);
 },
 });
}

This function basically calls the Reddit API with a specific subreddit. The reddit api will return all the new post from that particular subreddit. Pay attention to the Authorization that you must use. It’s basically an encoding og your access token. On success, I trigger a callback function that I pass to the query function.

Going back to the grabStories() function one last time, you will see that I pass in a function called queryCallback(). This function essentially just writes the title of each post obtained from the json data to the page:

/**
 * Write the data to page
 * @param {*} data - json object
 */
function queryCallback(data) {
 const list = document.createElement('ul');
 const posts = data.data.children;
 posts.forEach((element) => {
 const item = document.createElement('li');
 item.innerHTML = element.data.title;
 list.appendChild(item);
 });
 document.body.appendChild(list);
}

That’s it! When you run the program, your output should look like the following:

You can download the full project on GitHub.