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

Easier way to add groups of methods #418

Closed
davedelong opened this issue Apr 19, 2024 · 5 comments
Closed

Easier way to add groups of methods #418

davedelong opened this issue Apr 19, 2024 · 5 comments

Comments

@davedelong
Copy link

I'm playing around with the 2.x.x branch and am wishing there were an easier way to add groups of methods.

Currently I essentially have to list out all my methods like so:

let router = Router()

router.get("/") { ... }
router.get("/home") { ... }
router.get("/home/page1") { ... }
router.get("/home/page2") { ... }
router.get("/about") { ... }
router.get("/info") { ... }
... // etc

I can break this up into function an extensions, but it's all fundamentally this structure.

I would like the ability to group methods, perhaps using something like:

let router = Router()
router.add(PublicEndpoints.self)
router.add(UserEndpoints.self)
router.add(AdminEndpoints.self)
@davedelong
Copy link
Author

I'm not entirely sure what the shape of this should be… I've been playing around with prototyping this myself and haven't found something I'm happy with. I just know that I'm not happy with having to list everything out. 😅

@adam-fowler
Copy link
Member

adam-fowler commented Apr 20, 2024

Up until now I've always done it as follows

let router = Router()
UserController().addRoutes(to: router.group("user"))

struct UserController {
    func addRoutes(to group: RouterGroup<some BaseRequestContext>) {
        group.post("signup") { ... } 
        group.post("login") { ... }
    }
}

But I can see how people might prefer to create a RouteCollection they can pass around.

let router = Router()
router.add("user", UserController().routes)

struct UserController<Context: BaseRequestContext> {
    var routes: RouteCollection<Context> {
        let routes = RouteCollection<Context>
        routes.post("signup") { ... } 
        routes.post("login") { ... }
        return routes
    }
}

@adam-fowler
Copy link
Member

adam-fowler commented Apr 20, 2024

Interestingly the experimental result based Router in HummingbirdRouter allows you to do this now. This is from the webauthn example in hummingbird-examples

let router = RouterBuilder(context: WebAuthnRequestContext.self) {
    // add logging middleware
    LogRequestsMiddleware(.info)
    // add file middleware to server HTML files
    FileMiddleware(searchForIndexHtml: true, logger: logger)
    // health check endpoint
    Get("/health") { _, _ -> HTTPResponse.Status in
        return .ok
    }
    HTMLController(
        mustacheLibrary: library,
        fluent: fluent,
        sessionStorage: sessionStorage
    ).endpoints
    RouteGroup("api") {
        HBWebAuthnController(
            webauthn: .init(
                config: .init(
                    relyingPartyID: "localhost",
                    relyingPartyName: "Hummingbird WebAuthn example",
                    relyingPartyOrigin: "http://localhost:8080"
                )
            ),
            fluent: fluent,
            sessionStorage: sessionStorage
        ).endpoints
    }
}

Notice I add the HTMLController and HBWebAuthnController endpoints directly. You can add anything that conforms to RouterMiddleware,

@adam-fowler
Copy link
Member

#421 Solves this to some degree in that it provides an object you can pass around that holds a collection of router endpoints.

If you want to do what you have above you could add a protocol

protocol RouteCollectionBuilder {
    associatedtype Context: BaseRequestContext
    var routes: RouteCollection { get }
}

And extend RouterMethods which in effect extends Router and RouterGroup

extension RouterMethods {
    public func add<Collection: RouteCollectionBuilder>(_ path: String = "", routes collection: Collection) where Collection.Context == RouterMethods.Context {
        add(path, routes: collection.routes)
    }
}

Then you have

struct PublicEndpoints: RouteCollectionBuilder {
    var routes: RouteCollection {
        let routes = RouteCollection()
        routes.get { ... }
        routes.put("path") { ... } 
    }
}
router.add(PublicEndpoints())

Then of course you can hide a load of this behind macros.

@adam-fowler
Copy link
Member

Closing as #421 is now out.

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

No branches or pull requests

2 participants