title: How to prevent double submitting forms in ExpressJS
body: <p>I have come across the problem that people on my website can double-submit a form. This isn't an issue from the database side, as the records are saved by server-side validation. However, it's important I invalidate any additional requests so that users don't get double charged (as the charge occurs prior to the account being made). I would prefer to drop the request entirely as opposed to redirecting because it will really ruin the UX, whereas dropping the request will just make the process "appear" longer which is a better compromise.</p>
  
  <p>To clear things up, I am also using the post-redirect-get pattern, but it does not solve this problem, which is actually the most critical problem because it is the most common. Implementing a client-side solution will be done for cleanliness of the UI, but I cannot trust the user to not disable javascript or somehow tamper with it.</p>
  
  <p>I have a middleware solution using the library <code>csurf</code> that doesn't quite work.</p>
  
  <pre><code>var protect_route = function(req, res, next){
    if(!req.session.unique_requests){
      req.session.unique_requests = [];
    }
  
    var found = _.find(req.session.unique_requests, function(token){
      return token === req.body._csrf;
    });
  
    if(!found){
     req.session.unique_requests.push(req.body._csrf);
     next();
    } else {
     next('route'); //drop the post, and skip anything else.
    }
  };
  
  var unlock_route = function(req, res, next){
    req.session.unique_requests = _.without(req.session.unique_requests, req.body._csrf);
  };
  </code></pre>
  
  <p>I attach it to my routes like so:</p>
  
  <pre><code>router.post('/create', protect_route, function(req, res){
     // If it makes it in here, the route is protected and the 
     // request wasn't dropped.
  });
  
  router.get('/review', unlock_route, function(req, res){
     // The form will once again be able to be submitted.
  });
  </code></pre>
  
  <p>Unfortunately, even though the middleware gets called, if I mash the create button it will fire off a ton of requests, each will attempt to push the token onto the <code>unique_requests</code>, and the result is it won't block any requests for a few pushes until everything catches up.</p>
  
  <p>What am I doing wrong here, and how can I fix it?</p>
  
  <p><strong>EDIT:</strong> Rate-limiting the POST to /create seems like a possibility, though it doesn't seem like a <em>real</em> solution I suppose. It might work.</p>