KeystoneJS CloudinaryImage upload via API - node.js

There seems to be lack of documentation on this topic. I'm trying to upload an image and set it to avatar: { type: Types.CloudinaryImage } in my Keystone model.
I'm posting content as multipart form data with the following structure: avatar: <raw_data>. Here is how I handle this in my API:
exports.upload_avatar = function(req, res) {
if (!req.files.avatar) {
console.info('Request body missing');
return res.status(400).json({ message: 'Request body missing', code: 20 });
}
req.current_user.avatar = req.files.avatar;
req.current_user.save();
}
where current_user is a mongoose model. What I find confusing is how to set my CloudinaryImage type field to the data I receive in the API.

So, rather than just setting the avatar to the raw data (which would work fine for e.g. a string field), you'll need to go through the update handler, which calls to the {path}_upload special path in cloudinary image.
You should then be able to do avatar.getUpdateHandler, perhaps following this example.

I would like to share what worked for me. The process is kind of strange but by adding in this code, all of the model validation works just fine and cloudinary uploads are set.
post(req, res, next) {
const newBundle = new Bundle(); //A mongoose model
newBundle.getUpdateHandler(req).process(req.body, (err) => {
if (err) {
return res.status(500).json({
error: err.message,
});
}
return res.json(newBundle);
});
}
When posting to the endpoint, all you need to do is make sure you set your file fields to be {databaseFieldName}_upload.

Ok after some digging through the source code, I figured out a way to do that:
exports.upload_avatar = function(req, res) {
req.current_user.getUpdateHandler(req).process(req.files, {fields: 'avatar'}, function(err) {
if (err) {
return res.status(500).json({ message: err.message || '', code: 10 });
}
res.send('');
});
}
I had the following gotchas:
use getUpdateHandler to update CloudinaryImage field.
use "magic" naming for multipart form data fields you POST to your API: {field_name}_upload, which in my case would be avatar_upload.
process req.files, which is a dictionary with your field names as keys and your file data as values. req.body is empty due to some post-processing with multer.
invoke update handler on your keystone model (you need to fetch it with find first) rather than on a specific field. Then specify {fields: <>} to limit its scope, otherwise you could have some issues like validation errors trying to update the whole object.

Related

Express is not reading the request body

I am new in node and express. So, I was learning how to do post request. When I learned, it was alright and it was creating another document in mangodb database. Today when I tried to do the same, this problem occurred.
I tried different things like removing required part in schema. But it seems express is failing to read req.body since when I send the request without any required fields in schema, it happily accepted and filled up default fields. I don't really understand what is happening. I was parallelly doing another project which is not giving this error.
PS. I am using mongoose#5.
Code to create tour:
exports.createTour = async (req, res) => {
try {
const newTour = await Tour.create(req.body);
res.status(201).json({
status: 'success',
data: {
tour: newTour,
},
});
} catch (err) {
res.status(404).json({
status: 'fail',
message: err.message,
});
}
};
If the content-type for your POST is JSON, then you need this:
app.use(express.json())
as middleware before your POST request handler. That will read the JSON body of the request, parse it and put it in req.body.

Node JS Application not taking Values from postman

I wrote two node js applications, they are fetching data properly but they are not taking post values 1st applications is this TinderClone this is just an api with no frontend i am posting data from postman and it is returning auto generated id but not the data i am posting,
Other application i cloned from github, it has proper frontend with working CRUD, but when i tried to post values from postman it wont take any values it will just add record in database with null values, so is there anything wrong im doing on postman? cause it is still working if i post data with the form on its frontend the application url is MernCRUD
Postman Screenshots:
posting data,
fetching data
Code:
//Cards Schema (Cards.js)
import mongoose from 'mongoose'
const cardSchema = mongoose.Schema({
name: String,
imgUrl: String
})
export default mongoose.model('cards', cardSchema)
//Posting Data (Server.js)
app.post('/tinder/cards', (req, res) => {
const dbCard = req.body;
Cards.create(dbCard, (err, data) => {
if(err){
res.status(500).send(err)
}
else{
res.status(201).send(data)
}
})
})
//Fetching Data
app.get('/tinder/cards', (req, res) => {
Cards.find((err, data) => {
if(err){
res.status(500).send(error);
}
else{
res.status(200).send(data);
}
});
});
in postman you don't have to add content type manually . You should select json from the drop down and it will add content type by default. **
Now you have text in your raw dropdown change to json .
**

Upload either a document or image together with other form fields node express

Am developing an API for taking input information like title, description, and image or document. I have tested the API is working when I don't add the image/document part. How can I add this field to insert an image/document to the mongo DB? I can actually add info without the image/docs upload, How do I go about it?
Upload info controller
exports.upload = (req, res) =>{
const { title, category, duration, durationSys, description } = req.body
Project.findOne({ title }).exec((err, project) => {
if (project) {
return res.status(400).json({
error: 'Choose a unique title for your Project'
});
}
});
let newProject = new Project({ title, category, duration, durationSys, description});
newProject.save((err, success) => {
if (err) {
console.log('ADDING PROJECT ERROR', err);
return res.status(400).json({
error: err
});
}
res.json({
message: 'Added successfully, check your projects'
});
});
};
If you want to store the image in the MongoDB you can refer to this thread.
Store images in a MongoDB database
Personally, I would recommend uploading the image/document to a server (preferably S3 Bucket) and just storing the link to the image/document in the Database.
Read more here regarding why it's better to not store images/document on Database rather just storing a link to the image/document.

