fiber icon indicating copy to clipboard operation
fiber copied to clipboard

🤗 [Question]: Calling other endpoints without network

Open nexovec opened this issue 10 months ago • 8 comments

Question Description

Hi, Is it possible to call another route from the same fiber server without going through the network?

In the snippet below, I would like to call /api/endpoint from the /ui that returns a nice UI with the result of the api call (It needs to set fiber to being awesome just in case someone forgot to do it), and to call it with all the registered middlewares and the original request headers, without going through the network, because it seems like an unneeded step.

Is this possible?

Code Snippet (optional)

package main

import "github.com/gofiber/fiber/v3"
import "log"

func main() {
  app := fiber.New()

  // An example to describe the question
        app.Get("/ui", func(c *fiber.Ctx) error {
                ret := req.Get("/api/endpoint"), or something // TODO: help needed
		return templates.WelcomeUI(ret).Render(c.Context() ... )
	})
        api := fiber.Group("/api", AdminOnlyMw)
	api.Get("/endpoint", func(c *fiber.Ctx) error {
                technology.fiber.SetAwesome(true)
		return c.SendText("fiber is awesome")
	})

  log.Fatal(app.Listen(":3000"))
}

Checklist:

  • [X] I agree to follow Fiber's Code of Conduct.
  • [X] I have checked for existing issues that describe my questions prior to opening this one.
  • [X] I understand that improperly formatted questions may be closed without explanation.

nexovec avatar Mar 31 '24 07:03 nexovec

Thanks for opening your first issue here! 🎉 Be sure to follow the issue template! If you need help or want to chat with us, join us on Discord https://gofiber.io/discord

welcome[bot] avatar Mar 31 '24 07:03 welcome[bot]

@nexovec Why do you need to call another handler just to set a boolean? Move that logic to a separate function and call that function from both handlers.

gaby avatar Mar 31 '24 13:03 gaby

@gaby I am aiming at having less indirection and less boilerplate code for actually dispatching an HTTP request, so having a function for setting a boolean is making this worse, not better. The routes being called are always depending on the expected middlewares being called too. Needless to say, my use case is more complicated, with about 3–4 levels of this indirection that I just can't do anything about(think frontend calling a backend calling an email dispatcher calling authorization).

I would like to "call the router," not the function, ideally while staying inside the fiber framework and not going through the network. Is the purpose of the question clear?

nexovec avatar Mar 31 '24 19:03 nexovec

@nexovec Yes, it's clear

@ReneWerner87 Is this even possible? User is using v3 already.

gaby avatar Mar 31 '24 19:03 gaby

In v2 it was possible with https://docs.gofiber.io/api/ctx#redirecttoroute and I think the function is still there.

ReneWerner87 avatar Apr 01 '24 07:04 ReneWerner87

In v2 it was possible with https://docs.gofiber.io/api/ctx#redirecttoroute and I think the function is still there.

My understanding is these redirect methods do a client side redirect (basically responding to the original request with a 302). Am I wrong?

nexovec avatar Apr 01 '24 11:04 nexovec

I am still looking for a solution to this issue. To stimulate discussion and give a better idea, this is how I think this could look like:

package main

import "github.com/gofiber/fiber/v3"
import "log"

func main() {
  app := fiber.New()

  // An example to describe the question
        app.Get("/ui", func(c *fiber.Ctx) error {
                response, err := c.CallRoute("/api/endpoint")
		return templates.WelcomeUI(response).Render(c.Context() ... )
	})
        api := fiber.Group("/api", AdminOnlyMw)
	api.Get("/endpoint", func(c *fiber.Ctx) error {
                technology.fiber.SetAwesome(true)
		return c.SendText("fiber is awesome")
	})

  log.Fatal(app.Listen(":3000"))
}

The method <fiber.Ctx>.CallRoute would call another route on the same server without going through the network at all. It would pass the original requests information to the newly called route. In this case, there should also exist a fiber.CallRoute function that would enable you to call a route without having a fiber context object (by having to specify all the request information as parameters).

