Can't make #Get('id') to work in NestJS tutorial - nestjs

So I am following this tutorial, and following it, word by word:
https://medium.com/#kaushiksamanta23/nest-js-tutorial-series-part-1-introduction-setup-c87ba810ea9e
So I have this in my service file:
getCourse(courseId): Promise<any> {
let id = Number(courseId);
return new Promise(resolve => {
const course = this.courses.find(course => course.id === id);
if (!course) {
throw new HttpException('Course does not exist', 404)
}
resolve(course);
});
}
and this in my controller, exactly like in the tutorial ,except the log:
#Get(':courseId')
async getCourse(#Param('courseId') courseId) {
console.log(courseId)
const course = await this.coursesService.getCourse(courseId);
return course;
}
In the POSTMAN, when calling http://localhost:3000/courses/courseId=3, I am getting "message": "Course does not exist"
The log clearly shows the problem:
{ courseId: 'courseId=3' }
So when I try http://localhost:3000/courses/3 instead it works, but it's not the right way.
I am lost here, my code is identical to the one in tutorial, and I thought the whole purpose of #Param('courseId') is to recognize the string 'courseId' as key and get whatever value after the '=' in the url. If so, why am I receiving 'courseId=3'? Surely I am not supposed to parse the string manually.

You're confusing URL Parameters with Query Parameters. In a URL, you can have parameters that are parsed differently depending on the route handler. In this case, your URL says that you have a base of courses and then a url parameter by the name courseId. When you make the request, the URL looks likehttp://localhost/courses/3 (the 3 is the courseId) and then you would get the id like req.param['courseId]. If you are wanting to use a URL more like http://localhost/courses/?courseId=3 then you need to use #Query() instead to get the query parameter (this would map to req.query instead of req.param)

Related

Argument passed in must be a string of 12 bytes or a string of 24 hex characters or an integer : BSONTypeError

router.get('/editProduct/:id', async(req,res) => {
let products = await productHelpers.findProduct(req.params.id);
console.log(products);
res.render('admin/editProduct');
});
findProduct function is given below:
findProduct:(proId) => {
return new Promise(async(resolve, reject) => {
db
.get()
.collection(collection.PRODUCT_COLLECTION)
.findOne({_id:objectId(proId)})
.then((products) => {
resolve(products)
})
})
}
My problem is I cannot render the editProduct page. Products are printed on the console, so there is no problem with the findProduct function.
When I render a page it shows an error in terminal as follows:
I can redirect a page but cannot render a page in this case.
The problem seems to be with the additional resources (images, Javascript) that your page /admin/editProduct/<id> loads after successfully finding a product. The log shows many such requests, like GET /admin/editProduct/js/main.js, and they all fail with 404 Not Found. I assume you have an express.static(...) route that is meant to serve them, can you share that?
The BSONTypeError that you observe would be explained if one of these additional resources had the form GET /admin/editProduct/style.css. Such a request would be matched by the /editProduct/:id route, because it has only one / after the editProduct. Inside that route, you would then have req.params.id = "style.css", which is not a valid ObjectId string.

How can I route a path which has '?' character in it in nestjs?

