Skip to content
This repository was archived by the owner on Dec 25, 2025. It is now read-only.
This repository was archived by the owner on Dec 25, 2025. It is now read-only.

Abstract client interface  #123

@IzumiSy

Description

@IzumiSy

#76 is related to this issue.

Is your feature request related to a problem? Please describe.

Currently, fabrix internally sticks with urql to execute GraphQL queries.

However, it would be nicer for users to let them select a client library whatever they want to use.

Describe the solution you'd like

Abstract out the current implementation of data fetching into an interface like this:

type FetcherProps = {
  query: DocumentNode | string;
  variables?: Record<string, unknown>;
};

type FabrixRequestResult = {
  data: FabrixComponentData | undefined;
  error: Error | undefined;
};

interface FabrixClient {
  getClient(): {
    mutate: (props: FetcherProps) => Promise<FabrixRequestResult>;
    query: (props: FetcherProps) => Promise<FabrixRequestResult>;
  };

  getFetcherComponent(): (
    props: FetcherProps & {
      children: (props: { data: FabrixComponentData | undefined }) => ReactNode;
    },
  ) => ReactNode;
}

With this interface, we can add urql implementation:

export class URQLClient implements FabrixClient {
  private client: UrqlClient;

  constructor(private readonly url: string) {
    this.client = new UrqlClient({
      url,
      exchanges: [
        cacheExchange,
        removeDirectivesExchange(["fabrixView", "fabrixList", "fabrixForm"]),
        addTypenameFieldExchange,
        removeTypenameFromVariableExchange,
        fetchExchange,
      ],
    });
  }

  getClient() {
    return {
      mutate: async (props: FetcherProps) => {
        const r = await this.client.mutation<FabrixComponentData>(
          props.query, 
          props.variables,
        );
        return {
          data: r.data,
          error: r.error,
        };
      },

      query: async (props: FetcherProps) => {
        const r = await this.client.query<FabrixComponentData>(
          props.query, 
          props.variables,
        );
        return {
          data: r.data,
          error: r.error,
        };
      },
    };
  }

  getFetcherComponent() {
    return (
      props: FetcherProps & {
        children: (props: {
          data: FabrixComponentData | undefined;
        }) => ReactNode;
      },
    ) => {
      const [{ data, fetching, error }] = useQuery<FabrixComponentData>({
        query: props.query,
        variables: props.variables,
      });

      if (fetching || !data) {
        return <Loader />;
      }

      if (error) {
        throw error;
      }

      return props.children({ data });
    };
  }
}

I am feeling that this design potentially resolves #90 as well.

Users can create implementation with Suspense enabled, or do the same in getFetcherComponent.

Describe alternatives you've considered

N/A

Additional context

N/A

Metadata

Metadata

Assignees

Labels

featureNew feature or requestpackage:coreRelated to @fabrix-framework/fabrix

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions