Isomorphic form submission

The problem

I assume here the website uses heavily client script to control form submissions and give a smoother user experience.
I assume also the website must be somewhat usable even without client script available.

Doing so is somewhat easy in the particular case… one just uses two routes (one for xhr api, one for html) and the html route knows enough about the application to be able to redirect where appropriate.
If the client script code do more than just one xhr call (side effects), then the html route just do that call on behalf of the user without client side doing the submission.

now you have split the same knowledge about the application in two
places: client script and server code. This is bad because it’s difficult to maintain.

replay the form submission on server-hosted dom.
here’s a detailled description, it will scare some of you :)

Client script mode

Where application-bound code is defined

<form action=”/api/auth”>
 <input name=”login” />
 <input name=”password” />
$(document).on(‘submit’, function(e) {
 var form =;
 if (!validateForm(form)) {
 // some inputs were badly formatted, do not submit
 return history.pushState(null, null, “?status=400”);
 fetch(form.action, {
 method: ‘post’,
 body: new FormData(form)
 }).then(function(res) {
 // handle stuff and do other things
 // like another request, hide/show html, change location state (using history or direct assign)

Client noscript mode

Here everything is automatic — no application-code added once proper modules are installed.
Server receives from html form submission:
POST /api/auth + formData

  1. form handling server middleware catch the fact it came from a real form submission, not from XHR
  2. referer page is opened with express-dom or jsdom hosting a “server document” with “server code” doing the following
  3. the <form> that has the action /api/auth is filled with formData and submitted,
     triggering the “client script mode” handler described above (a.k.a. the application code)
  4. end of submission, plugin waits for these different outcomes:
    * all xhr requests are done, document.location is the same
    * history is changed (pushState or replaceState)
     * document.location is changed directly
  5. server code acquires new cookies that were set between opening of page and end of submission, or if possible only those cookies that were obtained by in xhr responses.
  6. finally the server sends the cookies using Set-Cookie header, according to the outcomes:
    * 200 and the serialized html content of the sdocument
    * same as first case — ideally it would be preferable to do a 303-redirect and then reply to it with the content of the current sdocument, not loading a new sdocument, but it poses more problems for very little advantage.
    * 303 (or 302 for http 1.0 compat) with Location the new sdocument location