Search code examples
asp.net-core-webapiasp.net-web-api-routingf#-giraffe

Best practice for specifying the routing in a big application


I am very new to Giraffe.

Assuming we have a big app with lots modules and pages (i.e. tens of web pages and hundreds or thousands of web api actions), what is the best way to specify the routing without creating a mess?

As an example, we have these business modules (let's say we can map them to subfolders with the same names):

  • HR
    • Employees
      • Display Page
      • CRUD actions:
        • Add Employee
        • Update
        • Remove
      • Get reference data actions
    • Postings ...
  • Payroll ...
  • Admin ...

The routing examples here are minimalistic: https://github.com/giraffe-fsharp/Giraffe/blob/master/DOCUMENTATION.md#routing. Usually, applications can have big routing tables. I assume that we will have to have a subroute for each module.

Thanks


Solution

  • I'm not aware of "best practices" discussed in the community (if I were you I'd also go to the F# Slack and start the discussion about this topic in the #web channel) but I usually stick to composing the application routes from module specific routers.

    At the top-level I tend to have very general routes (error routes, OIDC logout, etc) as well as top module routers

    let webApp =
        choose [
            route "/error" >=> handleError
            route "/logout" >=> logout
            moduleARoutes
            moduleBRoutes
        ]
    

    A module route could look like this

    let moduleARoutes : HttpHandler =
        subRoute "/api/moduleA"
            authorize >=> choose [
                GET >=>
                    choose [
                        routef "/%O" handleGet
                        routef "/%O/things" handleGetThings
                    ]
    
                POST >=>
                    choose [
                        routef "/%O" handleCreate
                        routef "/%O/things" handleThingCreation
                    ]
    
                subModuleA1Routes
            ]
    

    Submodules and other modules on the same level as moduleARoutes are done exactly the same way.

    The only thing you have to be very careful about is where you compose web-parts that handle special processing like authorization and authentication. I personally like to handle it on the basis of the top modules but this is very taste and use-case specific.

    All-in-all you have a lot of freedom and choice with Giraffe route definitions - everything is fully composable. One caveat could be performance though - I'm not entirely sure how your routing design (especially in an enormous app like you describe) impacts endpoint resolution. I'd experiment, measure and adjust accordingly.