How to properly use dataloaders across multiple users? - node.js

In caching per request the following example is given that shows how to use dataloaders in express.
function createLoaders(authToken) {
return {
users: new DataLoader(ids => genUsers(authToken, ids)),
}
}
var app = express()
app.get('/', function(req, res) {
var authToken = authenticateUser(req)
var loaders = createLoaders(authToken)
res.send(renderPage(req, loaders))
})
app.listen()
I'm confused about passing authToken to genUsers batch function. How should a batch function be composed to use authToken and to return each user corresponding results??

What the example is saying that genUsers should use the credentials of the current request's user (identified by their auth token) to ensure they can only fetch data that they're allowed to see. Essentially, the loader gets initialised at the start of the request, and then discarded at the end, and never recycled between requests.

Related

Will a Lambda function using Express reload it's middleware for each request?

TLDR: Will the middleware that gets called by app.use be recalculated for each individual request in Express?
I have a Lambda function that uses the following function as its middleware to inject the user into every request:
async function(req, res, next) {
try {
const IDP_REGEX = /.*\/.*,(.*)\/(.*):CognitoSignIn:(.*)/;
const authProvider =
req.apiGateway.event.requestContext.identity
.cognitoAuthenticationProvider;
const [, , , userId] = authProvider.match(IDP_REGEX);
const cognito = new AWS.CognitoIdentityServiceProvider();
const listUsersResponse = await cognito
.listUsers({
UserPoolId: process.env.AUTH_SAYM_USERPOOLID,
Filter: `sub = "${userId}"`,
Limit: 1,
})
.promise();
const user = listUsersResponse.Users[0];
req.user = user;
next();
} catch (error) {
console.log(error);
next(error);
}
}
My question is: Will this middleware run once for each individual user? Or will this code run only every time the Lambda function "goes to sleep"? What I mean is, will there be a bug, if the Lambda function gets pinged often and never goes idle again that each request will have the user who made the first request? Or does the middleware calculate for each individual request?
This middleware will run upon every request and this has nothing to do with Lambda. Once the requests reaches your Express-based Lambda function, it's just going to act as a regular Express server.
The middleware's responsibility is to intercept every HTTP call, regardless of where it's running.
The only thing I see here is to move the declaration of const cognito = new AWS.CognitoIdentityServiceProvider(); out of your middleware's scope, so the instance of this object is cached for as long as the container lives. You will gain a couple of microseconds by doing so (which may be irrelevant, but is indeed a good practice).
You have to keep in mind though that since modules in Node.js are singletons, anything required/declared in "global" scopes (outside a given function, for example) is going to be re-used in future invocations to the same running container. Use this in your favour but also be careful with unintentionally caching things you don't want to.

Is there a way to track an instance in Node without passing it around everywhere?

I have a singleton logger file. When a request comes into Express, I use middleware to set the request ID.
// Relevant parts of server.js
import express from 'express';
import requestIdMiddleware from './request-id-middleware';
const app = express();
app.use(requestIdMiddleware);
--
// Relevant parts of request-id-middleware.js
const uuid = require('uuid/v4');
const { setRequestId } = require('./logger');
module.exports = function(req, res, next) {
const id = uuid();
req.id = id;
// This sets a static variable on the plain logger object
setRequestId(id);
next();
};
--
// Relevant parts of logger.js
module.exports = {
request_id: null,
setRequestId: id => {
this.request_id = id;
},
log: message => {
// sends a log out using this.request_id, which is shared by all users of the server
}
}
Using the plain object now means everyone is sharing the same value. So despite each request calling setRequestId, it means if the first request takes longer than the second, it may use the second request's ID when referencing logger's value.
It seems I would need to make the logger a class instead of a plain object, instantiate it in my request middleware, and then pass the instance through to ensure unique request IDs across multiple calls from same request. Although unlikely, hoping to find a way around needing to pass a variable down into anything I want to log from.

handling Postbacks From other website to My website

