I'm on a task to write a simple CRUD program for a users list, following a similar nestjs example. While GET, POST and GET by id works fine, PUT and DELETE does not work properly. I get 'User does not exist' however user exists in database.
Controller
#Controller('users')
export class UsersController {
constructor(private userService: UsersService) {}
.....
//Update a user's details
#Put('/update')
async updateUser(
#Res() res,
#Query('userid') userID,
#Body() createUserDto: CreateUserDto
) {
const user = await this.userService.updateUser(userID, createUserDto);
if (!user) throw new NotFoundException('User does not exist!');
return res.status(HttpStatus.OK).json({
message: 'User has been successfully updated',
user
})
}
//Delete a user
#ApiParam({ name: 'id' })
#Delete('/delete')
async deleteUser(#Res() res, #Query('userid') userID) {
const user = await this.userService.deleteUser(userID);
if (!user) throw new NotFoundException('Customer does not exist');
return res.status(HttpStatus.OK).json({
message: 'User has been deleted',
user
})
}
Service
// Edit user details
async updateUser(userID, createUserDto: CreateUserDto): Promise<User> {
const updatedUser = await this.userModel
.findByIdAndUpdate(userID, createUserDto, { new: true });
return updatedUser;
}
// Delete a customer
async deleteUser(userID): Promise<any> {
const deletedUser = await this.userModel
.findByIdAndRemove(userID);
return deletedUser;
}
I'm using swagger to perform my tests. I'm passing id as a parameter to find and update user.
Based on your code repository, you aren't using URL Parameters, but rather you are using Query Parameters. The difference in the two is how they are passed to the server and how they are told to the server to listen for them.
Query Parameters
With query parameters, you pass them to your server starting with a ? in the url, and concatenating each one after by using a &. An example could look something like http://localhost:3000?name=Test&id=a26408f3-69eb-4443-8af7-474b896a9e70. Notice that there are two Query parameters, one named name and one named id. In Nest, to get these parameters in your route handler, you would use the #Query() decorator. A sample class could look like
#Controller()
export class AppController {
#Get()
getHello(#Query() query: { name: string, id: string }) {
return `Hello ${name}, your ID is ${id}`;
}
}
Notice how with the url above, the route called is the base route (/), with the query parameters added on.
URL Parameters
URL parameters are a way to dynamically build your routes without needing to specify what each possible URL. This is useful for things like IDs that are dynamically generated. Taking a similar URL as above, the sample URL this time could look like http://localhost:3000/Test/a26408f3-69eb-4443-8af7-474b896a9e70. Notice how this time there is no ? or & and it just looks like a full URL. To specify URL Params in nest, you need to a a colon(:) before the param name in the resource declaration decorator, along with any other part of the path necessary. Then to access the URL Parameters, you need to use the #Param() decorator in the route handler, similar to how you would the #Query() decorator. The class sample for this would be
#Controller()
export class AppController {
#Get(':name/:id')
getHello(#Param() params: { name: string, id: string })
return `Hello ${name}, your ID is ${id}`;
}
}
Problem and Solution
You're currently calling off to http://localhost/users/update/<ID> acting as if you are using URL parameters, but in your route handler you are expecting #Query() to grab the id. Because of this, there is no handler to find /users/update/:id and so you are getting a 404 in return. You can either modify your server to listen for URL Parameters as described above, or you can modify the URL to send the request using Query Parameters instead of URL parameters.
Related
I'm using Express with TypeORM and currently I got stuck in the following situation:
Before inserting or updating a record, I always want to set the username to the current record to persist the information who last updated this record.
With TypeORM, this can easily be done by using subscribers:
#EventSubscriber()
export class Subscriber implements EntitySubscriberInterface {
beforeUpdate(event: UpdateEvent<any>) {
const entity = event.entity;
if(entity instanceof Base) {
entity.lastUpdatedBy = 'username'; // TODO
}
}
}
The user is known after he was successfully authenticated:
#Put('/entity')
public updateEntity(#CurrentUser({required: true}) user: User, #Body enity: Entity): Promise<Entity> {
Log.info('User successfully authenticated: ' + user)
return this.manager.update(entity);
}
My question is: What is the simpliest way to store and access user information in a request scope?
What I found out so far:
The following stackoverflow-post seems to be related, but the
solutions sounds like a overhead for me (GLOBAL data per HTTP/Session request?)
I also could append the user information in the updateEntity method, but then I have to explicitly all the time.
Is there any other option?
You can use the atttribute data of type any of the SaveOptions.
#Put('/entity')
public updateEntity(#CurrentUser({required: true}) user: User, #Body enity: Entity): Promise<Entity> {
Log.info('User successfully authenticated: ' + user)
return this.manager.update(entity, {data: user});
}
And then, you can use the data in Subscriber with:
event.queryRunner.data
I used the following npm package to save the user in my request scope: https://www.npmjs.com/package/cls-hooked.
This package did not work for me since async/await seems not to be supported: continuation-local-storage.
I just want to know if there's a way to make the columns for a class when creating from Parse Dashboard mandatory. Just like _User class has Username, password and email required.
I suggest you use Cloud Code's beforeSave triggers for this. These beforeSave triggers are similar to validators in Mongoose: they are automatically executed before any document of that specific type is saved.
On top of checking that some fields are required, you can use it to set default values, etc.
If the trigger returns an error, the document will not be saved. If it returns a success, the flow continues and the document is saved.
Here's a quick sample to create a beforeSave on the User class:
Parse.Cloud.beforeSave(Parse.User, function (request, response) {
if (request.object.get('favouriteAnimals') === undefined) {
request.object.set('favouriteAnimals', []);
}
if (!request.object.get('firstName') || !request.object.get('lastName')) {
response.error('Full name is required.');
}
else {
response.success();
}
});
I want to write a JSON API.
My problem is, that sometimes I want to query for an ID, sometimes for a String.
One option would be to add a querystring, for example:
example.com/user/RandomName
example.com/user/1234556778898?id=true
and use it like:
api.get('user/:input', function(req, res) {
if(req.query.id) {
User.find({ '_id': req.params.input }, cb);
} else {
User.find({ 'name': req.params.input }, cb);
}
};
But this seems like bad practice to me, since it leads to a bunch of conditional expressions.
Are there more elegant ways?
I would suggest handling two endpoints. One for getting ALL the users and one for getting a SPECIFC user by ID.
example.com/users
example.com/users/:id
The second endpoint can be used to find a specific user by id.
The first endpoint can be used to find all users, but filters can be applied to this endpoint.
For example: example.com/users?name=RandomName
By doing this, you can very easily create a query in your Node service based on the parameters that are in the URL.
api.get('/users', function(req, res) {
// generate the query object based on URL parameters
var queryObject = {};
for (var key in req.query) {
queryObject[key] = req.query[key];
}
// find the users with the filter applied.
User.find(queryObject, cb);
};
By constructing your endpoints this way, you are following a RESTful API standard which will make it very easy for others to understand your code and your API. In addition, you are constructing an adaptable API as you can now filter your users by any field by adding the field as a parameter to the URL.
See this response for more information on when to use path parameters vs URL parameters.
I'm new to using mongoose middleware and don't know if I'm following it well. Here is the purpose. After saving department, I want to populate university and save departmentId inside university object.
DepartmentSchema.post('save', function(next) {
var departmentId = this._id;
University.findOne({
_id: this.university
}, function(err, university) {
if (!university.departments) {
university.departments = [];
}
university.departments.push(new ObjectId(departmentId));
university.save(function(err) {
if (err) return console.log('err-->' + err);
// saved!
});
});
});
This is working fine but I'm not sure why in Cascade style delete in Mongoose they have used exec() and next() calls. Could you please tell me the purpose of these calls? I don't know what they do and not able to find relevant documentation. I just want to make sure I'm not missing anything.
clientSchema.pre('remove', function(next) {
// 'this' is the client being removed. Provide callbacks here if you want
// to be notified of the calls' result.
Sweepstakes.remove({
client_id: this._id
}).exec();
Submission.remove({
client_id: this._id
}).exec();
next();
});
Post middleware doesn't have reference to the next function and you cant do any flow control. Its actually passing the department that just got saved, so your code can be something like this:
DepartmentSchema.post('save', function(department) {
var departmentId = department._id;
In pre middleware you have access to the next middleware in the order of execution. Which is the order of definition on a particular hook.
// hook two middlewares before the execution of the save method
schema.pre('save', pre1);
schema.pre('save', pre2);
function pre1(next) {
// next is a reference to pre2 here
next()
}
function pre2(next) {
// next will reference the hooked method, in this case its 'save'
next(new Error('something went wrong');
}
// somewhere else in the code
MyModel.save(function(err, doc) {
//It'll get an error passed from pre2
});
Mongoose also gives you the ability to execute pre middlewares in parallel, in this case all middlewares will be executed in parallel but hooked method will not execute till the done is called from each middleware.
As for the exec() function, there are two ways of executing a query in Mongoose, either pass a callback to the query or chain it with an exec(): User.remove(criteria, callback) or User.remove(criteria).exec(callback), if you don't pass a callback to the query, it'll return a query object and it won't execute unless you chain it with exec()
Here is my backbone code
var UserModel=Backbone.Model.extend({
url: 'http://api.myapi.com/user'
});
var user=new UserModel();
user.set({id:1});
user.fetch();
console.log(user.get('screenname'));
This returns the whole collection of users instead of user with the id of 1. When I change the url to
url: 'http://api.myapi.com/user/1'
I get back the user that I want. Why don't I get the record for 'user 1' when I user.set({id:1});
Note - My api is at a different domain, that is why I have the entire url in my 'url' property. Please help, I am ready to give up on backbone.
You have to set the urlRoot not url and Backbone will add the id to the end of your url :
var UserModel=Backbone.Model.extend({
urlRoot: 'http://api.myapi.com/user'
});
You will need to add the id to your model url like so.
url: function() {
if(this.id) {
return 'http://api.myapi.com/user/' + this.id;
}
return 'http://api.myapi.com/user';
}
And then when you instantiate the user model you can pass it an id like this.
var user = new UserModel({id: 1});
Then when you do user.fetch() it will get 'http://api.myapi.com/user/1'
Also by not passing an id to UserModel Backbone will send a POST request to 'http://api.myapi.com/user'
There are two points you need to validate:
Model use urlRoot as rest API url, but Collection use url.
Output result in the fetch success callback
var UserModel=Backbone.Model.extend({
urlRoot: 'http://api.myapi.com/user'
});
var user=new UserModel();
user.set({id:1});
//It is safe to output result in success callback which guarantee the api is called
user.fetch({success: function(){
console.log(user.get('screenname'));
}});