node-restify icon indicating copy to clipboard operation
node-restify copied to clipboard

Question/Feature-request: Is there an easy way to trigger a route manually

Open pkyeck opened this issue 4 years ago • 4 comments

  • [x] Used appropriate template for the issue type
  • [x] Searched both open and closed issues for duplicates of this issue
  • [x] Title adequately and concisely reflects the feature or the bug

Feature Request

Use Case

I want to be able to trigger a server route manually.
I'm not sure if this is already possible but scanning the docs and old issues I could not find anything that sounded similar.

Example API

This could look something like this const response = await server.inject({ method: 'GET', url: '/' }) (fastify-like).

Are you willing and able to implement this?

If someone would point me in the right direction I could try doing a PR ... or ask my company to pay a small amount for someone else to do it. Let me know ...

pkyeck avatar Nov 05 '21 16:11 pkyeck

I have a similar need — I want to be able to write a route handler that calls other routes, specifying the method, body, headers, params, etc. but in all other respects delegating to the original request.

I have this so far:

      const intReq = new http.IncomingMessage(req.socket);
      intReq.method = 'GET';
      intReq.url = '/an-internal-route';
      intReq.headers = req.headers;
      const intRes = new http.ServerResponse(intReq);
      server._onRequest(intReq, intRes);

Where server is a restify server instance, req is an incoming restify request, and http is the node http package.

It doesn't obviously fail, but it also does not obviously succeed. The primary difficulty is in constructing the new request and response class instances — I have little confidence I'm even close to doing something good and reasonable in this spike.

dball avatar Sep 07 '22 16:09 dball

Further investigation reveals that the fastify light-my-request library seems very nicely suited for this purpose, but the wrinkle there is that the synthetic request that it creates does not have all of the methods that the restify request monkey patches into the http.IncomingMessage class. I'm inclined to patch those in in my own dispatch function, but restify does not export its patch functions in any way that I can see.

There might be a way to do it by manually messing with the instance prototypes, but maybe someone has a better idea.

dball avatar Sep 07 '22 18:09 dball

Happy to report I have a working solution:

This needs to be run once, in order to add restify's monkey patched methods to the light-my-request Request:

const { inject } = require('light-my-request');

const { Request } = require('light-my-request/lib/request');
const patchRequest = require('restify/lib/request');
patchRequest(Request);

And then something along these lines:

const dispatch = server._onRequest.bind(server);
// accept-encoding of null disables gzip compression
const ireq = { method: 'get', url: '/your/internal-route', headers: { ...req.headers, 'accept-encoding': null } };
const res = await inject(dispatch, ireq)

Two things strike me as problematic about this — requiring sub-packages of restify and light-my-request, which may be outside of what they consider to be their public api, and similarly, using the restify _onRequest method. Otherwise, this seems eminently reasonable.

dball avatar Sep 07 '22 21:09 dball

For those landing on this it looks like patching light-my-request's Request object is also no longer feasible

    TypeError: Cannot set property closed of [object Object] which has only a getter

       6 | // eslint-disable-next-line @typescript-eslint/no-var-requires
       7 | const patchRequest = require('restify/lib/request');
    >  8 | patchRequest(Request);

freakyfelt avatar Oct 25 '23 07:10 freakyfelt