-
Notifications
You must be signed in to change notification settings - Fork 29
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
Adapter for express js middleware #47
Comments
This is indeed an interesting proposal, and it has been on my TODO list before. 🙂 Not sure why there's no issue or note of it anywhere. As long as they're using the Hyper.Node.Server, then yes, everything could be compatible. Then it is up to the one doing the FFI binding to provide a proper type signature, and perhaps to provide a value in the The tricky part, I guess, is that many Express middleware might perform a side effect, or not, and then just hand over to the next, which also might perform side effects. Indeed, this is why I started with Hyper in the first place. 😄 But I think Express middleware should be "lifted" to Hyper on an individual basis, not with a general One other thing, which I wanted to write about but I haven't had time, is how error handling differs with Hyper from Express (as we're tracking effects). In Express, errors are passed in continuations to signal that the response hasn't been sent and that it's up to someone else to do it. In Hyper, you need to "wrap" fallback middleware with other middleware that might fail. For example, the fileServer middleware takes an To sum up; I think this is valuable step for Hyper to take, and that it could, as you say, enable more projects to gradually migrate from Express. That is probably the selling point. I think Haskellers are unlikely to jump on the NodeJS runtime just to track side effects in middleware with Hyper, but battle-scarred NodeJS developers might long for some type safety in their backend projects. |
Yeah that's true. For the express middleware I've used before, it modifies the request or response object and add a new property to it, and it also performs a side effect like reading/writing to a database (like for a store for passportjs) |
here is an example module Server where
import Prelude
import Effect
import Effect.Aff
import Effect.Aff.Class (liftAff, class MonadAff)
import Data.Tuple (Tuple(Tuple))
import Control.Monad.Indexed.Qualified as IndexedMonad
import Hyper.Node.FileServer as Hyper
import Hyper.Node.Server as Hyper
import Hyper.Response as Hyper
import Hyper.Request as Hyper
import Hyper.Status as Hyper
import Hyper.Middleware as Hyper
import Hyper.Conn as Hyper
import Node.Encoding as NodeBuffer
import Node.Buffer as NodeBuffer
import Data.Function.Uncurried as Functions
import Effect.Uncurried as Effect.Uncurried
import Node.Path as Path
import Node.HTTP as NodeHttp
import Data.Newtype as Newtype
import Debug.Trace as Debug
type ForeignMiddleware = Functions.Fn3 NodeHttp.Request NodeHttp.Response (Effect Unit) (Effect Unit)
mkMiddlewareFromForeign
:: forall c
. ForeignMiddleware
-> Hyper.Middleware
Aff
(Hyper.Conn Hyper.HttpRequest (Hyper.HttpResponse Hyper.StatusLineOpen) c)
(Hyper.Conn Hyper.HttpRequest (Hyper.HttpResponse Hyper.ResponseEnded) c)
Unit
-> Hyper.Middleware
Aff
(Hyper.Conn Hyper.HttpRequest (Hyper.HttpResponse Hyper.StatusLineOpen) c)
(Hyper.Conn Hyper.HttpRequest (Hyper.HttpResponse Hyper.ResponseEnded) c)
Unit
mkMiddlewareFromForeign foreignMiddleware (Hyper.Middleware app) = Hyper.Middleware $ \conn ->
makeAff \cb -> do
let (Hyper.HttpRequest (nodeHttpRequest :: NodeHttp.Request) _requestData) = conn.request
(Hyper.HttpResponse nodeHttpResponse) = conn.response
onNext = do
Debug.traceM "traceM: before calling cb"
runAff_ cb (app conn)
Debug.traceM "traceM: after calling cb"
Debug.traceM "traceM: before calling foreignMiddleware"
Functions.runFn3 foreignMiddleware nodeHttpRequest nodeHttpResponse onNext
Debug.traceM "traceM: after calling foreignMiddleware"
pure nonCanceler
foreign import _testMiddleware :: ForeignMiddleware
testMiddleware
:: forall c
. Hyper.Middleware
Aff
(Hyper.Conn Hyper.HttpRequest (Hyper.HttpResponse Hyper.StatusLineOpen) c)
(Hyper.Conn Hyper.HttpRequest (Hyper.HttpResponse Hyper.ResponseEnded) c)
Unit
-> Hyper.Middleware
Aff
(Hyper.Conn Hyper.HttpRequest (Hyper.HttpResponse Hyper.StatusLineOpen) c)
(Hyper.Conn Hyper.HttpRequest (Hyper.HttpResponse Hyper.ResponseEnded) c)
Unit
testMiddleware = mkMiddlewareFromForeign _testMiddleware
main :: Effect Unit
main =
let
app = IndexedMonad.do
Debug.traceM "traceM: app is called"
Hyper.writeStatus Hyper.statusOK
Hyper.headers []
Hyper.respond "<h1>Hello from app!</h1>"
app' = app # testMiddleware
in Hyper.runServer Hyper.defaultOptionsWithLogging {} app'
const testMiddlewareEndsResponse = function(req, res, nxt) {
console.log('testMiddlewareEndsResponse')
console.log('Outside')
console.log('Request Type:', req.method)
return function() {
console.log('Inside')
console.log('Request Type:', req.method)
console.log('Ending reponse')
res.writeHead(200);
res.end('<h1>Hello from testMiddlewareEndsResponse!</h1>')
}
}
const testMiddlewareCallsNext = function(req, res, next) {
console.log('testMiddlewareCallsNext')
console.log('Outside')
console.log('Request Type:', req.method)
return function() {
console.log('Inside')
console.log('Request Type:', req.method)
console.log('calling next')
next()
}
}
exports._testMiddleware = testMiddlewareCallsNext examle1 output
examle2 output
|
Being able to reuse existing express middleware would be really nice in hyper. As it currently is, nodejs apps using express that are transitioning to purescript wouldn't be able to switch to hyper immediately because all of their infrastructure would be bound to the express middleware. They would be forced to either use the express wrapper (not ideal) or leave it as is (even worse).
I think hyper middleware should be completely compatible with express middleware as far as I can tell. What needs to be done to get this adapter going?
The text was updated successfully, but these errors were encountered: