NestJS proceed with default routing behavior when needed - nestjs

I have created a wildcard route in my App.controller.ts, in order to catch certain URL requests and handle them. But in case if path contains /API/ - i want to pass handling back to other controllers ( like 'api/user', 'api/something').
Here is my code:
#Get('*/')
getResources(#Res() res: Response, #Req() req: Request): void {
if(req.path.startsWith('/api/')){
// HERE I NEED TO RETURN HANDLING BACK TO 'api/*' controllers
// Something like next(); - but it doesn't work here - i tried
}
// Some other logic
}
Expected result:
GET <>/banana - // Some other logic
GET <>/apple - // Some other logic
GET <>/api/user - // Data from api/user controller

Related

ApolloGateway: How to make a gql call after receiving a response

I have a simple ApolloGateway server, which receives a gql mutation. What I am trying to accomplish is to make another gql mutation upon receiving a success call from the mutation call.
I can see that I can use the method didReceiveResponse in order to handle the received body. However, I am not sure whether there is a better way to do it. Specially, I will need to parse the body, check for the request type, and then make a qgl query, and send an http request, etc.
class SomeRemoteGraphQLDataSource extends RemoteGraphQLDataSource {
async didReceiveResponse(response, req, context) {
const body = await super.didReceiveResponse(response, req, context);
// Do something
return body;
}
}

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);
}

Node typescript global service to return value from a function

I'm trying to implement a service for my backend that allows the session data to be called from anywhere in the code, which means I want to create a service file that exports the values from the functions that get the session data. Otherwise I can only get the session data from inside functions that have both req: Request and res: Response parameters. So I'm basically trying to lock the values to a variable that can be used and called from anywhere in my project. My code now looks like this but if I use the exports anywhere else in the file, it just prints the actual code (function) snippet instead of the return value specified inside the code. I'm pretty new to typescript and node in general which means I might be doing some really silly errors here.
Thanks for all the help in advance!
/Victor
import { Request, Response, NextFunction } from "express";
function getUserSessionData(req: Request, res: Response) {
const userData = res.status(200).send(req.session.userData);
return userData;
}
function getUserSessionLang(req: Request, res: Response) {
const userLang = res.status(200).send(req.session.language);
return userLang;
}
function getUserSessionAll(req: Request, res: Response) {
const userAll = res.status(200).send(req.session);
return userAll;
}
module.exports = {
userData: getUserSessionData,
userLang: getUserSessionLang,
userAll: getUserSessionAll
};
How I would like it to work:
const sessionService = require("./services/sessionService");
function getStuff(req: Request, res: Response, next: NextFunction) {
let redisKey;
if (!req.session.language) {
redisKey = "getStuffEN";
}
else {
redisKey = "getStuff" + sessionService.userLang;
}
console.log(redisKey);
getRedis(redisKey, function success(reply: any) {
res.status(200).json(
JSON.parse(reply)
);
}, function error(err: Error) {
console.log("Something went wrong");
});
}
This is how it is right now (and working)
function getStuff(req: Request, res: Response, next: NextFunction) {
let redisKey;
if (!req.session.language) {
redisKey = "getStuffEN";
}
else {
redisKey = "getStuff" + req.session.language;
}
console.log(redisKey);
getRedis(redisKey, function success(reply: any) {
res.status(200).json(
JSON.parse(reply)
);
}, function error(err: Error) {
console.log("Something went wrong");
});
}
I want it to work like the first example, since there are some instances in my code where I want to access the data without having to pass the req, res parameters, if possible.
First, a short explanation on sessions:
When a user logs in (and you verify his credentials, etz) you start a new session for that user.
The middleware you're using will assign this session a unique ID and you can assign some data to it.
The ID is transferred to the users Browser in form of a cookie
The Data is stored on the Server (in Redis for your case)
The middleware will check if a session-cookie with a valid ID is included in a request and do the following:
Fetch Session-Data for the given ID from Redis
Populate the req.session-object with the Data from Redis
Call the next Route
With this out of the way, a word of advice: Don't store the session-data in your applications memory. Why? The data should only be relevant in the context of a request from a user. You'll need the session data to handle the request, but you don't need it otherwise.
Instead of storing it globally, build your handlers and functions to accept the specific Session-Data as parameters, like this:
// handler, after middleware:
const getUserBio = (req, res) => {
const userId = req.session.userId;
const bioData = fetchBio(userId);
res.render("userBio", bioData);
}
// somewhere else in your code
function fetchBio(userId) {
const fullBio = database.fetchBio(userId);
return {
full: fullBio,
excerpt: fullBio.substring(0, 24);
}
}
This has two important advantages:
You don't have to keep the session-Data in your memory synchronized with the one in Redis
These (almost) pure functions make your code easier to understand
If you write functions that work entirely on their input parameters and don't use any global state, things like "order in which to call functions" or "check if data is available" become irrelevant. The caller is responsible for getting the data, the callee is responsible for working with it.
Extending on that, express routes should never use any global in-memory data, if you ever want to scale your application horizontally (by using multiple instances). It can't be guaranteed that the same client will always connect to the same server-instance, so the globally stored data might be available for one request but not for the next. In this case, you'll have to find a way to share the global data between all your instances, which is what Redis already does in your case.
tl;dr: Only access the session-data in a request-handler, then pass it on as parameters to any functions that need to work on it. Don't keep global in-memory state in your server if you ever want to scale it horizontally.
You can write data to a db or to a file as it say madvic, but we can use also global variables. But global variables is a bad practise, as I know, so It's better to write someone your data.

