DEV Community

Mary
Mary

Posted on • Originally published at thehappycactus.dev on

How to Send a Request for Data from a Website to an Extension

While the docs for creating a Chrome extension are really quite good, I initially struggled to figure out the right way for my extension to communicate with a webapp (specifically: sending a request for data from the webapp to an extension and receiving a response). There can be a lot to read, and with extension changes from Manifest v2 to v3 some docs can be a bit misleading as well. I thought I’d distill my knowledge on this to a simple tutorial and hopefully make this easier for anybody out there who needs it.

If extension creation is new to you and you’re looking to request an extension’s saved data from your website, allow me to lay it out for you. 👍

Initial Assumptions/Requirements

This tutorial assumes a couple things:

  1. You have at least a basic skeleton of both your website and extension complete; we won’t be going through how to create an extension (or website) from scratch.
  2. Your extension is using Manifest V3
  3. Your extension saving data using the Chrome Storage API (chrome.storage). I’ll be using chrome.storage.local specifically, but I don’t think it should matter if you’re using a different storage area (sync, session, or managed).

Alrighty? Let’s get started!


Quick Jumps


Website Setup - Sending a Message

Let’s start by sending a message from your website to the extension.

1. Get your extension’s ID

You’ll need to obtain the ID of your extension so your website knows the extension with which it’s communicating. This is easy enough:

  1. Open your Extensions list in Chrome (chrome://extensions/)
  2. Switch on the “Developer Mode” toggle (in the top-right corner)
  3. The ID will be easily visible within your extension’s box

2. Send a message to the extension

To send your request you’ll use chrome.runtime.sendMessage within your webapp.

You may be saying to yourself, “Wait a second, I thought websites didn’t have access to certain Chrome extension-specific APIs! How can we be using chrome.runtime.sendMessage?” That’s a great question.

Within your extension setup (further below), you’ll update your manifest to include a new key called externally_connectable - any site set up in this key’s value will have the messaging API exposed to it!

const extensionId = 'myextensionsid'; // You should have this from step 1

chrome.runtime.sendMessage(
    extensionId,
    'the message to send - can be a string or an object',
    (response) => {
        // Callback upon the extension's response
    }
);
Enter fullscreen mode Exit fullscreen mode

Shia Lebouf in a wig and mustache mouthing the word 'magic'

3. Add a differentiating type to your message (optional)

When sending a message to your extension, you might want to include a type or some such specific identifier in your data so the extension can verify that the message it’s receiving is the one you’ve intended for it. This is also useful if you have a couple different messages you want to be able to send; a type will help you differentiate between the two.

const extensionId = 'myextensionsid'; // You should have this from step 1

chrome.runtime.sendMessage(
    extensionId,
    {
        type: 'fetch-data',
        data: 'some data you might want to send'
    },
    (response) => {
        // Callback upon the extension's response
    }
);
Enter fullscreen mode Exit fullscreen mode

4. Handle the extension’s response

We won’t implement anything complex here - though we’ll include a data property on the returned response.

const extensionId = 'myextensionsid'; // You should have this from step 1

chrome.runtime.sendMessage(
    extensionId,
    {
        type: 'data-fetch',
        data: 'some data you might want to send'
    },
    (response) => {
        console.log(response.data);
    }
);
Enter fullscreen mode Exit fullscreen mode

Quick Jumps


Extension Setup - Listening for and Responding to a Message

With your website ready to send a message to the extension and receive a response, let’s ensure the extension is ready to listen and reply!

1. Add a service worker to your extension

If you haven’t yet added a service worker yet, there’s multiple steps involved to set it up successfully:

  1. Create the actual service worker file. E.g., service-worker.js
  2. In your manifest, add the background key with your new background script file name under service_worker
  3. In your manifest, add the externally_connectable key with an array of “matches” that include the websites for which you want this extension to listen
"background": {
    "service_worker": "service-worker.js"
},
"externally_connectable": {
    "matches": ["https://localhost:<portnumber>/*"]
}
Enter fullscreen mode Exit fullscreen mode

A couple helpful notes here:

  • Matches can be as complicated as you need them to be, but you can also easily focus them on a single page, including localhost for development.
  • Depending on if you’re looking at v2 or v3 Manifest versions in the docs, you may see the phrase “background script” used in a similar context to “service worker”. While I’m fairly sure there’s some overlap between these 2 concepts, for our purposes it’s important to work exclusively with the “extension service worker” concept, which the Chrome docs use explicitly for Manifest V3. This conflation tripped me up a few times, so I wanted to make that clear here. 👍

2. Add a message listener

Add a listener to your service worker for the message incoming from your webapp:

chrome.runtime.onMessageExternal.addListener(
    (message, sender, sendResponse) => {
        // You'll add your response to the event here!
    }
);
Enter fullscreen mode Exit fullscreen mode

The only input parameter for addListener is a callback whose signature includes:

  • message - the message sent from your website
  • sender - a MessageSender object, particularly useful, I think, if your extension needs to know what tab is open.
  • sendResponse - callback that, as described, sends the response back to the caller (in our case, your web site)

3. Send a response back to your web site

Once you’ve added the skeleton of your listener, it’s time to send a response. This response can be a simple string or a complex object.

If you added the optional type key in your message sent from the webapp, check for it here to determine if you want to respond.

chrome.runtime.onMessageExternal.addListener(
    async (request, sender, sendResponse) => {
        if (!request.type === 'data-fetch') {
            return;
        }

        const storageData = await chrome.storage.local.get('my-data');

        sendResponse({
            type: 'data-response',
            data: storageData['my-data']
        });
    }
);
Enter fullscreen mode Exit fullscreen mode

A couple items to note here:

  • The listener function is now async, as we want to wait for the storage to return before sending the reponse.
  • Remember that when you call storage.local.get(), the object returned includes the initial my-data key:
    await chrome.storage.local.set({
        'my-data': {
            firstName: 'Liz',
            lastName: 'Lemon'
        }
    });

    const storageData = await chrome.storage.local.get('my-data');
    console.log(storageData);

    /*
    returns...
    {
        'my-data': {
            firstName: 'Liz',
            lastName: 'Lemon'
        }
    }
    */ 
Enter fullscreen mode Exit fullscreen mode

I found this kind of counter-intuitive since you’re explicitly requesting my-data; I would’ve expected it to just return the value. 🤷‍♀️ Happy to field info an opinions on this one!

In Conclusion

That’s it! At this point you have both your website sending a request and console.log in the response, and your extension listening, pulling the data from storage, and returning it. Hopefully someone finds this helpful!

Top comments (0)