ketting icon indicating copy to clipboard operation
ketting copied to clipboard

Rethinking end-user fluid APIs

Open evert opened this issue 1 year ago • 0 comments

There's a lot of cases where users will want to chain multiple commands, one obvious pattern we support is:

const res = await resource.follow('a').follow('b')

This works because .follow() returns a special promise (FollowPromise) that can be awaited, but also chain because that promise itself also has a .follow() function.

One issue is that often users will want to also do other operations after a follow, for example:

const state = await resource.follow('a').follow('b').get();

This is not possible, because the last .follow() must be awaited first:

const state = await (await resource.follow('a').follow('b')).get();

See #479

One solution is to add .get() on FollowPromise as well, but then the question is, should we duplicate most of the Resource api on FollowResource, and is this going to be confusing because sometimes you might be dealing with a Resource, and other times with a FollowResource, which might have slightly different behavior.

Similarly, when doing a POST request, you might want to get access to the resulting Resource (and use postFollow()) or get access to the resulting State (and use post()), but the Action features don't have an equivalent submitFollow(), see : #481'

Also this feature only exists for POST and not for example PATCH.

There's also #478, which I think is also vaguely a symptom of the same problem.

Ultimately I think this library can really benefit from a fully chainable API similar to what you might find in query builders. We should try to come up with a consistent API which less duplication that lets you chain without restrictions until the statement gets terminated.

Extreme example:

  await res
    .follow('author')
    .follow('article')
    .form('new-article')
    .submit({title: 'Hello world'})
    .follow() // Follows the Location header
    .get()

Every intermediate step is potentially a HTTP request, if not cached. This is kind of an ideal example, so one question would still be to users: "Will this makes sense". One thing in particular I find a bit contentious is that there's always a difference between 'State' and 'Resource' objects, and they have a lot in common.

Anyway, this is issue is a rough idea for a future. No immediate plans to implement but I think it may be helpful to collect some feedback here and also find more examples that could be solved better wiht this kind of fluid API.

evert avatar Sep 22 '24 21:09 evert