Closed astampoulis closed 6 years ago
Here are my thoughts on these:
wrt. the HypotheticalMiddleware
-- I see what you're saying, but I think let's cross that bridge if we get there. Maybe there's a way to adapt the HKT encoding to do that? Do you have an example of a middleware that would behave like that? My guess would be that most middleware just 'adds' to the input type rather than impose constraints on it/do transformations on it. I do think that we won't need a large number of middleware -- we'll need something for capture params, something for specifying outputs probably (e.g. JSON encoding vs. something else), maybe something for setting a route to be GET or POST eventually.. What else do you have in mind?
(BTW -- I think we might need to revisit the name middleware. It does translate to middleware on the server, but not on the client necessarily. What do you think of "endpoint combinators"? Or something like that.)
wrt. the client visitor result type -- the change was mostly motivated because I was trying to do capture params as an example where the HKT encoding was needed, and I made the switch there, and I thought it's a switch worth doing anyway. For capture params, needing the input to generate the data is much cleaner, I think: in the alternative, the intermediary structure would need to have the url
be a function, which given the input
parameter would return the actual URL, with the captured parameters substituted out. (Or something of the sort). I moved the change to this PR so that the HKT encoding does get used and tested.
Since the visitor result is mostly contained within the module that does the wrapping of a package (here, isomorphic-fetch
), I think we can treat it as an implementation detail and we should thus go with the representation that makes that process easier. It might make sense to write unit tests for the result of the visitor as well (e.g. to check that the right URL gets generated), but I think we should be able to do that with both representations anyway.
With regards to the Client visitor, I agree that the function type makes it easier to implement and we should pick whatever is easier, since that's opaque to the library user anyways.
Yeah, I think sticking to adding inputs might be a fair assumption for now. It forces our library to be more declarative. If we support arbitrary input transformations then the library might be too powerful and therefore less "analyzable".
I'm fine renaming Middleware
to something else, like Combinator
now that we've transposed the logic to Visitors.
This changes the base implementation of endpoints and middleware to allow for a generic visitor pattern, instead of hardcoding the specific
mapServerData
andmapClientData
methods we used to have. That way we can separate the definition of endpoints and middleware from the definitions of visitors/"interpreters" of those types, which are defined by pattern matching on all the various cases.As a result we could have the following package structure:
safe-api-base
is where all the base definitions of endpoints are done, which basically give a well-typed alternative to using the string type for URLs (and also specifying query parameters etc.)safe-api-fetch
andsafe-api-koa
, which define their own interpreters/visitors of the base types, giving well-typed versions of their basic methodsThis does introduce a bit of complexity, as it requires the use of higher-kinded types for typing the generic
visit
function correctly and maintaining all type information. (I have added an explanation in the code about how higher-kinded types are encoded in Flow. An example of such a type we have used already is$ObjMap<Keys, F>
--F
is a type-level function transforming the types of values of an object, and therefore has the higher kindType -> Type
.) I do think the extra complexity is well-warranted though; we have discussed with @sleexyz that being able to define new interpreters of the endpoint constructors is necessary in order to be able to add wrapper APIs for more libraries as independent NPM packages.Closes #21 .