Search code examples
node.jsexpressmongooseapolloapollo-server

Apollo Server + mongoose, passing createdBy, updatedBy to every update/insert/delete operation


I am trying to pass createdBy and updatedBy information to every query with mongoose. Is there any way to do this using middlewares with express and/or Apollo Graphql?

I then will use mongoose.set to send debug information to our logging server.


Solution

  • I managed to solve this using AsyncLocalStorage implementation.

    this is my threadContext implementation

    const { AsyncLocalStorage } = require('async_hooks');
    
    const localStorage = new AsyncLocalStorage();
    
    const contextInit = {
      user: null,
      environment: process.env.DEPLOYMENT_ENV || 'local',
      sourceCompany: undefined,
    };
    
    const getContext = () => localStorage.getStore();
    
    const initializeContext = (additionalContext) => localStorage.enterWith({ ...contextInit, ...additionalContext });
    
    const updateContext = (context) => {
      Object.keys(context).forEach((k) => {
        localStorage.getStore()[k] = context[k];
      });
    };
    
    module.exports = {
      getContext,
      contextInit,
      initializeContext,
      updateContext,
    };
    

    then, I injected middleware to initialize the context to express

    const { initializeContext: initializeThreadContext } = require('./services/threadContext');
    
    const RequestId = require('./helpers/expressRequestId');
    
    app.use(RequestId()); <<-- feel free to write your own
    app.use((req, res, next) => {
      initializeThreadContext({ requestId: req.id });
      next();
    });
    

    In apollo server context to store user data

    // include user in threadContext
    threadContext.getContext().user = user ? { _id: user._id, name: user.name } : undefined;
    

    then in mongoose, I am logging our queries with user data

    mongoose.set('debug', (collectionName, methodName, query, doc, ...methodArgs) => {
      const ignoredMethods = [/createIndex/, /watch/];
      if (ignoredMethods.some((m) => new RegExp(m).test(methodName))) return;
    
      logger.debug(`Mongodb operation '${methodName}' on  '${collectionName}'`, {
        collectionName,
        methodName,
        query,
        ...threadContext.getContext(),
      });
    });
    

    I preferred not to store user info with createdBy instead logged it