For example in browser we send /controller/test?value=2 but I want to route this at an endpoint? I have another endpoint /controller/test which captures even the requests made to this route
That's a query string.
You can use #Query decorator in the function parameter
#Get('/path')
async find(#Query() query) {
console.log(query);
}
More info
In this situation, it seems like your route /controller/test accepts a query parameter value. Regardless of whether or not you query that route with the query parameter value, all requests will hit your route controller at controller/test.
In other words, your server treats
GET /controller/test as a request to /controller/test as a request with value: undefined
and
GET /controller/test?value=2 as a request to /controller/test as a request with value: 2
Therefore, your controller should look something like (assuming you're using typescript)
interface ControllerQuery {
value?: number
}
#Get('/controller/path')
async controller( #Query query: ControllerQuery ) {
if (query.value) {
console.log(query.value);
} else {
console.log('No value provided, now what?');
}
}
Like said above, query params are not filtered by route. But you could do something like this, which would be more restful as well. That or having a search endpoint.
#Get('/path')
async without() {
console.log('without param');
}
#Get('/path/:value')
async find(#Param('value') value: string,) {
console.log(value);
}
#Get('/path/search')
async search(#Query() query) {
console.log(query);
}

how can I get an element in a redirect url using node.js

Non-English country, please forgive my spelling mistakes.
For example, I want to first redirect url1(http://localhost:3000/api/song/167278) to url2(http://localhost:4000/api/song/167278) to use url2's api. And url2 will reponse a json file, which can be seen in the postman's panel.
(postman's pannel)
But there maybe a lot of elements, I only want an element in the file, such as data[0].url. How can I return just return the url value (data[0].url in this json) when people access http://localhost:3000/api/song/167278.
I am using express.js now, how can I edit it? Or is there any other methods?
app.get('api/song/:id', async (req, res) => {
try {
const { id } = req.params
url = "http://localhost:4000/api/song/" + id
res.redirect(url)
}
catch (e) {
console.log(e)
}
}
You could either proxy the entire request there or fetch localhost:4000/api/song/1 in your request handler (with something like node-fetch or axios or with node's APIs and send the fields that you want back to the client as json.

getting parameter from default route

I am getting paramter from default route like this
conf.js: router.use('/' ,require('./hello'));
hello.js:
router.get('/:param', async (req,res)=>{
try{
let { param }= req.params;
console.log(param)
let param = await Params.findOne({param});
if(!param){
return res.redirect('/not-found')
}
})
and when i visit to any url that i am handling or not handling (like this: router.use('/blah',require('./blah'));)
the code above handles it, so how can i prevent that and let the actual handler handle it? for example when i am redirected to /not-found this is actual /not-found handler
router.get('/', async (req,res)=>{
try{
res.render('notfound')
}catch(err){
return res.redirect(url.format({
pathname: '/error',
query: {
message: err.message
}
}))
}
})
but it is handled as /:param
Express routes and middleware are order-dependent. Everying you add to app via use, get, post, etc. is actually one long chain of possible matches for the URL.
If the first route you add matches everything (at least, everything with only one path component) then sure enough, that route is going to match everything.
The solution is to add more specific routes like app.get('/people') first, and catch-alls like app.get('/:param') last.
As for the "not found" case however, I do not understand why you would expect this to fire at all. Of course any one-path-component path will match the /:param route. So perhaps what you really want is to match only certain values of param. For that, Express supports regex rules. There is a good explanation here.
if I understand you , you need to set this route /:param at the end of your routers chain
router.use('/blah',require('./blah'))
//then
router.get('/:param', async (req,res)=>{
try{
let { param }= req.params;
console.log(param)
let param = await Params.findOne({param});
if(!param){
return res.redirect('/not-found')
}
})
so your request will arrive on the first route if it doesn't match it will go to the second one

SailsJS : Redirecting to controller action with variables

I am using sailsJS with ejs engine and i want to redirect the user back to the input page with messages ( validation errors ... ) .
i used to use this easily with laravel in PHP ( return redirect('dashboard')->with('status', 'Profile updated!'); )
i.e : i need to redirect the user back saying that this site dont exist
find : function(req,res){
var id = req.param(íd');
Site.find(id).where({isDeleted : null })
.exec(function(err,siteFound){
if(err) console.log(err);
if(siteFound) {
return res.view('site/show', {
site : siteFound
});
} else {
return res.redirect('/site');
}
})
},
i searched in sails documentation but found nothing. how can this be performed in SailsJS ?
thanks
UPDATE : i found what i needed exactly by installing sails-hook-flash . the feature i needed is called flash messages.
Thank you for your help !
Blockquote
I can't quite tell if you want a true browser redirect. A browser redirect means sending a message back to the browser that says "use this other url instead", and then it gets fresh data (meaning new req and res objects) from your app. If this is what you want, I'd say the only real options for passing data are query strings, like:
return res.redirect('/site?message=notfound');
Then in your recieving controller action for site you can access this via req.param('message').
However, if you just want to return the appropriate content now without getting the browser to redirect, you can just call whatever view or controller action you like:
if(siteFound) {
return res.view('site/show', {
site : siteFound
});
} else {
// uncomment one of the options:
// ONE: return another view
// return res.view('site/notfound, {requested: id});
// TWO: pass to another controller action in same controller
// req.options.message = 'not found';
// return module.exports.someOtherAction(req, res);
// THREE: pass to a controller action in another controller
// req.options.message = 'not found';
// var OtherController = require('./OtherController');
// return OtherController.someOtherAction(req, res);
}
Any of those three options will keep you at the user's requested url ("/site/abc123" or whatever), but display the content you specify.
res.notfound("my message string"); should do it right?
You can work with res.json() if it is an ajax request expecting a custom response.
Read the docs about the res object HERE and the custom notfound response HERE.

Resources