How to work with implied function parameters in NodeJS

In my website's routes file, I have a function like this:
router.post('/', ctrl1.validate, ctrl2.doSomething)
With the validate function looking like this:
function(req,res,next){
var errors = validator.checkForm('myForm')
if(errors){
res.redirect("/")
}else{
next()
}
}
If I want to pass parameters into the validator function (like the name of forms I want to validate) besides the implied req,res,next, how is that done? I have tried ctrl1.validate(formName) and ctrl1.validate(formName, req,res,next) with function(formName, req,res,next) in the controller, and neither work.
The ideal solution would be to identify what form you're working on from the data passed with the request in the first place. You don't show what that is, so we don't know exactly what to recommend or if that is feasible in this case.
If you can't do that and want to have a generic function that you can use as a request handler in multiple places and you want to pass a parameter to it that is different in each of the different places you use it, then you need to create a function that returns a function.
router.post('/', ctrl1.validate("someFormName"), ctrl2.doSomething)
// definition of ctrl1.validate
validate: function(formName) {
// return a request handler that will knkow which formName to use
return function(req,res,next){
var errors = validator.checkForm(formName)
if(errors){
res.redirect("/")
} else {
next()
}
}
}
When you first call this method, it returns another function that is your actual request handler. That inside function then has access to both req, res, next from the request and has access to the formName that was originally passed in.

How to get req and res object of Expreejs in .ejs file

I am trying to use Express js with .ejs views.
I want to redirect my page to some another page on any event let say "onCancelEvent"
As per Express js documentation,I can do this by using res.redirect("/home");
But I am not able to get res object in my ejs file.
Can anyone Please tell me how to access req and res object in .ejs file
Please help.
Thanks
Short Answer
If you want to access the "req/res" in the EJS templates, you can either pass the req/res object to the res.render() in your controller function (the middleware specific for this request):
res.render(viewName, { req : req, res : res /* other models */};
Or set the res.locals in some middleware serving all the requests (including this one):
res.locals.req = req;
res.locals.res = res;
Then you will be able to access the "req/res" in EJS:
<% res.redirect("http://www.stackoverflow.com"); %>
Further Discussion
However, do you really want to use res in the view template to redirect?
If the event initiates some request to the server side, it should go through the controller before the view. So you must be able to detect the condition and send redirect within the controller.
If the event only occurs at client side (browser side) without sending request to server, the redirect can be done by the client side javascript:
window.location = "http://www.stackoverflow.com";
In my opinion: You don't.
It is better to create the logic that determines the need to redirect inside some middleware that happens long before you call res.render()
It is my argument that your EJS file should contain as little logic as possible. Loops and conditionals are ok as long as they are limited. But all other logic should be placed in middleware.
function myFn( req, res, next) {
// Redirect if something has happened
if (something) {
res.redirect('someurl');
}
// Otherwise move on to the next middleware
next();
}
Or:
function myFn( req, res, next) {
var options = {
// Fill this in with your needed options
};
// Redirect if something has happened
if (something) {
res.redirect('someurl');
} else {
// Otherwise render the page
res.render('myPage', options);
}
}

Resources