nexovec avatar Apr 14 '24 06:04 nexovec

my 2 cents here,

if you create a controller a-like, wouldn't be better to just call it from within your call?

example:

package main

import "github.com/gofiber/fiber/v3"
import "log"

func main() {
  app := fiber.New()

  // An example to describe the question
  app.Get("/ui", func(c *fiber.Ctx) error {
    textToSend := setEndpointText(true)
    return templates.WelcomeUI(textToSend).Render(c.Context() ... )
  })

  api := fiber.Group("/api", AdminOnlyMw)
  api.Get("/endpoint", func(c *fiber.Ctx) error {
    textToSend := setEndpointText(true)
    return c.SendText(textToSend)
  })

  log.Fatal(app.Listen(":3000"))
}

func setEndpointText(txt string) string {
  technology.fiber.SetAwesome(true)
  return "fiber is awesome"
}

in a way you would not spend time in a request by redirecting it, and you end up encapsulating the data you want in both cases into just one internal call

balexandre avatar May 01 '24 14:05 balexandre

@nexovec You shouldn't look for the answers in a web framework library.

Things you may need to consider is it the network overhead really blocking you? If not, just go with the network call, especially if it happens on server side I think the latency should be negligible for your users.

What if the network call is really blocking you? You should refactor your code. You can create a pipeline pattern to make the behavior similar to middleware in fiber, and you should abstract your code so you can reuse it inside fiber middlewares and remove duplicates on your codebase.

kbiits avatar May 04 '24 07:05 kbiits

@kbiits the primary concern is reliability of the connection, the latency is a really bad problem and you shouldn't need to do a client side redirect unless you specifically need to, but the fact the request might not even arrive is much more troubling.

I just have to comment on how bad of an architectural decision it is to have 2 separate middleware systems, even worse when you make the other one to supplement the first one. Fiber is already doing this for me, it makes me really happy I don't have to deal with this myself.

I like code locality and so I don't have the slightest reason to abstract away from the fiber API either, I like it, I don't have duplicity problems.

In fact, I'd like to rely on fiber to provide faculties to simulate requests and interfaces for testing and introspection, and this falls well within the scope of what a framework should offer, if for nothing else than how closely coupled those are to the framework.

The only thing I'm asking for rn is to be able to "call a route" as you would with a function.

(Not getting confrontational, just hard disagree)

Is this understandable?

nexovec avatar May 04 '24 23:05 nexovec

Yes, it's all clear @nexovec. But I don't see any reason for fiber to provide such API. Why does fiber need to provide an API to simulate requests without a network? If it's used for testing, yes, it makes sense, but I don't get it to expose such API to the fiber users. There are many ways for fiber users to test their code without relying on the API you mentioned. Also, your usecase is very niche.

Nevertheless, I think you can still use *fiber.App.Stack() method which returns fiber stack routes to achieve what you want. You need to filter the routes that you want and call the handlers just like you call functions.

image

kbiits avatar May 05 '24 14:05 kbiits

Nevertheless, I think you can still use *fiber.App.Stack() method

@kbiits This is exactly what I imagined! Thank you so much. It's a bit clumsy to use this directly, I will likely write a convenience wrapper for this. This is massively helpful.

Also, your usecase is very niche.

This is a bit sad to hear. To me going through network when you don't have to is an extra failure point, I feel a bit of joy anytime I can avoid it.

nexovec avatar May 05 '24 17:05 nexovec

@kbiits This is exactly what I imagined! Thank you so much. It's a bit clumsy to use this directly, I will likely write a convenience wrapper for this. This is massively helpful.

Glad to help.

This is a bit sad to hear. To me going through network when you don't have to is an extra failure point, I feel a bit of joy anytime I can avoid it.

Yes, I really understand your point. We engineers of course don't want to add overhead to our system. But, objectively speaking, fiber also need to keep their library to be as simple as it can.

All the best for your projects

kbiits avatar May 05 '24 17:05 kbiits