{"id":13433919,"url":"https://github.com/dragonman225/trigger-webhook-from-notion","last_synced_at":"2025-03-21T17:32:59.184Z","repository":{"id":65846714,"uuid":"202181824","full_name":"dragonman225/trigger-webhook-from-notion","owner":"dragonman225","description":"Add a button that can trigger a POST webhook in Notion.so's table.","archived":false,"fork":false,"pushed_at":"2022-05-31T21:15:21.000Z","size":940,"stargazers_count":73,"open_issues_count":1,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-10-13T12:05:21.443Z","etag":null,"topics":["notablog","notion"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dragonman225.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-08-13T16:18:04.000Z","updated_at":"2024-07-31T04:34:26.000Z","dependencies_parsed_at":"2023-02-13T19:55:21.197Z","dependency_job_id":null,"html_url":"https://github.com/dragonman225/trigger-webhook-from-notion","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dragonman225%2Ftrigger-webhook-from-notion","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dragonman225%2Ftrigger-webhook-from-notion/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dragonman225%2Ftrigger-webhook-from-notion/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dragonman225%2Ftrigger-webhook-from-notion/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dragonman225","download_url":"https://codeload.github.com/dragonman225/trigger-webhook-from-notion/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221817419,"owners_count":16885531,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["notablog","notion"],"created_at":"2024-07-31T02:01:40.371Z","updated_at":"2024-10-28T10:32:57.479Z","avatar_url":"https://github.com/dragonman225.png","language":"JavaScript","funding_links":[],"categories":["JavaScript","HarmonyOS"],"sub_categories":["Windows Manager"],"readme":"# Trigger Webhook from Notion\n\nTrigger a webhook that activates on a POST request directly from a [Notion table](https://www.notion.so/b6fcf809ca5047b89f423948dce013a0?v=03ddc4d6130a47f8b68e74c9d0061de2).\n\nBelow is a demo where I can just click a button on a Notion table and trigger [Netlify](https://www.netlify.com)'s build webhook to update my blog built with [Notablog](https://github.com/dragonman225/notablog).\n\n![demo-gif](assets/demo.gif)\n\n## How to Use\n\nThis is a **proof-of-concept**, so it requires a few changes in code to adapt to your need. They are simple, and I'll explain as clear as possible.\n\n1. Open `src/extension.js`, replace the dummy `buildHookUrl` with your webhook URL.\n  \n   Any webhook should work, as long as the webhook can be triggered by an empty `POST` request (for example, Netlify's).\n   \n   ```javascript\n   const buildHookUrl = 'https://Replace.this.string.with.your.webhook.URL'\n   ```\n\n2. (Optional) If you want the trigger button appear only on one table, open `manifest.json`, in `content_scripts.matches`, change `https://www.notion.so/*` to a table's URL.\n  \n   If you skip this step, you will see the trigger button **on every table**.\n   \n   ```javascript\n   \"content_scripts\": [\n     {\n       \"matches\": [\n         \"https://www.notion.so/*\" // Change this\n       ],\n       // ...\n     }\n   ]\n   ```\n\n3. Load this extension into your browser. Since this is an unpacked extension, it needs to be loaded in developer mode or debug mode.\n  \n   For **Google Chrome** or **Chromium-based**, follow the tutorial: https://developer.chrome.com/extensions/getstarted.\n   \n   For **Firefox**, follow the tutorial: https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Your_first_WebExtension#Installing.\n\n4. Open a table in Notion to test it out!\n\n## How it works\n\n### Motivation\n\nIn my case, I want to trigger Netlify's webhook to rebuild my static blog when I update posts on Notion with a single click. The webhook needs to be triggered by an empty `POST` request, but I couldn't find a block in Notion that can do a `POST` request. In addition, I use a full-page table to manage my posts, so there's actually no way to insert other type of blocks unless I change the [Notablog](https://github.com/dragonman225/notablog)'s logic.\n\n### The Extension\n\n#### Prepare a trigger button\n\nI think of HTML's `\u003cform\u003e`, where I can specify the HTTP method and in my case I just need to `POST` with an empty form. This is attractive to me because it doesn't need AJAX, which means easier to implement !\n\nSo, I write the following\n\n```javascript\n/** Prepare a button */\nconst button = document.createElement('DIV')\n/** Give the button an id so we can check if it exists later */\nbutton.setAttribute('id', BUTTON_ID)\nbutton.setAttribute('style', 'margin-left: 10px; display: inline-flex')\n/**\n * Use a dummy iframe to prevent redirect\n * @see https://stackoverflow.com/a/28060195\n */\nbutton.innerHTML = `\\\n\u003ciframe width=\"0\" height=\"0\" border=\"0\" name=\"dummyframe\" id=\"dummyframe\" style=\"display: none;\"\u003e\u003c/iframe\u003e\n\u003cform action=\"${buildHookUrl}\" method=\"post\" target=\"dummyframe\"\u003e\n  \u003cbutton style=\"font-size: 14px; color: white; height: 24px; background: rgb(46, 170, 220); border: none; border-radius: 3px;\"\u003eTrigger Site Update\u003c/button\u003e\n\u003c/form\u003e\n`\n```\n\nCreate a `\u003cdiv\u003e`, inside the `\u003cdiv\u003e` we have a `\u003cform\u003e`, then we have a `\u003cbutton\u003e` in the `\u003cform\u003e`. The `\u003cform\u003e` has `action` attribute set to the `buildHookUrl`, which is the webhook URL, and `method` attribute set to `post`.\n\nThis translates to: When we click the button, an empty form will be submit to the URL in `action` with `post` method. This is exactly what I want.\n\n---\n\nWait! What's the `\u003ciframe\u003e` doing there?\n\nActually, the script works without the `\u003ciframe\u003e`, but in that case after we click the button, we will be redirected to the webhook URL. This behavior is a bit annoying, so I find a way to prevent it, see https://stackoverflow.com/a/28060195.\n\nThe `\u003ciframe\u003e` is not rendered, and the I set `target` attribute of `\u003cform\u003e` to the `\u003ciframe\u003e`'s `id`, so the redirect happens in the `\u003ciframe\u003e`, without effecting the main page.\n\n#### Inject the button to the page\n\nBy default, the browser loads content scripts of an extension after a page is **loaded**. The \"**loaded**\" means HTML is loaded. This is OK for static web pages, but since Notion is an React app, the parent element we want to inject to **may not exist** when HTML is loaded, so we need additional techniques to handle it.\n\nIn the past, I used to use `DOMSubtreeModified` event to trigger my injection logic ([like this](https://github.com/dragonman225/AlbumArtTool/blob/dcac7a5e58838e80a279f710f7ac8e89da34c7b5/src/extension.js#L186)), but now this event seems to be completely removed from Firefox 68, so I have to find something new, which is the [`MutationObserver`](https://developer.mozilla.org/zh-TW/docs/Web/API/MutationObserver). \n\n```javascript\n/**\n * Use MutationObserver API, DOMSubtreeModified event is deprecated.\n * @see https://developer.mozilla.org/zh-TW/docs/Web/API/MutationObserver\n */\nconst observer = new MutationObserver(function (mutations) {\n  /** Try to hook up the button if not exist */\n  let toolbar = document.querySelector('div.notion-scroller:nth-child(2) \u003e div:nth-child(2) \u003e div:nth-child(1) \u003e div:nth-child(1) \u003e div:nth-child(2)')\n  let btn = document.getElementById(BUTTON_ID)\n  /** btn doesn't exists \u0026\u0026 toolbar exists */\n  if (!btn \u0026\u0026 toolbar) toolbar.appendChild(button)\n});\nconst observerConfig = {\n  attributes: true,\n  childList: true,\n  characterData: true,\n  subtree: true\n}\nobserver.observe(document, observerConfig)\n```\n\nThe `MutationObserver` behaves the same as `DOMSubtreeModified` event, if I set `subtree: true` in the config object.\n\nNote that here I target `document` in `observe()`, which seems to be inefficient. But since Notion's React app is bundled as an [IIFE](https://developer.mozilla.org/en-US/docs/Glossary/IIFE), which does not expose any variable that can be accessed from outside bundle, also we don't know how does the app execute. So listening to all mutations in `document` makes sure we don't lose any chance to detect if the page is ready for injection.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdragonman225%2Ftrigger-webhook-from-notion","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdragonman225%2Ftrigger-webhook-from-notion","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdragonman225%2Ftrigger-webhook-from-notion/lists"}