Node Express: handing different params to a URL

I have an Express app, and ran into a problem that I should know how to solve.
I have this URL that I am trying to GET
http://localhost:3000/users/546c2b15a340bb881f853fa6/teams/newTeam
however, I get this error:
CastError: Cast to ObjectId failed for value "newTeam" at path "_id"
the easy solution would be change my app from this:
http://localhost:3000/users/546c2b15a340bb881f853fa6/teams/newTeam
into this:
http://localhost:3000/users/546c2b15a340bb881f853fa6/newTeam
however, I should probably figure out how to parse different params in URLs..any other way good way to fix it?
I believe my app is trying to take "/newTeam" and turn into into an MongoDB _id in this method and that's where things are going wrong:
app.param('team_id', function(req, res, next, team_id) {
var userTeam = TeamModel.getNewTeam(user_db);
userTeam.findById(team_id, function(err, team) {
if (err) {
return next(err);
}
if (!team) {
return new Error("no team matched");
}
req.team = team;
next();
});
});
You can create routes like this in the following order:
app.get("/users/:userId/teams/newTeam", routeHandler1);
app.get("/users/:userId/teams/:team_id", routeHandler2);
Now, app.param("team_id", handler) will only be called when team_id doesn't contain the value "newTeam".

Node.js: req.params vs req.body

I've been cobbling together code from a few different tutorials to build a basic todo app with the MEAN stack, using node, express, angular, and mongodb. One tutorial covered creating an api for GET, POST, and DELETE actions, but neglected the POST. So, I took it as a challenge to write a function that will update an existing todo. While I got the function working, I encountered an error involving req.params that I didn't understand.
Relevant Code:
Node:
In app.js
app.put('/api/todos/:_id', ngRoutes.update);
which leads to:
exports.update = function(req, res){
var user_id = req.cookies ?
req.cookies.user_id : undefined;
Todo.findByIdAndUpdate(req.params._id,
{ $set: {
updated_at : Date.now(),
content : req.body.formText
}}, function (err, todo) {
if (err)
res.send(err)
Todo.find({ user_id : user_id}, function(err, todos) {
if (err) res.send(err);
res.json(todos);
});
});
};
Angular:
$scope.update = function(id) {
$http.put('/api/todos/' + id, this.todo)
.success(function(data) {
console.log(data);
$scope.todos = data;
})
.error(function(data) {
console.log('Error: ' + data);
});
};
Jade/HTML:
form(ng-submit="update(todo._id)")
input.update-form(ng-show="todo.updating" type="text", name="content", ng-model="todo.formText" placeholder="{{todo.content}}")
This function works fine. It updates the todo in question, and returns the entire list to be reloaded onto the page with the updated value.
However, if in the node code, I change
content : req.body.formText
to
content : req.params.formText
I get the following error as my HTTP response:
Object {
message: "Cast to string failed for value "undefined" at path "content"",
name: "CastError",
type: "string",
path: "content" }
Even while, elsewhere in the function,
req.params._id
works fine to retrieve the todo's '_id' property and use it to find the appropriate document in the database. Furthermore, when viewing the request in Firefox's developer tools, the todo object appears in JSON format under the "Params" tab.
Why does this happen? What is the difference between using req.params vs req.body, and why does the second work and the first not?
req.params is for the route parameters, not your form data.
The only param you have in that route is _id:
app.put('/api/todos/:_id', ...)
From the docs:
req.params
This property is an object containing properties mapped to
the named route “parameters”. For example, if you have the route
/user/:name, then the “name” property is available as req.params.name.
This object defaults to {}.
source: http://expressjs.com/en/4x/api.html#req.params
req.body
Contains key-value pairs of data submitted in the request
body. By default, it is undefined, and is populated when you use
body-parsing middleware such as body-parser and multer.
source: http://expressjs.com/en/4x/api.html#req.body
req.params is the part you send in the request url parameter or the header part of requests.
req.params example in postman
In example above req.params is the data we are sending in postman after ninjas in the
url.
route.delete('/ninjas/:id',function(req,res,next)
{
Ninja.findByIdAndRemove({_id:req.params.id}).then(function(ninja)
{
console.log(ninja.toString());
res.send(ninja);
})
.catch(next);
});
req.body is the part you send in body part of requests
req.body example in postman
req.body is the JSON data we are sending in postman so we can access it in the post request body part.
route.post('/ninjas',function(req,res,next)
{
Ninja.create(req.body).then(function(ninja)
{
console.log("POST"+req.body);
res.send(ninja);
})
.catch(next);
});

Resources