Im using SuperRewards (SR) To make Coin transaction for users in my website,maybe you are familiar with SR.
Whenever a transaction happens SuperRewards sends a postback (Post Request) to my server containing information about the transaction and coins etc...
So my question is how to handle Postbacks or Post request ( i really dont know the diffrence ) from other website to my server USing Nodejs Express ?
Picture 1 App testing
picture 2 app Testing
Code
You would handle it like any other request coming to your Express app. Since it's a GET request, you would have a GET route defined.
According to section Fields and Formats, you get back quite a few query strings. Knowing that we can do the following:
app.get('/super-rewards', async (req, res) => {
// `new` is a reserved keyword, so we can't use `new` as a variable name.
const newCurrency = req.query['new']
const {
id,
uid,
oid,
total,
sig
} = req.query
})
Additionally, the documentation states that sig should match the MD5 hash of your secret key, if I'm understanding that correctly. So a full example would be something like:
const crypto = require('crypto')
const express = require('express')
const app = express()
app.get('/super-rewards', async (req, res) => {
// `new` is a reserved keyword, so we can't use `new` as a variable name.
const newCurrency = req.query['new']
const {
id,
uid,
oid,
total,
sig
} = req.query
const secretHash = crypto.createHash('md5').update(process.env.SECRET_KEY).digest('hex')
if (secretHash !== sig) {
throw new Error('Invalid transaction')
}
})
Aside, it is a GET request because the documentation clearly states that:
Postbacks are sent from our server to yours using a HTTP GET request (...)
I contacted the support team and they told me i must use a public domain not Local host that s why it wasnt working so the problem is solved and thank you for your time :)

per-request session in meteor server?

I am adding an auth layer and I think I have it figured out except for one tricky detail.
My Meteor app doesn't have any routes but I've added a hook into the connect middleware so that the "/" route errors if there isn't a correct API token. If the token is okay then I call next() to forward the route to Meteor.
The problem is that, depending on the token, I need to set server-side parameters for the connection, and I don't know how to do this. For example, say I have a static list of API keys mapped to permission levels. If a user sends a request with "ADMIN_API_KEY" then I would like to set Session.permission_level = "admin" for use by the Meteor server's functions. Session is just for the client in Meteor, though.
# this code's in coffeescript
WebApp.connectHandlers.use '/', (req, res, next) ->
validator = new RequestValidator(req, next)
validations = [
"valid_namespace",
"only_https"
]
error = validator.validate(validations)
next(error)
# <<<<<<<<<<<<<<<<<<<<<<<<
# Here I want to set some config option which can be
# read by the server in the same way it can read things like
# Meteor.user()
In Rails I would just say session[:permission_level] = "admin". But it seems to not work this way in Meteor.
By the way, I am not using a Routing package yet in Meteor, though if that would make this easier than I would.
I'm not sure about Session I've been doing something like
import { DDP } from 'meteor/ddp';
import { DDPCommon } from 'meteor/ddp-common';
export const authMiddleware = (req, res, next) => {
const userId = identifyUser(req); // parse the request to get the token you expect
if (!userId) {
return next();
}
DDP._CurrentInvocation.withValue(new DDPCommon.MethodInvocation({
isSimulation: false,
userId,
}), () => {
next();
// in that context, Meteor.userId corresponds to userId
});
};
for my REST api and that works well regarding the user Id and being able to call Meteor function that should be invoke in a DDP context, like Users.find(...).

Node Express auth status

I have multiple routes, split into different files (my app consists of different "modules", which I maintain in separate folders. For each folder, there is an index.js file in which I manage the routes per module, and I require these in the app.js file).
For every route, I will require to check the auth, and pass the loggedIn status to the header of every page:
//Default variables for the ejs template
var options = {
loggedIn: true
};
res.render("home/home", options);
If the logged in status is true, then the user's name will be displayed. If not, the login / signup labels are displayed.
What is the best way to centralise this, so that I don't need to require the auth script in every of these index.js (route) files?
I need to be able to pass the auth status to the view via the options object (see example).
In your auth, module, use a middleware function. That function can check and store res.locals.loggedIn which will be available for any view that will eventually be rendered. Just make sure the app.use call executes prior to your other routes and it will work properly.
app.use(function auth(req, res, next) {
res.locals.loggedIn = true; // compute proper value here
next();
});
From what I understand you need to do this for every request.One common thing is adding this as middleware so that all the request gets this .
For Example :
var http = require('http');
var connect = require('connect');
var app = connect();
app.use(function(req, res) {
res.end('Hello!');
});
http.createServer(app).listen(3000)
Now for every request , Hello is printed . You could extract this as a module and reuse it across projects. Check here for more details

Resources