Retrieve all your Medium Blog Posts and Display them on your Website in a Neat Fashion

I am the lead web developer at Why Waste?. At one point in time, we at Why Waste? wanted to start writing blogs on medium. After we published a few, I wondered whether it would it would be possible to fetch all user blogs off medium and get it to the website’s subdomain (medium.whywaste.io).

Background Research

After doing some research I found out that Medium had a free custom domain service where you could basically create another medium page on one of your website’s custom domain that would only contain your profile and it’s blog posts; however, this service is not free anymore and currently costs 75$ (Reference- Benjamin Dada, MSc.’s post- https://medium.com/benjamin-dada/medium-now-charges-75-to-set-up-a-custom-domain-54ab54117fd4). Parts of the Javascript logic, in this blog post, for fetching blog posts were taken from Jason Matthew’s blog post.

The purpose of writing this blog post

I have found other blog posts regarding this on the internet, some which have no easy front-end design implementation, some which are poorly described, and some which are useless XD. I can assure you that this blog post is easy to understand and seamlessly implementable if you have an understanding of the Prerequisites.

Prerequisite Knowledge:

HTML5, JavaScript, JQuery(Just to make the JavaScript cleaner :P), and Bootstrap 4.

If you do not have any knowledge about these, then you can either learn it off Treehouse (teamtreehouse.com) or W3 Schools(w3schools.com) or Youtube.

Steps

Getting user feed in JSON format

We can look at a user’s feed in JSON by adding “/latest?format=json” (medium.com/@username/latest?format=json); however, that is going to throw a CORS(Cross-Origin Resource Sharing) error. In simple words, CORS error implies that the web server is preventing us from gaining unauthorized access to a web application from a different origin (domain, protocol, and port). You can read up more about CORS error here. Nevertheless, if you try and use “/latest?format=json”, you will get the error below which basically means it is a dead end :(.

Google Chrome Developer Console Error Message due to CORS error

RSS to the Saviour

Luckily, medium lets you extract RSS feed from a user’s profile. Go to medium.com/feed/@username to get your RSS feed (More details here). Now, we can use rss2json.com to convert our RSS feed to JSON using their API and get the JSON data!

Now that we have our JSON data, let’s dive into the code!

Including Libraries

First, let us include JQuery in our code:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Note- I like using the Google JQuery CDN (Content Delivery Network). Nevertheless, you could include JQuery in whatever way you wish to. (More information about including JQuery here)

While we are at it let’s also include the Bootstrap 4 CSS library. Again, I have used their CDN, but you can include it however you want!

<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">

We’re done with importing all the libraries needed! Now let’s start getting some data.

Getting feed from the API

Once you convert your RSS feed to JSON on the rsstojson.com converter API, you will get a URL that looks like this- https://api.rss2json.com/v1/api.json?rss_url=https%3A%2F%2Fmedium.com%2Ffeed%2F%40mediumstaff. We need to GET data from this url.

We will use the $.get() method which is an AJAX (Asynchronous JavaScript And XML- You don’t have to learn AJAX for this tutorial; however, you can find more information here) short hand provided by JQuery.

Syntax for $.get() is

$.get(URL,data,function(data,status,xhr),dataType)

This looks intimidating at first, but it isn’t. I’ll explain each parameter:

  • URL- The URL, you want to request data from (Required)
  • data- Specifies data to send to the server along with the request (Optional)
  • function- Callback function if it recieves a successful response (Required in this case)
  • dataType- Specify the type of response to recieve. By default, JQuery takes care of this (Optional)

More information on the $.get() method here

Let’s use this function in our code

Defining parameters in our use case:

Now, let’s define our callback function:

var data = {rss: "https://medium.com/feed/@mediumstaff"}
$.get(https://api.rss2json.com/v1/api.json?rss_url=https%3A%2F%2Fmedium.com%2Ffeed%2F%40mediumstaff, data, funciton(response){
//Checking Response Status
if (response.status == 'ok') {
//Defining a variable where I create my response data
var output = '';
//Response...
}
});

Let’s iterate to get multiple posts

To iterate through the posts let’s use, the JQuery $.each() method.

Syntax for the $.each() method

$.each(object, function( index, value ) {
//Code to be Iterated
}

Note- The $.each() function is not the same as $(selector).each(), which is used to iterate, exclusively, over a jQuery object. More information about the $.each function over here.

Let’s iterate through the posts in our code

var data = {rss: "https://medium.com/feed/@mediumstaff"}
$.get(https://api.rss2json.com/v1/api.json?rss_url=https%3A%2F%2Fmedium.com%2Ffeed%2F%40mediumstaff, data, funciton(response){
//Checking Response Status
if (response.status == 'ok') {
//Defining a variable where I create my response data
var output = '';
//Response...
$.each(response.items, function (k, item) {
//Iteration
return k < 10;
});
}
});

Note- If you are confused about why I am returning k<10. What I am basically doing is telling the iterator to end the loop once the index (k) becomes 9 (index starts from 0). The Medium RSS feed unfortunately gives only 10 of the user’s latest blog posts; hence, we can only retrieve 10 blog posts.

Great! We have successfully completed getting posts from the API!

Displaying data on the front-end

Before we start, the reason I’m using Bootstrap 4 and not my own custom CSS is because Bootstrap is much faster to deploy. I know how to create my own custom CSS files, but I don’t want to spend too much time on that :)

