Hi!
How to properly make requests to remote websites from a plugin? I tried simple fetch("URL")
, but it is blocked by CORS: Access to fetch at 'https://url' from origin 'app://obsidian.md' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
My personal fave way to work around CORS issues is to create a middleware server that you control. From there, you can make whatever external calls you want. Then from your plugin fetch($YOUR_SERVER)
because you control both ends you can set the correct CORS policy.
A super simple (and free) way to do this is using Glitchâs express server template. Add the cors
npm package and use it with express: app.use(cors())
. Youâll be able to fetch any endpoint you setup in express
within your plugin.
There are other ways and some are faster/easier, but controlling both sides of the request is the best way imho.
Sorry for dumb questions, Iâm not really familiar with the javascript ecosystem and approaches. I understand the CORS concept in the context of web pages. But shouldnât it be possible to make an arbitrary web request from a local application such as obsidian and ignore CORS headers? Creating, hosting and maintaining a separate server sounds like too much of a hassle.
Obsidian is an electron app which means it is essentially Chrome under the hood. Youâll need to handle CORS like you would in any other client-side cross domain request just as the error indicates.
You could try throwing no-cors
on the fetch request. Or Google handling cors with fetch.
If you want to go the quick-n-dirty route lookup âcors anywhereâ. I wouldnât ship a plugin with cors anywhere though as itâs liable to break.
Setting up a server of Glitch is pretty fast and easy tbh. Just use the express template and move your cross-browser fetching into the glitch app.
Regular webpages opened in Chrome donât get access to the file system, while obsidian plugins can use the javascript fs
module for local files. I thought that something similar is available for web requests.
If you are okay with your plugin only working in the desktop you can use the package node-fetch. GitHub - node-fetch/node-fetch: A light-weight module that brings the Fetch API to Node.js nodeâs libraries bypass cors restrictions but are only available in the desktop apps.
Hi,
I have a similar problem. I want to implement a CalDav Client Plugin and get the same error.
I try to implement a middleware with express and the cores package, but it doesnât work. Can someone give me please a code example, a tutorial or a link to the GlichĂs express server?
Thanks
Kantiran
You can overcome this by using node.jsâ http/https module instead of fetch (or use a convenience module like request). It doesnât have the same limitations.
require('request')('http://example.com/', (error, response, body) => {
console.log(JSON.parse(body));
});
I want to get google books API data, but Iâm running into this same CORs policy issue. @intellectronica, I donât think your solution works for me, at least while trying in the dev console. Some people might have luck with this article. I havenât got it to work yet.
Iâm not working on a plugin, but Iâm using a dataviewJS code block to download image data.
Hello @mjduck,
This may interest you !
Itâs a script to be used inside Quickadd plugin to fetch book data from Google Books API, and then automatically create a note from a template.
You can check line 183
of the script how the request is made using request
. I have not encountered CORs issues with that function.
The author of Quickadd plugin made a script to fetch movie data using OMDB API (using the same request
function) which helped me a lot to write this Google Books API script.
Well thatâs neat. Thanks @JamesKF !
In plugin you should import requestUrl
function provided by obsidian
package, see the declaration at obsidian-api/obsidian.d.ts at c01fc3074deeb3dfc6ee02546d113b448735b294 ¡ obsidianmd/obsidian-api ¡ GitHub
Is this usable from within a Templater template or DataviewJS? That is, is it usable within arbitrary JS executing within an Obsidian plugin?
Asking as something of a node/TS newbie but experienced developer. I assume that this depends on what sort of library search paths are available from within the environment provided at runtime by these respective plugins?
DataviewJS and Templater have access to most functions.
These included.
You can check by opening the Console in the Developer tools, and see if it shows up.
The one thing they canât do is anything related to event handling.
So how would a basic dataviewjs codeblock look that incorporates such a requestUrl
call? How would the response be handled inside such a dataviewjs codeblock?
Imagine the request I want to use returns a JSON string which contains data/numbers I want to see in a table (or even visualize with the Obsidian Charts plugin)?
As a quick example:
One of my notes has a weather forecast embedded that uses DVJS
Here is a part of it
const headers = {
"Authorization": "Bearer whatever"
}
const forecast = await requestUrl({url: "https://weatherapi/forecast", headers})
dv.table(["Datum", "Icon", "Beschreibung", "Min", "Max", "Regen", "Luftfeuchtigkeit"], forecast.json.map(day => {
return [
dv.date(day.date).day + ".",
"![icon|50](" + day.icon + ")",
day.description,
day.temperature_min + "°C",
day.temperature_max + "°C",
day.probability_of_precipitation + "%",
day.humidity + "%"
]}))
The data returned by that API looks like this:
[
{
"date": "2022-06-26T11:00:00Z",
"sunrise": "2022-06-26T02:55:38Z",
"sunset": "2022-06-26T20:00:30Z",
"temperature_day": 19.19,
"temperature_night": 18.76,
"temperature_min": 18.3,
"temperature_max": 21.55,
"feels_like_day": 19.38,
"feels_like_night": 18.65,
"pressure": 1016,
"humidity": 85,
"dew_point": 16.65,
"uvi": 6.26,
"clouds": 100,
"visibility": 0,
"wind_speed": 7.47,
"wind_degree": 0,
"description": "Leichter Regen",
"icon": "icon_url_here",
"probability_of_precipitation": 0.5
},
....
]
Awesome, @joethei, I guess this helps a lot! Will dive into this, asap!
Thanks a lot!!!
I am using requestUrl to avoid this issue but it seems that I am getting an issue anyway. Error thrown:
{
âstatusâ:400,
âheadersâ:{
âvaryâ:âOrigin, Access-Control-Request-Method, Access-Control-Request-Headersâ,
âx-content-type-optionsâ:ânosniffâ,
âx-frame-optionsâ:âDENYâ,
âx-xss-protectionâ:â1; mode=blockâ
}
}
Anyone got any ideas?
Youâd have to post the request code for anyone to give any hints.
HTTP status 400 âindicates that the server cannot or will not process the request due to something that is perceived to be a client error (for example, malformed request syntaxâ.
So it sounds like the code to create the request is invalid.
I do apologise, first time posting here ⌠not something I am used to doing
Here is the code, itâs a pretty simple call:
const options: RequestUrlParam = {
url: url,
method: 'POST',
headers: {
'X-Api-Key': this.settings.apiToken,
'Content-Type': 'application/json'
},
body: json
}
var response: RequestUrlResponse;
try
{
response = await requestUrl(options);
return response.text;
}
catch(e) {
console.log(JSON.stringify(e);
}
I captured the actual request and sent it to the server (headers and all) using Postman (just to check that what I am sending is valid) and the server accepted it no problem.
I canât actually see anything of my call appearing in the Network tab of Developer Tools. I can see other traffic but nothing on this call. All seems a bit weird but I am very new to this so hoping someone can spot something obvious.
It might also be worth (or not) pointing out that it is throwing an error rather than returning the status via the response.