AlexCouch / pluvo

Pluvo is a modern web framework purely in Gleam built ontop of mist
4 stars 0 forks source link

Middleware and context adapters #4

Closed AlexCouch closed 10 months ago

AlexCouch commented 10 months ago

Description

Middleware is a function that wraps route handlers and allows for customizing further the web server's behavior. Context Adapters allow for extending the capabilities of the context object that every route handler is given. Together, they provide a lot of power over one's backend such as easily calling and executing sql code, automatically provide custom headers, authentication, CORS, and much more.

Implementation

The implementation of middleware is more simple than one might think. A middleware is a function which wraps the route handler and is called in place of the route's handler. The middleware then does it needs to do and then calls the route handler itself.

In the case of gleam, we can utilize function capturing to return the middleware function from a factory.

import pluvo/middleware/context.{type MiddlewareContext} as mw_ctx
import pluvo/middleware.{type Middleware}
import psql/middleware/config.{type PsqlConfig}

pub fn psql(config: PsqlConfig) -> Middleware{
    //Init psql server with config here
    let sql_server = ...
    psql_middleware(sql_server, _)
}

pub fn psql_middleware(server: Psql, route: Route, ctx: Context) {
    //Connect to psql server
    ...
    ctx
    |> context.add_adapter(psql_adapter)
    |> route.method.handler
}

A middleware context would provide two things for the middleware: the route and the route's context. Whenever a middleware function is found. Using function capturing, a middleware factory function can return a new function of the type Middleware, whose signature is simply

pub type Middleware = fn(Route, Context) -> Response(ResponseData)

Note: We will have to eventually make our own response object to simplify response typing.

Then, in the app's main function, one can simply add the middleware to either a route, route group, or the entire app.

pub fn main(){
    let psql_mw = PsqlConfig() 
    |> psql.with_config

    let cors_mw = CORSConfig() |> cors.with_config

    let pluv = pluvo.new()
    |> pluvo.enable(cors_mw)

    //To a single route
    let routes = pluv
    |> pluvo.router
    |> router.get_enable("...", handler, psql_mw)

    let jwt_mw = JWTConfig() |> jwt.with_config

    let admin = pluv
    |> pluvo.group("admin")
    //Enable a middleware on the entire group
    |> group.enable(jwt_mwt)
    |> group.get()

    pluv
    |> pluvo.add_router(routes)
    |> pluvo.add_group(admin)
    |> pluvo.start(3000)
}
AlexCouch commented 10 months ago

Actually, I lied, I forgot to add enable to pluvo and not just the router for global middleware.