javascriptcorscross-browseraxiosmedium.com-publishing-api

Error while trying to access public stories from Medium API on client side


I'm trying to access Medium's API to get a list of public stories from a user. However, I'm getting a CORS error when I try to access it on the client side. Here's the code

axios.get(`http://medium.com/@ev/latest`).then((res)=>{
  console.log(res.data)
})
.catch((error)=>{
  console.log(error)
})

I did some research and found this github issue, but couldn't find any workaround. Is there any way to make this request work on the client side?


Solution

  • You can get the HTML from https://medium.com/@ev/latest by making your request through a CORS proxy — either a proxy you set up yourself or else just by using a public open CORS proxy like https://cors-anywhere.herokuapp.com/. Here’s how to do it using the standard Fetch API:

    fetch("https://cors-anywhere.herokuapp.com/https://medium.com/@ev/latest")
      .then(res => res.text())
      .then(text => document.querySelector("div").innerHTML = text)
      .catch(error => console.log(error))
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <div></div>

    For more details — including how to set up your own CORS proxy on Heroku in just a few minutes, see How to use a CORS proxy to get around “No Access-Control-Allow-Origin header” problems in the answer at No 'Access-Control-Allow-Origin' header is present on the requested resource—when trying to get data from a REST API.


    Incidentally, if instead you want JSON, you can try https://medium.com/@ev/latest?format=json but you’ll find that what you get back isn’t actually valid JSON; instead it starts out like this:

    ])}while(1);</x>{"success":true,"payload":{"user":{"userId":"268314bb7e7e","name"…
    

    Apparently that’s intentional, per a comment from a Medium developer in their issue tracker:

    The JSON page is not intended to be used as a read API. The extra code is there to support our own use and is a standard technique to avoid JSON hijacking.

    That’s trivial to work around, though: Just first handle the response as text in your client code, and strip out the ])}while(1);</x> from the start of it, and then run JSON.parse on what remains.

    But as far as using Axios to get the response as text, I think you’ll find it’s not going to work as expected even if you make the request through a CORS proxy; try this:

    axios.get('https://cors-anywhere.herokuapp.com/http://medium.com/@ev/latest', {
        responseType: 'text'
      })
      .then(res => console.log(res.data))
      .catch(error => console.log("ERROR"))
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>

    The code hits the catch because apparently even when you specify responseType: 'text', Axios apparently still tries the parse the response as JSON:

    This is because JSON.parse is always tried in the response, even if responseType is text. We should fix that indeed.

    And https://medium.com/@ev/latest is HTML, not JSON, so running JSON.parse on it will fail.

    That’s why the first snippet in this answer uses the Fetch API instead (you can get text back with it).