Best way to implement a "front controller" with Racket's web server

This question is primarily directed to @jeapostrophe , but anyone can feel free to comment :slight_smile:

I think back in 2019, Jay mentioned my use of web-server/dispatchers/dispatch-lift in Axio was less than ideal, and I never bothered to follow up for more detail re: why. I'm not using continuations, and I simply want a straightforward way for the Racket web server to dispatch to my own "front controller", and using dispatch-lift seemed like the most straightforward way to dispatch to a function.

The relevant code is axio-app-init and front-controller

If there's a better way to get the web server to call my front controller, I'm all ears :slight_smile:

I'm about to use @bogdan 's sentry package to send unhandled exceptions to Sentry, so it's a good time to tweak this part of the architecture if necessary.

Thanks!

1 Like

Replying partly to keep track of this thread.

IIRC when I started development on Frosthaven Manager's web server, I looked at lift:make, too. I don't recall what for and I don't have any commit records of the experiment (sadly), but I seem to remember the primary issue being that the implementation doesn't handle continuations well:

(define ((make procedure) conn req)
(output-response/method
conn
(procedure req)
(request-method req)))

Again, I don't remember the specific problem, but I think it had to do with adding "middleware" to the request handling and not having that work correctly in certain cases?

Jay and others can probably comment more thoroughly. I know Brian is also interested in how Koyo handles this, and I haven't dived into the code yet to find out :slight_smile:

If you don't want to use continuations in your application, then I don't think there's anything particularly wrong with avoiding dispatch/servlet and using dispatch-lift.

I like to put my middleware at a level below dispatchers, though, so I typically have middleware defined as procedures from a request handler to a request handler:

(define-values (app reverse-uri)
  [("") index-page])

(define (stack handler)
  (~> handler
      (wrap-cors)
      (wrap-auth)
      (wrap-session)
      (wrap-sentry))

(dispatch/servlet (stack app))

where wrap-sentry might look like:

(define ((wrap-sentry hdl) req . args)
  (with-handlers ([exn:fail? (lambda (e)
                               (sentry-capture-exception! e)
                               (raise e))])
    (apply hdl req args)))