graphql-zeus has become my favorite GraphQL client in large part to not having to constantly run code generation every time I write a new query, only when my schema changes. Queries are simple and type safe:

const response = await client('query')({
    tags: [{
        where: {
            site_id: {
                _eq: siteId
            }
        }
    }, {
        id: true,
        name: true
    }]
});

There's a lot of TypeScript wizardry going on behind the scenes to make type safety a reality. If you've ever opened up the generated zeus.ts file, you know what I'm talking about.

While it is obvious how to create a new instance of Chain to run queries, it's not obvious how to possibly pass the client around to functions to share an instance when needed. I mean, look at this:

export const Thunder = (fn: FetchFunction) => <
  O extends keyof typeof Ops,
  R extends keyof ValueTypes = GenericOperation<O>
>(
  operation: O,
) => <Z extends ValueTypes[R]>(o: Z | ValueTypes[R], ops?: OperationOptions) =>
  fullChainConstruct(fn)(operation)(o as any, ops) as Promise<InputType<GraphQLTypes[R], Z>>;

TypeScript makes this very simple to do, thanks to ReturnType. Define your own type by defining the following somewhere shared outside your generated client:

import { Chain } from "zeus";

export type GraphQLClient = ReturnType<typeof Chain>;

Then use this type whenever you need to pass or return Chain:

const getTaggedPosts = ({
  client
}: {
  client: GraphQLClient;
}) => {
  // run query here using `client`
}

It's possible something like this is built in but I couldn't find it after a little digging.

via GIPHY