lundi 27 janvier 2020

Implicitly passing client context to services in Node

In node.js I have been trying to find a way to pass client context from the Express routes into the services without every service function to have a context. By context I mean an object with user type, ID, possible queries passed on the request, etc.

I see three possible ways to do this, keep the context as argument of services (already more or less how it is done), refactor all services to only receive the exact data they need (a lot of work and have to take into account that other services might be getting called passing the context) or what I am trying to do, implicitly pass the context object between services with a Service base class like:

class Service {
  call(service, context = null) {
    service._context = context || this._context;
    return service;
  }

  callWith(context) {
    this._context = context || this._context;
    return this;
  }
}

class FooService extends Service {
  constructor(model) {
    this.model = model;
  }

  async foo(bar) {
    // Calls other service and passes the context object to it
    bar.test = await this.call(barService).test(bar);
    return await this.model.foo(bar);
  }
}

// Somewhere else, defining my Express routes:
app.route(`foo/`).get(async (req, res, next) => {
  try {
    const limit = parseInt(req.query.limit, 10);
    const data = await fooService.callWith(req.context)
      .foo({ prop: req.query.property, limit: !isNaN(limit) ? limit : null });
    res.send(data);
  } catch (err) {
    next(err);
  }
});

If there is a nicer clean way to do this would be great to know. Also I am worried that as services are singleton instances somehow it can run asynchronous code while using the wrong context. I know node is single-threaded, but the context property is not async while the methods are and context can change while still resolving a promise on another service.

Any ideas are welcome, thanks.

Aucun commentaire:

Enregistrer un commentaire