
Question:
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 <a href="https://github.com/Medium/medium-api-docs/issues/51" rel="nofollow">github issue</a>, but couldn't find any workaround. Is there any way to make this request work on the client side?
Answer1:You can get the HTML from <a href="https://medium.com/@ev/latest" rel="nofollow">https://medium.com/@ev/latest</a> 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 <a href="https://cors-anywhere.herokuapp.com/" rel="nofollow">https://cors-anywhere.herokuapp.com/</a>. Here’s how to do it using the standard Fetch API:
<pre class="snippet-code-js lang-js prettyprint-override">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))
<pre class="snippet-code-html lang-html prettyprint-override"><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 <em>How to use a CORS proxy to get around “No Access-Control-Allow-Origin header” problems</em> in the answer at <em><a href="https://stackoverflow.com/questions/43871637/no-access-control-allow-origin-header-is-present-on-the-requested-resource-whe/43881141#43881141" rel="nofollow">No 'Access-Control-Allow-Origin' header is present on the requested resource—when trying to get data from a REST API</a></em>.
<hr />Incidentally, if instead you want JSON, you can try <a href="https://medium.com/@ev/latest?format=json" rel="nofollow">https://medium.com/@ev/latest?format=json</a> 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, <a href="https://github.com/Medium/medium-api-docs/issues/51#issuecomment-267711057" rel="nofollow">per a comment from a Medium developer in their issue tracker</a>:
<blockquote>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.
</blockquote>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:
<pre class="snippet-code-js lang-js prettyprint-override">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"))
<pre class="snippet-code-html lang-html prettyprint-override"><script src="https://unpkg.com/axios/dist/axios.min.js"></script>
The code hits the catch
because apparently even when you specify responseType: 'text'
, <a href="https://github.com/axios/axios/issues/907#issuecomment-322054564" rel="nofollow">Axios apparently still tries the parse the response as JSON</a>:
This is because JSON.parse
is always tried in the response, even if responseType
is text. We should fix that indeed.
And <a href="https://medium.com/@ev/latest" rel="nofollow">https://medium.com/@ev/latest</a> 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).
Answer2:This is currently not allowed by Medium (There server doesn't respond with the Access-Control-Allow-Origin
header). Probably because of security concerns.
As suggested in the GitHub issue you linked to, a possible solution will be to tunnel the request to Medium through your server (as proxy). You can create an endpoint on your server (i.e. http://my-server.com/get-medium/@ev/latest
) that will retrieve the requested Medium page (on the server side) and will return it to the client side.
This comment to the issue describes a way to do that using AWS Lambda as the proxy server - <a href="https://github.com/Medium/medium-api-docs/issues/51#issuecomment-278467906" rel="nofollow">link</a>