AMP forms with Node.js

UPDATE 11/14/19: At some point in the years since I originally posted this, Google made some improvements to the way AMP handles URLs and I’ve updated my headers to keep up. Most of the information below is still applicable but I’ve updated the Access-Control-Allow-Origin and AMP-Access-Control-Allow-Source-Origin settings to:

res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
res.setHeader('AMP-Access-Control-Allow-Source-Origin', req.query.__amp_source_origin);

So keep that in mind while reading the original post…


I’m in the middle of creating Google AMP pages for my fonts and everything was going pretty well until I tried getting the amp-form component working with Node.js. I followed the AMP by Example code, my page passed validation, but whenever I tried to use the form data all I got was an empty req.body.

Finding a solution took more effort than the usual reading through the first few Stack Overflow results, so I thought it might be useful for anyone else out there struggling.

I’d assumed amp-form would send data using the default application/x-www-form-urlencoded and work with body-parser to populate req.body just like that. However, the data is actually sent using multipart/form-data which body-parser doesn’t handle so another module is needed to get it working. They recommend using busboy, multiparty, formidable, or multer.

I’m already using multer on my site so here’s a quick example of how to use it in a post request. This is a really simple, it just takes in a text value and then sends it right back.

var multer = require('multer');
var multipart = multer();

app.post('/update-text', multipart.fields([]), function (req, res) {
    var response = {
        example: req.body.text ? req.body.text : ''
    };

    res.setHeader('Content-type', 'application/json');
    res.setHeader('Access-Control-Allow-Credentials', true);
    res.setHeader('Access-Control-Allow-Origin', '*.ampproject.org');
    res.setHeader('AMP-Access-Control-Allow-Source-Origin', 'http://' + req.headers.host);
    res.setHeader('Access-Control-Expose-Headers', 'AMP-Access-Control-Allow-Source-Origin');
    res.json(response);
});

Note that you also have to update your headers to follow the AMP CORS spec.

Here’s the form to send that text:

<form method="post" action-xhr="/update-text" target="_top">
    <input type="text" name="text" id="text" value="Enter your text." />
    <button type="submit">Send Text</button>

    <div submit-success>
        <template type="amp-mustache">
            Text: {% raw %}{{text}}{% endraw %}
        </template>
    </div>
</form>

Bonus bug that got me: if you’re using the Swig template engine like I am, you have to wrap your amp-mustache variables in {% raw %}{% endraw %}.

Hopefully this’ll be helpful to someone!

Comments