The fetch api

There are two things that caught me by surprise in the fetch api that aren’t mentioned in the documentation

  • Only https is supported (this is unfortunate for any localhost usage)
  • The request is cached and it’s unclear for how long and how to prevent this (other than the super hacky appending of #${Math.random()})
1 Like

@Kilian A small suggestion: you could use ngrok to create a secure introspectable tunnel to localhost

Actually, I think Kilian did use ngrok in his demo at our XD plugin event in Amsterdam :slight_smile:

I did, it’s super useful! It’s not really something you could/should ship as part of an XD integration though.

The fetch documentation should also mention it won’t work with binary results like graphics.

I believe you can use 127.0.0.1 with regular http, even though localhost requires https. This is all based on the macOS App Transport Security feature, which by default blocks most insecure communications over the network. Why it applies that to localhost as well is a mystery to me, but I believe using the loopback IP address directly should get around it.

@kerrishotts, do you have any info on the other questions regarding caching and binary responses? I could swear we do support using fetch() to retrieve binary data…

2 Likes

I haven’t though to try using the loopback IP address… that would be a nice workaround.

As to SSL support, I know this will seem a distinction without a difference, but the API we built has no inherent restrictions on accessing insecure resources. Rather, it’s the host environment (macOS w/ ATS) that’s enforcing those restrictions. IIRC (and something to be careful of) Windows should access insecure resources without complaint.

For caching, it’s important to note that we use the OS’s networking APIs for XHR & Fetch. For example, on macOS, we use NSURLSession, which has a cache by default. It’s supposed to respect your response headers, so you should be able to configure your endpoint to expire the cache more frequently. (Let us know if this doesn’t hold, though.)

Finally, for binary data, we do support this, but not via FormData or Blobs yet. Instead, you have to use ArrayBuffer. Both FormData and Blob support is coming soon.

1 Like

I have some feedback as well. In some places the docs have helpful examples:

How to make network requests · Adobe XD Plugin Reference

But for fetch I don’t see any examples and what I’ve tried I can’t get to work,

I’m attempting to get localhost (check that it’s running):

function testServerPath() {
	var serverPath = "http://localhost/";

	fetch(serverPath).then(function(response) {
		console.log("Response: ", response)
	});
}

If http is not supported (only https) shouldn’t it error out? Tested https://www.google.com and it responds but nothing from http://www.google.com.

Update:
http://127.0.0.1 does respond when a server is running

Update 2:
Didn’t have catch on fetch but in the docs it says that it shouldn’t matter.

The fetch specification differs from jQuery.ajax() in two main ways:

  • The Promise returned from fetch() won’t reject on HTTP error status even if the response is an HTTP 404 or 500. Instead, it will resolve normally (with ok status set to false), and it will only reject on network failure or if anything prevented the request from completing.
fetch(serverPath).then(function(response) {
	log("Response: ", response)
}).catch(function(error) { 
	log("Error occurred", error); // TypeError: Network request failed
})

So if someone was to check for localhost running on http what should they do? I can create another question.

Note: I’ve turned my local server on and off and I’m checking using fetch with a url of http://127.0.0.1 and it’s still responding in both cases but visiting it in the browser it will report:

127.0.0.1 refused to connect.

when the server is turned off and load the home page when it is running.

Two years are over, still no blob support in PS?

The plumbing is in place – this works:

const request = new Request('https://mdn.github.io/fetch-examples/fetch-request/flowers.jpg')
const response = await fetch(request);
const buffer = await response.arrayBuffer();
const imageBlobURL = URL.createObjectURL(new Blob([buffer]));
document.querySelector("img").setAttribute("src", imageBlobURL);

For some reason the response.blob method is not present, even though Blob exists in the global namespace. @pkrishna / @Sujai can you take a look into why fetch isn’t registering a blob method on the response?

1 Like

@kerrishotts Is there a workaround for uploading an image to a URL which accepts multipart/form-data, since FormData is not supported ?

Is there planning blob support in PS? It’s missing.

Regards, John, manager, Work Time.

Hi @kerrishotts , the code works great in Phostoshop, thanks!. Now, would it be possible to save that file and then open it as a new Photoshop document? do I need to use batchPlay for that?

Hiya @AndresLP,
Do you mean you got this code working in your Photoshop Plugin? ie. opening a file within Photoshop from a URL source?

Hi @Symo470 , what I do is download the file and then open it in Photoshop:

  // Function to download file from url, save with user dialog, open in Photoshop
  async function downloadFileFromUrlAndOpen({ fileUrl, fileName }) {
    // Download file from url
    const request = new Request(fileUrl);
    const response = await fetch(request);
    const buffer = await response.arrayBuffer();

    // Save file to the filesystem
    const storage = require("uxp").storage;
    const file = await storage.localFileSystem.getFileForSaving(fileName);
    await file.write(buffer);
    try {
      await executeAsModal_openFile(file);
    } catch (e) {
      console.log(e);
    }
  }
/* Function to let user select a file, and open in Photoshop */
export function executeAsModal_openFile(file) {
  async function openFile(file) {
    console.log(file);
    if (file) {
      await require("photoshop").app.open(file);
    } else {
      return;
    }
  }

  try {
    require("photoshop").core.executeAsModal(async function () {
      openFile(file);
    });
  } catch (e) {
    if (e.number == 9) {
      showAlert(
        "executeAsModal was rejected (some other plugin is currently inside a modal scope)"
      );
    } else {
      console.log("exception!");
      // This case is hit if the targetFunction throws an exception
    }
  }
}
2 Likes

Thanks @AndresLP,
What am I doing wrong if I get an:
Unexpected token 'export'
error upon launching your code?

Hi @Symo470 , you can take the “export” word out from the code, it shouldn’t be there unless you are calling the function from a separate file (as a JS module)

Thanks @AndresLP. You can probably tell I’m pretty new at this…

So I’ve tried out your code and it doesn’t seem to do what I am expecting. Do I call the downloadFileFromUrlAndOpen function with strings for the fileUrl and fileName arguments?

I tried that and when I console.log the request variable is suggests that the url:“undefined”.

Appreciate your help.

Hi @Symo470 , are you sending fileUrl and fileName as object properties? something like this:

downloadFileFromUrlAndOpen(
{
fileUrl: “https://www.someplace.com/file.jpg”,
fileName: “image name”
}
)

I’ve tried your suggestion, but I feel like the code hangs at the line:
const response = await fetch(request);
I’ve tried multiple Url’s for testing, but just no working for me.