ValidationError on POST request with csv file in body #7574

This discussion has been migrated from our Github Discussion #7574


bashmentarium127d ago

Whenever I try to create a new Stock content-type entry with a csv file as body of a POST request to the /stocks route results in validation error, in spite of the fact that the file I am trying to send is generic csv file with stock stats.
Screen Shot 2020-08-23 at 21 53 32

P.S. When I send a POST request with an empty body to the same route, a new entry is created in the Stocks collection type, so I know the request is alright and the problem is the correct csv formatting.

My question is: How can I find the correct shape of the csv file so it is validated and accepted by Strapi?

Responses to the discussion on Github


derrickmehaffy126d ago

Collaborator

Hi @bashmentarium currently Strapi only supports application/json or the multipart/form-data body types, so you will want to format this into the multipart/form-data and parse that within your controller.


bashmentarium126d ago

Author

Thank you for taking the time to respond, however when I try to send the same file through multipart/form-data, another error arises:

Screen Shot 2020-08-24 at 17 55 51

P.S. I really really want to apologize for being stupid, but my only excuse would be that I’ve put my hands on Strapi a few weeks ago and I am still learning.


derrickmehaffy126d ago

Collaborator

(You aren’t stupid, everyone is learning something every day ) Yeah that error is coming from the built-in form-data parser as it was built for the upload plugin, you may want to implement your own. A link to that code is here: https://github.com/strapi/strapi/blob/master/packages/strapi-utils/lib/parse-multipart.js

It’s expecting a files for the actual file and some context of the file being sent in data for the moment can you try to just send the file in files and set something like { "okay": true } in the data? It’s ugly but I want to see if that works and you can get access to the content of the file.


bashmentarium126d ago

Author


derrickmehaffy126d ago

Collaborator

Let me do a bit of testing @bashmentarium and I’ll get back to you. Just to clarify, what is your goal once the file is sent in and parsed? (Just so I can try to build a more targeted example)


bashmentarium126d ago

Author

The ultimate goal is to automatically keep the prices of the products in sync with the accounting software. So, the accountant schedules a daily csv report that has the products’ id and price and sends it over to the Strapi server, where a controller parses the csv into an array of objects, loops through it and does await strapi.services.product.update(productObject) for every entry.


derrickmehaffy126d ago

Collaborator

Great thank you @bashmentarium I’ll get back to you with an example :slight_smile:


derrickmehaffy123d ago

Collaborator

Hi @bashmentarium sorry for the wait, here is the example I came up with:

Creating a new custom controller (this is a very basic example):

Path: ./api/product/controllers/product.js

‘use strict’; const { parseMultipartData } = require(‘strapi-utils’); const csv = require(‘csvtojson’); const fs = require(‘fs’); module.exports = { async updatePrices(ctx) { if (ctx.is(‘multipart’)) { // parse the multipart data, you will need to send file as files.files key // and some random json object in the data key const { files } = parseMultipartData(ctx); // convert the local tmp file to a buffer const buffer = fs.readFileSync(files.files.path); // stream that file buffer into the conversion function to get usable json let json = await csv().fromString(buffer.toString()) // finally loop through the json and fire the Strapi update queries await json.map(async product => { await strapi.query(‘product’).update({ id: product.id }, { price: product.price }) }) return { updated: true } } else { return ctx.badRequest(‘request format incorrect’); } }, };

Then make sure you add an entry to the routes for it:

Path: ./api/product/config/routes.json

… { “method”: “PUT”, “path”: “/products/updatePrices”, “handler”: “product.updatePrices”, “config”: { “policies”: [] } }, { “method”: “PUT”, “path”: “/products/:id”, “handler”: “product.update”, “config”: { “policies”: [] } }, …

From here I just send the csv and some random JSON (due to how that parseMultipartData function works, it’s used for file upload)

image

The content of that CSV file is very simple just including the id and the new price:

id,price
1,69.99
2,100
3,120.78
4,299.89

And calling that route with the file will loop through it and update the prices within your Strapi application


bashmentarium122d ago

Author

Man, @derrickmehaffy, you absolutely nailed it! I really appreciate you wasting time figuring this out, however, even after writing the code you provided, and making sure the products have the appropriate fields that need to be updated, and respected the .csv file structure, when I send the request through postman I get error: invalid input syntax for type integer: "updatePrices" in the strapi terminal console. How is that possible? Shouldn’t the updatePrices controller method be of a function type? I’ve deployed the app into production with heroku and they forced me to use a postgres database instead of strapi default sqlite. Could this be the issue?


derrickmehaffy122d ago

Collaborator

That shouldn’t be a problem (PG that is), I’m not sure on Heroku due to how their filesystem works. But that error doesn’t make sense to me either.


bashmentarium116d ago

Author

It worked only after I’ve deleted this route

{
  "method": "PUT",
  "path": "/products/:id",
  "handler": "product.update",
  "config": {
    "policies": []
  }
}

from the product’s routes.json file. Confusing


derrickmehaffy116d ago

Collaborator

Ah you need to place the custom route above that one, else Strapi thinks the updatePrices is an :id.


bashmentarium116d ago

Author

Thanks a lot for your help, Derrick. I would never ever figure out that thing with the files.files field and this one with the above route without you.


derrickmehaffy116d ago

Collaborator

(Hey even I needed some help there so wasn’t all just me ) But no problem, glad we found you a solution and I was able to help a few others that had similar requests