Now, let’s use some HTML and Bootstrap 4.

First, I want to add a Jumbotron to give a nice and bold header.

<body>
<div class="container mt-5 pt-5" id="medium">
<div class="jumbotron text-center">
<div id="logo"></div>
<h1 class="display-4 mb-3">@mediumstaff's Posts</h1>
<a class="btn btn-outline-primary btn-lg" href="https://medium.com/@mediumstaff" target="_blank" role="button">View all posts</a>
</div>
<div class="row" id="jsonContent">
</div>
</div>
</body>

I have also defined a div with the id of jsonContent because we are going to feed our JSON data into this div.

Now let’s add the JavaScript to get our data

var $content = $('#jsonContent');
var data = {
rss: 'https://medium.com/feed/@mediumstaff'
};
$.get('https://api.rss2json.com/v1/api.json?rss_url=https%3A%2F%2Fmedium.com%2Ffeed%2F%40mediumstaff', data, function (response) {
if (response.status == 'ok') {
$("#logo").append(`<img src="${response.feed["image"]}" class="rounded mx-auto d-block">`)
var output = '';
$.each(response.items, function (k, item) {
output += `<div class="card mb-3 mx-auto mr-5 " style="width: 20rem;">`; //Adding Each Post into a card
var tagIndex = item.description.indexOf('<img'); 
//Find where the img tag starts
var srcIndex = item.description.substring(tagIndex).indexOf('src=') + tagIndex; //Find where the src attribute starts
var srcStart = srcIndex + 5; //Find where the actual image URL starts; 5 for the length of 'src="'
var srcEnd = item.description.substring(srcStart).indexOf('"') + srcStart; //Find where the URL ends

var src = item.description.substring(srcStart, srcEnd); // Extract just the URL

output += `<img src="${src}" class="card-img-top" alt="Cover image">`; //Adding the image to the Card

output += `<div class="card-body">`; //Defining the Card body

output += `<h5 class="card-title"><a href="${item.link}">${item.title}</a></h5>`; //Hyperlinking the title of the card to the post
var yourString = item.description.replace(/<img[^>]*>/g,""); //We do not want an image in the description so I replace the images with "" (Empty text)

yourString = yourString.replace('h4', 'p'); //Replacing h4 tags with p tags

yourString = yourString.replace('h3', 'p'); //Replacing h3 tags with p tags

var maxLength = 120; // maximum number of characters to extract

//trim the string to the maximum length
var trimmedString = yourString.substr(0, maxLength);
//re-trim if we are in the middle of a word
trimmedString = trimmedString.substr(0, Math.min(trimmedString.length, trimmedString.lastIndexOf(" ")))

output += `<p class="card-text">${trimmedString}...</p>`;

output += `<a href="${item.link}" class="btn btn-outline-success">Read More</a>`; //Adding a Read More button that links to the original post.

output += '</div></div>';

return k < 10;

});

$content.html(output); //Feeding the output into the div with id jsonContent

}
});

The code might look slightly messed up, but don’t worry, I have created a codepen project, so that you can take my code (Link is at the bottom of this post). This is only for your understanding purpose.

I defined a variable called output, which stores the content of each post. I have tried to put a lot of comments explaining each line of the code. I have used a Bootstrap card to display all the elements in an orderly fashion.

I’ve added comments at each line to help you understand the code.

Link to the complete code is given at the bottom of the next section


Optional Section that will make the front end design aesthetic

JavaScript Promise

Let’s understand what a JavaScript promise is and we’ll later understand how it is going to be useful later on in our code.

What is a Promise?

No, I wasn’t referring to the promise you keep with your loved ones. I am referring to a JavaScript promise.

“The Promise object represents the eventual completion (or failure) of an asynchronous operation, and its resulting value.”
Reference- MDN web docs

