Skip to content

Conversation

@remidewitte
Copy link

It is very convenient to type relations when we follow links.

@evert
Copy link
Collaborator

evert commented Mar 16, 2023

Hey, generally a good idea. Been sitting on this a bit. The feature I really want is something like this:

type Article = State< { title: string}, {
    author: Author
}>;

The idea here is that if I later on have a Resource

, and I call await article.follow('author');, Typescript would know that I got a Resource<Author> as a result.

A next step after that is that we can start writing an OpenAPI-like document that lets us generate types and understand what kind of resources we are getting as we're traversing the graph.

So this PR is a step in the right direction I think, but there's a lot more to be done, I don't want to introduce this and potentially alter it after.

P.S. If you ever want to chat, I'm on matrix: @evert:badgateway.net and we could do a ketting room =)

@remidewitte
Copy link
Author

Thanks for your answer. Let me add a bit more context !

What we (Synako) really use is a hook like this. I would be happy to make a PR as well to react-ketting :

export interface UseResourceResponse<T, L extends string = never> {
    useFollow: <TFollow, LFollow extends string = never>(rel: L, variables?: Partial<LinkVariables>) => UseResourceResponse<TFollow, LFollow>;
    useFollowCollection: <TFollow>(rel: L, variables?: Partial<LinkVariables>, options?: UseCollectionOptions) => UseCollectionResponse<TFollow>;
    useFollowAll: <TFollow>(rel: L, variables?: Partial<LinkVariables>) => UseResourcesResponse<TFollow>;
    deleteResource: () => Promise<void>;
    loading: boolean;
    error: Error | null;
    resource: Resource<T>;
    data?: T;
    links?: Links<L>;
}

export default function useResource<T, L extends string = never>(
    resourceLike: RootRels | Resource<T> | FollowPromiseOne<T> | State<T>,
    variables?: Partial<LinkVariables>,
    options?: UseResourceOptions<T>,
): UseResourceResponse<T, L> {

... implementation based on useReadResource...

}

Which results in the usage with :

const {data: article, useFollow} = useResource<Article, links.Article>(resourceLike);
const {data: author, useFollowCollection} = useFollow<Author, links.Author>('author');
const {items: otherArticles} = useFollowCollection<Article>('other_articles');

where ...

interface Article {
 ...
}
interface Author {
 ...
}
export namespace links {
  export type Article = 'author' | 'section';
  export type Author = 'other_articles' | 'belongs_to';
}

We derive all types from OpenAPI schema :

  • State data interfaces T in State<T>
  • Link names by state Rels in State<T, Rels>
    Example :
title: Article
type: object
properties:
  title:
    type: string
  _links:
    type: object
    properties:
      author:
        $ref: ../util/link.yaml
      section:
        $ref: ../util/link.yaml

What we really miss is a ketting native way for type-safe links to avoid typos while we traverse the graph. We have not found a way yet to explain in OpenAPI the target type of a link but we don't really suffer from this as it is quite easy to get the target type thanks to auto-completion.

We have found the approach to be very flexible and especially opt-in.

I'll try to think a bit more about you really want !

@evert
Copy link
Collaborator

evert commented Mar 16, 2023

Cool, based on this I think the feature I would like would still work for your use-case. I hope I can spend some time of this soon. I would feel bad if you did a bunch of work but it doesn't fit with the goals i have for this.

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

Successfully merging this pull request may close these issues.

2 participants