Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose some information about API resolution to middlewares #1553

Open
nbacquey opened this issue Mar 7, 2022 · 1 comment · May be fixed by #1561
Open

Expose some information about API resolution to middlewares #1553

nbacquey opened this issue Mar 7, 2022 · 1 comment · May be fixed by #1561

Comments

@nbacquey
Copy link
Contributor

nbacquey commented Mar 7, 2022

Recently, I tried to write a WAI Middleware that was aware of the API my application was using. In particular, given an incoming request url, I wanted to be able to tell which endpoint was used, and which parts of the url were captured.

For instance, consider the server api below:

type Api =
       "foo" :> Capture "id" Int :> Get '[JSON, PlainText] Foo
  :<|> "bar" :> Capture "id" Int :> Get '[JSON, PlainText] Bar

Let us request it with the following command:

curl -X GET "http://localhost/foo/12345" -H "Accept: application/json"

If the request succeeds, we should get a Foo object, encoded as JSON.
The additional data I would like to get from Servant for my middleware would be something like:

{
  "effective-url": "/foo/<id::Int>",
  "content-type": "application/json"
}

The "content-type" part is easy to get from the response header, but the "url" part doesn't seem easily available.

Effectively, what I would need is a way to perform the transformation GET "/foo/12345" => GET "/foo/<id::Int>"

I can think of several ways for Servant to provide this information:

  • Return the "effective type" that was used for each request, to be processed by the middleware. For instance, the request above would return
type EffectiveApi = "foo" :> Capture "id" Int :> Get '[JSON] Foo
  • Introduce a combinator that would have the effect of including routing information in the response headers. The API above would then be written as
type Api = WithRouting :>
       "foo" :> Capture "id" Int :> Get '[JSON, PlainText] Foo
  :<|> "bar" :> Capture "id" Int :> Get '[JSON, PlainText] Bar

then the response headers would include "Effective-Url": "/foo/<id::Int>"

  • Introduce a new Servant.Middleware that would be aware of API resolution mechanisms.
@arianvp
Copy link
Member

arianvp commented Mar 10, 2022

You can do this to day with the servant-ekg library's HasEndpoint typeclass though it might be more beneficial to move HasEndpoint to servant proper

withEndpoints :: HasEndpoint api => Proxy api -> Middleware
withEndpoints proxy req resp app = do
  case getEndpoint proxy req of
    Nothing -> putStrLn "unknown"
    Just (APIEndpoint pathParts _method) = print pathParts
  app req resp

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants