FetchIt
fetchit
About
FetchIt submits the forms of a MODX site with the Fetch API: no page reload, no jQuery or other libraries. The forms are processed by FormIt, with all its hooks and validation, or by a snippet of your own that returns `success`, `message` and `data`. One package installs on MODX 2.8 and on MODX 3.
**Your own markup.** All it takes is a form chunk with attributes: `data-error` for the errors of fields, `data-success` and `data-validation-error` for the messages of the form. The classes of invalid fields are system settings, and chunks go through pdoTools, with Fenom and `@FILE`, on both versions of MODX. A ready example comes with the package.
**Spam protection, on by default.** Every form gets a signed single-use token that needs no session, a minimum fill time, a hidden trap field with a random name per installation, and a limit of submissions per form and address, which also works behind trusted proxies and CDNs. The checks run before FormIt and before your snippet, for submissions through FetchIt and for forms sent without JavaScript alike, and the service fields are removed from `$_POST` before FormIt reads them. When the token of a page is stale — a page from a cache, a page open for long, a new key — the script sends the form once more with a new token by itself.
**Proof of work and captcha**, both off by default, for when spam gets through the main checks. The proof of work asks the browser for a SHA-256 with a given number of zero bits, solved while the visitor fills the form, so every submission costs a bot processor time. The captcha is Cloudflare Turnstile, Google reCAPTCHA v3 or Yandex SmartCaptcha: FetchIt adds the provider's script, gets its answer before sending and checks it on the server last, so bots without a token never reach the provider.
**Rules of your own.** A plugin on the `OnFetchItBeforeProcess` event gets the fields sent and the properties of the form and refuses a submission with a message for the visitor.
**Without JavaScript** a form with FormIt is sent the usual way, and the messages and the values entered come back through its placeholders — the protection included. Only the proof of work and the captcha need JavaScript.
**Notifications.** The answers of the server can be shown as notifications in a corner of the page. They are FetchIt's own: no library is loaded. Screen readers hear them from two live regions, a notification stays while hovered or focused, and its colours are CSS variables. A site that shows messages its own way sets `FetchIt.Message` and uses any library.
**Events and types.** `fetchit:before`, `fetchit:after`, `fetchit:success`, `fetchit:error` and `fetchit:reset` are dispatched on `document` with the form, its data and the answer of the server, and any of them can be cancelled. TypeScript types ship next to the script, and the script is type-checked against them.
**No dependencies.** The native Fetch API and `FormData`, files included. The minified script is 19 KB, about 7 KB in gzip, loaded with `defer`. A page can hold as many forms as you like, each with its own key and handler.
**Upgrading.** FetchIt 4 replaces the 1.x line for MODX 2 and the 3.x line for MODX 3 and installs over either of them, keeping the system settings and chunks; the snippet calls need no changes. The API of both lines works on both versions of MODX. What may affect your code is listed in the README, under Upgrading to FetchIt 4.
At a glance
MODX 3 compatibility unknown. Based on our testing and community reports where available.
- Developer gulomov
- Latest release September 29, 2026 (1 month ago)
- First seen in SiteDash September 25, 2026 (1 month ago)
- License GPLv2
- MODX support 2.8 – 100.0
| Changelog | |||
|---|---|---|---|
| MODX.com |
4.0.0-pl |
September 29, 2026
1 month ago |
|
|
FetchIt submits MODX forms with the Fetch API: no page reload, no jQuery or other libraries. The forms are processed by FormIt, with all its hooks and validation, or by a snippet of your own. One package for MODX 2.8 and MODX 3.
Every form is protected from spam out of the box: a signed single-use token, a minimum fill time, a hidden trap field and a limit per address, both for FetchIt submissions and for forms sent without JavaScript. Optionally: a proof of work in the browser and a captcha (Cloudflare Turnstile, Google reCAPTCHA v3, Yandex SmartCaptcha). Plugins can add their own rules through the OnFetchItBeforeProcess event. - Your own markup: a form chunk with data-error, data-success and data-validation-error attributes. - Built-in notifications, or your own through FetchIt.Message. - JavaScript events: fetchit:before, fetchit:after, fetchit:success, fetchit:error, fetchit:reset. - TypeScript types next to the script. - Fenom and @FILE chunks through pdoTools. FetchIt 4 installs over FetchIt 1.x and 3.x; settings and chunks are kept. Documentation: [https://github.com/GulomovCreative/FetchIt#readme](https://github.com/GulomovCreative/FetchIt#readme) What's new in 4.0.0 - One package for MODX 2.8 and MODX 3, installs over FetchIt 1.x and 3.x. - Spam protection on by default: signed single-use token, minimum fill time, trap field, limit per address, also for forms sent without JavaScript. - Optional proof of work and captchas: Cloudflare Turnstile, Google reCAPTCHA v3, Yandex SmartCaptcha. - OnFetchItBeforeProcess event for rules of your own. - Built-in notifications instead of Notyf, accessible, styled with CSS variables. - TypeScript types for the script and its events. - Many fixes: see [https://github.com/GulomovCreative/FetchIt/releases/tag/v4.0.0](https://github.com/GulomovCreative/FetchIt/releases/tag/v4.0.0) |
|||
| modstore.pro |
4.0.0-pl |
September 25, 2026
1 month ago |
|
|
# Changelog for FetchIt<br /><br />
All notable changes to this project are documented in this file. The format<br /> is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the<br /> project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).<br /><br /> FetchIt 1.x was the package for MODX 2 and FetchIt 3.x the package for MODX 3.<br /> FetchIt 4 replaces both with one package and installs over either of them.<br /><br /> ## [4.0.0] - 2026-09-25<br /><br /> One package for MODX 2.8 and MODX 3, with spam protection on by default. It<br /> installs over FetchIt 1.x on MODX 2 and FetchIt 3.x on MODX 3; system settings<br /> and chunks are kept. What may affect your code is marked **Breaking** below and<br /> explained in the README, [Upgrading to FetchIt 4](https://github.com/GulomovCreative/FetchIt#upgrading-to-fetchit-4).<br /><br /> ### Added<br /><br /> - Spam protection for every form, on by default, both for FetchIt submissions and for forms sent without JavaScript. `fetchit.protection` turns it all off.<br /> - A signed single-use token in every form (`fetchit_token`), with no session needed. Only a request with a well-signed token of the form gets the next one, in the `X-FetchIt-Token` header. When the token of a page is stale (a cached page, a page open for long, a new key), the script sends the form once more with the new token by itself.<br /> - A minimum fill time (`fetchit.protection.min_time`, 3 seconds), counted from the first output of the page, resends included.<br /> - A hidden trap field with a random name per installation. A bot that fills it gets a fake success, and the form is not processed.<br /> - A limit of submissions per form and client address (`fetchit.protection.rate_limit`, `fetchit.protection.rate_window`), also behind trusted proxies and CDNs (`fetchit.protection.proxies`, `fetchit.protection.ip_header`).<br /> - Used tokens are marked atomically in `core/cache/fetchit/tokens/`, which "Clear cache" leaves alone. When a mark cannot be written, the form is refused and the cause is logged.<br /> - An optional proof of work (`fetchit.protection.pow`, in bits, off by default): the browser finds `n` such that SHA-256 of `token:n` starts with that many zero bits, starting as soon as the visitor enters the form. When a page from a cache asks for less than the server, the refusal carries `X-FetchIt-Pow`, and the script solves and sends once more.<br /> - Optional captchas: Cloudflare Turnstile, Google reCAPTCHA v3 and Yandex SmartCaptcha (`fetchit.captcha`, `fetchit.captcha.site_key`, `fetchit.captcha.secret_key`, `fetchit.captcha.min_score`). FetchIt adds the provider's script, gets its answer before each submission and checks it on the server last. A provider that cannot be reached refuses the form with its own message, `fetchit_err_captcha_unavailable`; a captcha with no answer to send stops the submission with `fetchit_err_captcha_client`.<br /> - A log of refusals and problems (`fetchit.protection.log`), and a signing key generated at install (`fetchit.protection.secret`). Mistakes in the settings of the captcha and the proof of work are logged.<br /> - The `OnFetchItBeforeProcess` event: a plugin gets `$action`, `$fields`, `$properties` and `$FetchIt`, and refuses a submission with `$modx->event->output()`. It fires with the protection off too.<br /> - MODX 3 support in the same package, built on MODX 2.8: it installs fresh on MODX 2.8 and MODX 3.<br /> - The API of FetchIt 3.x on both versions: the `FetchIt\FetchIt` class, `saveActionProperties()` and `getActionProperties()`, and `$modx->services->get('FetchIt')` on MODX 3.<br /> - `FetchIt::service()`, one shared instance on MODX 2 and MODX 3; the 1.x and 3.x ways to get FetchIt return the same instance.<br /> - `FetchIt::pdoTools()`, which finds pdoTools 2 on MODX 2 and pdoTools 3 on MODX 3, for Fenom and `@FILE` chunks.<br /> - `FetchIt::prepareForm()`, which gives the form tags of a chunk the POST method and the key of the form.<br /> - TypeScript types, `assets/components/fetchit/js/fetchit.d.ts`: `FetchIt`, the config, `FetchIt.Message`, the instances of forms and the `fetchit:*` events with their `detail`. The script is type-checked against them.<br /> - `FetchIt.createNotifier()`: the built-in notifications for a site that sets `FetchIt.Message` itself. `duration: 0` keeps a notification until it is closed.<br /> - `detail.error` in `fetchit:error` when a request fails, and the `fetchit_err_request` lexicon entry the visitor sees then.<br /> - MODX log entries when the script cannot be added to a page with a form: no ``, or a `fetchit.frontend.js` that is not a `.js` file.<br /> - An upgrade with `fetchit.frontend.default.notifier` on says in its log that Notyf is no longer loaded, and warns when `fetchit.frontend.js` points to a script of the site.<br /><br /> ### Changed<br /><br /> - **Breaking:** forms get the service fields of the protection right after the form tag; they are removed from `$_POST` before FormIt reads them, so they never reach e-mails. A script of the site used instead of the bundled one must send `fetchit_token` and take the next token from the `X-FetchIt-Token` header of every answer.<br /> - **Breaking:** the notifications of `fetchit.frontend.default.notifier` are FetchIt's own instead of Notyf, and `window.Notyf` is no longer loaded. Styles for `.notyf__toast` and scripts that call `new Notyf()` need to change. The new notifications are accessible (live regions for screen readers, a labelled close button, focus handling), stay while hovered or focused, and take CSS variables for their colours, by default the pastel green and red of Tailwind CSS 4. Under a Content-Security-Policy they take the nonce of the FetchIt script.<br /> - **Breaking:** `fetchit:error` also fires when a request fails, with `detail.response` set to `null`.<br /> - **Breaking:** the processing snippet gets only the form sent in `fields`: `$_POST`, and `$_FILES` for FetchIt submissions. It used to get `$_REQUEST`, with GET values and, depending on `request_order`, cookies.<br /> - With the protection on, the snippet still runs FormIt on every page view, for its preHooks, but only a POST with a token of its form counts as a submission.<br /> - The script is built with ES2019 syntax, so it runs in every browser of the project's browserslist.<br /> - The hooks of `FetchIt.Message` and the events get a string `message` and an object `data` even when the processing snippet left them out. An exception in a hook is logged with its name and no longer keeps the answer from the form.<br /> - `fetchit:success` can be cancelled: `event.preventDefault()` keeps the fields filled.<br /> - A second submission while a request is running is ignored.<br /> - On MODX 3 the plugin and `action.php` take FetchIt from the service container instead of the deprecated `getService()`.<br /> - The inline `FetchIt.create()` call checks that the class of `fetchit.frontend.js.classname` is loaded, so a page without the script falls back to a normal submission instead of throwing. `FetchIt.create()` warns in the console when no form matches.<br /> - `method` and `data-fetchit` come last among the attributes of the form tag, and the `?v=` of the script URL keeps its query string and fragment.<br /><br /> ### Removed<br /><br /> - `lib/notyf.min.js` and `lib/notyf.min.css` are no longer shipped. An upgrade leaves the old copies in place.<br /><br /> ### Fixed<br /><br /> - A form sent without JavaScript never showed its success or error message when the chunk used output filters on the FormIt placeholders, like the example chunk: the chunk was rendered before FormIt ran.<br /> - A form tag with its own `data-fetchit` came out broken, look-alike tags such as `` were changed, and so were the `formmethod` and `data-fetchit` of elements inside the form. Form tags are now parsed one by one, quoted values included.<br /> - The script was not added on sites without anonymous sessions, and when `` was written in capitals or had attributes.<br /> - FormIt was not installed together with FetchIt on MODX 3. The installer now also reports each way the download or install of FormIt can fail.<br /> - Two identical snippet calls on one page bound the form twice, and it was sent twice.<br /> - Fields disabled in the markup became enabled after a submission.<br /> - A network error, or an answer that is not FetchIt's (a PHP error page, HTML after a redirect, JSON from a firewall), was only logged to the console. The visitor now sees `fetchit_err_request`.<br /> - A FormIt error placeholder holding only ` ` marked its field as invalid.<br /> - Field names with quotes broke the error selectors.<br /> - A snippet property set that does not exist made `action.php` fail on PHP 8. The snippet now runs without it, and the MODX log names the missing set.<br /> - Without the `fetchit.frontend.js` setting the plugin linked the missing `js/default.js`.<br /> - PHP 8.1 deprecation notices when the invalid-class settings are missing.<br /><br /> ### Security<br /><br /> - A POST straight to the URL of a page with a FetchIt form reached FormIt and its hooks, e-mails included, without any check. It now has to carry a valid token of the form.<br /> - FormIt 5.2 and later kept the properties of every FetchIt form, hooks included, for its own `action.php`, which processes a form without FetchIt's checks. FetchIt now turns that AJAX mode off for its forms: it removes the stored properties, the `ajaxToken` placeholder and `formit.js`. FormIt forms of their own on the same page keep their AJAX mode.<br /><br /> ## [3.1.4] - 2026-09-09<br /><br /> ### Fixed<br /><br /> - The success and validation messages of the form were not shown on AJAX submissions. They now go to the `[data-success]` and `[data-validation-error]` elements of the form ([#15]).<br /> - FormIt field errors of only whitespace marked the form as failed ([#14]).<br /> - A PHP 8 warning for the undefined `$_SESSION['fetchit_called']` ([#13]), and for the undefined `$_SESSION['FetchIt']`.<br /> - A GET request to `action.php` was not redirected.<br /> - The `successMessage` of the snippet call was not used in the AJAX answer of FormIt.<br /><br /> ### Changed<br /><br /> - The frontend is built with Rollup 4.<br /><br /> ## [3.1.3] - 2026-07-21<br /><br /> ### Fixed<br /><br /> - The fatal error `Serialization of 'PDO' is not allowed` on MODX 3.2.2 and PHP 8.3 ([#17], [#18]). The properties of a form no longer hold the FetchIt instance: custom snippets get it with `$modx->services->get('FetchIt')`.<br /><br /> ## [3.1.2] - 2023-11-11<br /><br /> ### Added<br /><br /> - The reCAPTCHA v3 error of FormIt is returned as the error of the `recaptcha` field ([#6]).<br /><br /> ### Fixed<br /><br /> - `action.php` could not find `config.core.php` when the document root is not the root of MODX ([#4]).<br /><br /> ## [3.1.1] - 2023-07-14<br /><br /> ### Fixed<br /><br /> - A fatal error on PHP 8 when the script was added to the page.<br /><br /> ## [3.1.0] - 2023-06-12<br /><br /> ### Added<br /><br /> - The `fetchit.frontend.default.notifier` setting: the answers are also shown as Notyf notifications.<br /><br /> ## [3.0.0] - 2023-06-06<br /><br /> ### Added<br /><br /> - The package for MODX 3, based on FetchIt 1.0.1.<br /><br /> ## [1.1.3] - 2026-08-12<br /><br /> ### Fixed<br /><br /> - The fatal error `Serialization of 'PDO' is not allowed` on PHP 8.3 ([#17]). The properties of a form no longer hold the FetchIt instance: custom snippets get it with `$modx->getService('fetchit', 'FetchIt', MODX_CORE_PATH . 'components/fetchit/model/')`.<br /> - The success and validation messages of the form were not shown on AJAX submissions ([#11], [#15]). They now go to the `[data-success]` and `[data-validation-error]` elements of the form; custom chunks need these attributes instead of `[[+fi.success:…]]` conditions.<br /> - FormIt field errors of only whitespace marked the form as failed ([#10], [#14]).<br /> - A PHP 8 warning for the undefined `$_SESSION['fetchit_called']` ([#7], [#9], [#13]), and for the undefined `$_SESSION['FetchIt']`.<br /> - A GET request to `action.php` was not redirected.<br /> - The `successMessage` of the snippet call was not used in the AJAX answer of FormIt ([#5]).<br /><br /> ## [1.1.2] - 2023-11-11<br /><br /> ### Added<br /><br /> - The reCAPTCHA v3 error of FormIt is returned as the error of the `recaptcha` field ([#6]).<br /><br /> ## [1.1.1] - 2023-07-14<br /><br /> ### Fixed<br /><br /> - A fatal error on PHP 8 when the script was added to the page.<br /><br /> ## [1.1.0] - 2023-06-12<br /><br /> ### Added<br /><br /> - The `fetchit.frontend.default.notifier` setting: the answers are also shown as Notyf notifications.<br /><br /> ## [1.0.1] - 2023-03-25<br /><br /> ### Fixed<br /><br /> - The script was missing when the snippet was called from Fenom. A plugin on `OnWebPagePrerender` now adds it to ``.<br /><br /> ## [1.0.0] - 2023-03-20<br /><br /> ### Fixed<br /><br /> - The undefined variable `$hash`.<br /><br /> ## [1.0.0-beta] - 2023-02-23<br /><br /> ### Added<br /><br /> - The first release: forms sent with the Fetch API and processed by FormIt or a snippet of your own.<br /><br /> [4.0.0]: https://github.com/GulomovCreative/FetchIt/compare/v1.1.3...v4.0.0<br /> [3.1.4]: https://github.com/GulomovCreative/FetchIt/compare/a2f6d4d...v3.1.4<br /> [3.1.3]: https://github.com/GulomovCreative/FetchIt/compare/7ac236f...a2f6d4d<br /> [3.1.2]: https://github.com/GulomovCreative/FetchIt/compare/730b50d...7ac236f<br /> [3.1.1]: https://github.com/GulomovCreative/FetchIt/compare/3.1.0-pl...730b50d<br /> [3.1.0]: https://github.com/GulomovCreative/FetchIt/compare/3.0.0-pl...3.1.0-pl<br /> [3.0.0]: https://github.com/GulomovCreative/FetchIt/compare/1.0.1-pl...3.0.0-pl<br /> [1.1.3]: https://github.com/GulomovCreative/FetchIt/compare/c89c7fe...v1.1.3<br /> [1.1.2]: https://github.com/GulomovCreative/FetchIt/compare/1.1.1-pl...c89c7fe<br /> [1.1.1]: https://github.com/GulomovCreative/FetchIt/compare/1.1.0-pl...1.1.1-pl<br /> [1.1.0]: https://github.com/GulomovCreative/FetchIt/compare/1.0.1-pl...1.1.0-pl<br /> [1.0.1]: https://github.com/GulomovCreative/FetchIt/compare/1.0.0-pl...1.0.1-pl<br /> [1.0.0]: https://github.com/GulomovCreative/FetchIt/compare/1.0.0-beta...1.0.0-pl<br /> [1.0.0-beta]: https://github.com/GulomovCreative/FetchIt/releases/tag/1.0.0-beta<br /> [#4]: https://github.com/GulomovCreative/FetchIt/issues/4<br /> [#5]: https://github.com/GulomovCreative/FetchIt/issues/5<br /> [#6]: https://github.com/GulomovCreative/FetchIt/pull/6<br /> [#7]: https://github.com/GulomovCreative/FetchIt/issues/7<br /> [#9]: https://github.com/GulomovCreative/FetchIt/issues/9<br /> [#10]: https://github.com/GulomovCreative/FetchIt/issues/10<br /> [#11]: https://github.com/GulomovCreative/FetchIt/issues/11<br /> [#13]: https://github.com/GulomovCreative/FetchIt/pull/13<br /> [#14]: https://github.com/GulomovCreative/FetchIt/pull/14<br /> [#15]: https://github.com/GulomovCreative/FetchIt/pull/15<br /> [#17]: https://github.com/GulomovCreative/FetchIt/issues/17<br /> [#18]: https://github.com/GulomovCreative/FetchIt/pull/18 |
|||
| MODX.com |
3.1.4-pl |
September 23, 2026
1 month ago |
— |
| modstore.pro |
3.1.4-pl |
September 9, 2026
1 month ago |
— |
| MODX.com |
3.1.2-pl |
November 16, 2023
2 years ago |
— |
| modstore.pro |
3.1.2-pl |
November 11, 2023
2 years ago |
— |
| MODX.com |
3.1.1-pl |
July 18, 2023
3 years ago |
— |
| modstore.pro |
3.1.1-pl |
July 14, 2023
3 years ago |
— |
| modstore.pro |
1.1.3-pl |
August 31, 2026
2 months ago |
— |
| modstore.pro |
1.1.2-pl |
November 11, 2023
2 years ago |
— |
| MODX.com |
1.1.2-pl |
November 16, 2023
2 years ago |
— |
| modstore.pro |
1.1.1-pl |
July 14, 2023
3 years ago |
— |
| MODX.com |
1.0.1-pl |
February 28, 2023
3 years ago |
— |
| MODX.com |
1.0.0-beta |
February 28, 2023
3 years ago |
— |
|
No matching releases. |
|||