Express body-parser transforms nested objects into flattened key-value pairs

I’m having trouble with data transformation when sending nested objects from my React app to an Express server. When I send a POST request with jQuery, the nested object structure gets flattened by the time it reaches my backend route.

What I’m sending:

updateProduct() {
    let productId = "5bf4459853bfc70bc82e8e91"
    
    $.ajax({
        url: '/api/products/' + productId,
        method: 'post',
        data: {
            'product': {
                title: "sample product",
                dimensions: {
                    width: 200,
                    length: 75
                }
            }
        },
        success: (response) => {console.log(response)}
    });
}

What arrives at the backend:

{ 'product[title]': 'sample product',
  'product[dimensions][width]': '200',
  'product[dimensions][length]': '75' }

My Express setup:

const express = require('express');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');

mongoose.connect('mongodb://localhost/inventory');
mongoose.Promise = global.Promise;

const app = express();
const port = process.env.PORT || 3000;

app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

app.use('/api', require('./routes/api'));

app.use((err, req, res, next) => {
    res.status(422).send({error: err.message});
});

app.listen(port, () => {
    console.log(`Server running on port ${port}`);
});

Route handler:

router.post('/products/:id', (req, res, next) => {
    console.log(req.body);
    res.send("received");
});

Is there a way to preserve the original nested object structure when it gets to my Express route? Should I modify my body-parser configuration or handle this differently on the frontend?

jQuery’s form serialization is causing this. When you use the data property with nested objects, jQuery converts them to URL-encoded format - that’s what body-parser’s urlencoded middleware parses. The flattened structure you’re seeing is how form data normally handles nested objects.

You’ve got two fixes: send actual JSON by setting contentType: 'application/json' and stringify your data, or parse the flattened structure on your backend with something like qs to rebuild the nested object from bracket notation.

I’ve hit this same issue before. For complex data structures, going with JSON is way cleaner.

Interesting issue! Try setting processData: false and contentType: 'application/json' in your jQuery call, then wrap your data with JSON.stringify(). That should preserve the nesting. What’s showing up in your network tab for the actual request?

jQuery serializes nested objects like that by defualt. Just switch to fetch() with proper JSON headers instead of AJAX - way cleaner than messing with processData flags. You could also use the qs library on the backend to parse those flattened keys back into objects, but that’s more work IMO.