gcanti / fp-ts-routing

A type-safe bidirectional routing library for TypeScript

Home Page:https://gcanti.github.io/fp-ts-routing/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Ability to extract the structure of the Match

sledorze opened this issue · comments

fp-ts-routing is very handy but when one want to extract a string representation, says in the form of : /foo/:fooId/bar/:barId for integration with a framework or /foo/{fooId}/bar/{barId} for another one, there's no way to extract a custom representation from it.

@gcanti would that be in the scope of this lib to expose the structure of the Match to enable further integration with third partys?

If not I'm not sure how we can extract that information from fp-ts-routing non intrusively..

says in the form of : /foo/:fooId/bar/:barId for integration with a framework

That's interesting, what kind of integration?

@gcanti integration with HapiJs routes (pre 17 flavor) and then the Express one when we'll migrate to it (hopefully soon).
AFAIK hyper-ts does not integrate fp-ts-routing, that would be interesting to see.

My thinking here is that we could build (in the same way as the formatter / parser) a generic representation of the actual shape of the route.

This then may be used in various ways by third parties.

There is a tension between the openness of combinators that can be created and a fixed representation.

That's the point to discuss, I guess.
I'm thinking that the structure of the generic representation may involves the actual Matchers, tagged and keeping some information for introspection.
That would mean that for instance the type combinator would expose the io-ts Type.

@gcanti Do you have another alternative?

integration with HapiJs routes (pre 17 flavor) and then the Express one

@sledorze express contains its own router, not sure how you'd use both.

To me using fp-ts-routing with express would mean to ignore its built-in router and go for something along the lines of

import * as express from 'express'
import { lit, int, end, Route, parse, zero } from '../src'

const home = end
const foobar = lit('foo')
  .then(int('fooId'))
  .then(lit('bar'))
  .then(int('barId'))
  .then(end)

type Location = { type: 'home' } | { type: 'foobar'; fooId: number; barId: number } | { type: 'notfound' }

const fptsRouter = zero<Location>()
  .alt(home.parser.map(() => ({ type: 'home' as 'home' })))
  .alt(foobar.parser.map(({ fooId, barId }) => ({ type: 'foobar' as 'foobar', fooId, barId })))

const expressRouter = express.Router()
expressRouter.get('*', (req, res) => {
  const route = Route.parse(req.originalUrl)
  const location = parse(fptsRouter, route, { type: 'notfound' as 'notfound' })
  res.send(location)
})

const app = express()
app.use(expressRouter)
app.listen(3000, () => console.log('Express listening on port 3000. Use: GET /'))

Care to elaborate? What kind of integration are you thinking about?

@gcanti today we use hapiJs, so my first step would be to generate routes for HapiJs, then migrate to Express.

I see two issues:

  • Using a catchall route means we are no more able to use different middlewares setup by routes AFAIK.
  • HapiJs do not allow catchall routes without loosing the automatic Validation (injected from io-ts definitions) and corresponding error handling.

I actually like the approach you proposed and to think of it as a target, problem is the work to get there - I'll think about it (including what would that means on the middleware side of things).

How fast a pure fp-ts-routing router compares to existing one with, say, a hundred of routes? (I have the feeling we may need some kind of 'common prefix matching simplification')

Oh another good reason, to consider providing a way to represent the Matching structure is that it enables this common prefix matching simplification.

Which may lead to a (very) much faster router.