If you still didn’t understand what a promise is, it is a object which returns a status of success or error (aka resolve, reject).

Let’s take a look at an example. Let’s say you want to run one JavaScript function first and then the next because you have to pass in the value from the result of the first function into the second. However, if you just call them separately, there is a possibility that the second function can finish before the first and throw an error as it hasn’t received the parameter from the first function. Promise helps us tackle this problem easily. We wait for a response and run the next function. Simple? Isn’t it?

Now, that you’ve understood Promises, let’s dive in to applying it!

Adding dynamic Pagination

If you want to present the cards over different pages, you should go through this section. (Recommended)

If you don’t know what pagination means, pagination are the controls that you see at the bottom of a page with a lot of content that helps you navigate through the content on multiple pages. (More information on pagination here)

Adding it in our Code

$(function () {
//Creating a new Promise and passing resolve as the function's parameter. The arguments passed into resolve method will execute only after a particular line of code is done.
var mediumPromise = new Promise(function (resolve) {
var $content = $('#jsonContent');
var data = {
rss: 'https://medium.com/feed/@mediumstaff'
};
$.get('https://api.rss2json.com/v1/api.json?rss_url=https%3A%2F%2Fmedium.com%2Ffeed%2F%40mediumstaff', data, function (response) {
if (response.status == 'ok') {
$("#logo").append(`<img src="${response.feed["image"]}" class="rounded mx-auto d-block">`)
var output = '';
$.each(response.items, function (k, item) {
output += `<div class="card mb-3 mx-auto mr-5 " style="width: 20rem;">`;
var tagIndex = item.description.indexOf('<img'); // Find where the img tag starts
var srcIndex = item.description.substring(tagIndex).indexOf('src=') + tagIndex; // Find where the src attribute starts
var srcStart = srcIndex + 5; // Find where the actual image URL starts; 5 for the length of 'src="'
var srcEnd = item.description.substring(srcStart).indexOf('"') + srcStart; // Find where the URL ends
var src = item.description.substring(srcStart, srcEnd); // Extract just the URL
output += `<img src="${src}" class="card-img-top" alt="Cover image">`;
output += `<div class="card-body">`;
output += `<h5 class="card-title"><a href="${item.link}">${item.title}</a></h5>`;
var yourString = item.description.replace(/<img[^>]*>/g,""); //replace with your string.
yourString = yourString.replace('h4', 'p');
yourString = yourString.replace('h3', 'p');
var maxLength = 120; // maximum number of characters to extract
//trim the string to the maximum length
var trimmedString = yourString.substr(0, maxLength);
//re-trim if we are in the middle of a word
trimmedString = trimmedString.substr(0, Math.min(trimmedString.length, trimmedString.lastIndexOf(" ")))
output += `<p class="card-text">${trimmedString}...</p>`;
output += `<a href="${item.link}" class="btn btn-outline-success">Read More</a>`;
output += '</div></div>';
return k < 10;
});
resolve($content.html(output)); //Passing the output into the resolve function. This means that once it writes the output into the div, it will send a success response to carry out the next function in the .then() method.
}
});
});
//.then() method executes the function after the Promise is resolved
mediumPromise.then(function()
{
//Pagination
pageSize = 3;
var pageCount = $(".card").length / pageSize; //Calculating the number of pages it the content needs to spread over
//Running a loop to add the pagination links(numbers) dynamically
for (var i = 0; i < pageCount; i++) {
$("#pagin").append(`<li class="page-item"><a class="page-link" href="#">${(i + 1)}</a></li> `); //Appending the list elements to the unordered list(ul) in the HTML code
}
                //Highlighting the first page number on load by giving it the class "active"
$("#pagin li:nth-child(1)").addClass("active");
                //showpage adds the content on to each page
showPage = function (page) {
$(".card").hide();
$(".card").each(function (n) {
if (n >= pageSize * (page - 1) && n < pageSize * page)
$(this).show();
});
}
//On page load, It will display contents of first page
showPage(1);
//Highlights each page number(list element) onclick
$("#pagin li").click(function () {
$("#pagin li").removeClass("active");
$(this).addClass("active");
showPage(parseInt($(this).text()))
});
});
});

Now, you should see links like this

Dynamic Pagination

Yay! We’re done!

Access to the complete code- ref.nadig.in/codepen-medium-to-website


If you have any questions about the code or any other suggestions/ideas. Hit me up in the comments section :)

Stay up to date with my medium posts by following Pranav Shikarpur on medium and frequently visiting- medium.nadig.in

My Website- www.nadig.in