genql

repository·master·Indexed 21 days ago

https://github.com/remorses/genql

A type-safe GraphQL query builder for TypeScript that allows developers to write queries as JavaScript objects. It eliminates the need for manual GraphQL string manipulation and the graphql runtime dependency. Key features include full TypeScript auto-completion, scalar fetching via __scalar: true, and support for various environments including Browser, Node, Deno, Cloudflare Workers, and Bun. The @genql/cli package is used to generate a type-safe client from a GraphQL schema file or a live endpoint.

Tokens
18.6K
Snippets
63
Records
85
Agent score
76%

What's inside genql

  1. Core features of Genql

    master

    Genql is a type-safe GraphQL query builder designed for speed and safety. Key features include:

    • Type Safety: Full TypeScript auto-completion and validation for queries.
    • Zero Dependencies: Does not require the graphql package or runtime query parsing.
    • Scalar Fetching: Easily fetch all scalar fields in a type using __scalar: true.
    • Client Agnostic: Works with any fetcher or client (e.g., Apollo, Relay).
    • Advanced Support: Built-in support for Subscriptions and query batching.
    • Environment Support: Runs in Browser, Node, Deno, Cloudflare Workers, Bun, and more.
  2. Avoid using `QueryRequest` when extracting `QueryResult`

    master
    When you intend to use QueryResult<typeof fields> to extract a type, do not explicitly type the fields object with QueryRequest. If you do, TypeScript will treat fields as the generic QueryRequest type rather than the specific selection object, causing you to lose the specific type information of the selected fields in the resulting QueryResult.
  3. Configure module output (ESM vs CJS)

    master

    By default, genql generates CommonJS code using require and module.exports. You can change the output format using the following flags:

    • --esm: Generates only ES modules (imports/exports). Recommended when using bundlers like Webpack to enable tree shaking.
    • --esm-and-cjs: Generates both ESM and CommonJS code. Recommended when publishing a package intended for both browser and Node.js environments.
    # Generate only ESM
    genql --esm --schema ./schema.graphql --output ./generated
    
    # Generate both ESM and CJS
    genql --esm-and-cjs --schema ./schema.graphql --output ./generated
  4. Generate the client from an HTTP endpoint

    master

    Use the --endpoint flag to provide the URL of a GraphQL HTTP endpoint. By default, genql performs a schema introspection via a POST request. If you need to use a GET request for the introspection, include the --get flag.

    # Standard POST introspection
    genql --endpoint https://countries.trevorblades.com --output ./generated
    
    # Using GET for introspection
    genql --get --endpoint https://countries.trevorblades.com --output ./generated
  5. Use genql in React with SWR

    master

    Since genql methods return Promises, you should use a query manager like swr to handle data fetching in React applications.

    Important TypeScript Note: You must declare the fetcher function separately (outside the component or as a named function) to ensure TypeScript completion works correctly. Defining the fetcher inline may break type inference.

    import React, { useState } from 'react'
    import useSWR from 'swr'
    import { createClient, everything } from './generated'
    
    const client = createClient()
    
    const Page = () => {
        const [filter, setFilter] = useState('.*')
    
        // IMPORTANT: declare the fetcher separately for TypeScript completion to work
        const fetcher = (filter) =>
            client.query({
                countries: [
                    { filter: { continent: { regex: filter } } },
                    { name: 1, code: 1, languages: { ...everything } },
                ],
            })
    
        const { data, error } = useSWR([filter], fetcher)
        return <div>{JSON.stringify(data)}</div>
    }
  6. Use genql in React with React Query

    master

    To use genql with react-query, pass a genql client.query call within a fetcher function.

    Important TypeScript Note: You must declare the fetcher function separately (outside the component or as a named function) to ensure TypeScript completion works correctly. Defining the fetcher inline may break type inference.

    import React, { useState } from 'react'
    import { useQuery } from 'react-query'
    import { createClient, everything } from './generated'
    
    const client = createClient()
    
    const Page = () => {
        const [regex, setRegex] = useState('.*')
    
        // IMPORTANT: declare the fetcher separately for TypeScript completion to work
        const fetcher = (_, regex) =>
            client.query({
                countries: [
                    { filter: { continent: { regex: regex } } },
                    { name: 1, code: 1 },
                ],
            })
    
        const { data, error } = useQuery(['countries-key', regex], fetcher)
        return <div>{JSON.stringify(data)}</div>
    }
  7. Manual steps for managing GraphQL APIs

    master

    The scraper workflow involves several manual intervention steps to manage the lifecycle of discovered APIs:

    1. Discovery: Run pnpm discover to find new URLs and add them to the CSV.
    2. Selection: Manually add a slug to the APIs in the CSV that you want to target for package generation.
    3. Verification: Run pnpm publish:dry to inspect the generated package folder and ensure the queries and metadata are correct.
  8. Change headers at runtime

    master

    To support dynamic headers (such as authentication tokens stored in localStorage), pass a function to the headers field in the createClient options. This function is executed at query time to retrieve the latest header values.

    import { createClient } from './generated_dir'
    
    const client = createClient({
        url: 'http://your-api',
        headers: () => ({
            Authorization: localStorage.get('authToken'),
        }),
    })
  9. Use the chain syntax for executing queries

    master

    Genql provides a chain mode as an alternative to the standard object-based query syntax. In chain mode, you access fields using dot notation.

    If a field requires arguments, you call it like a function. After navigating the field path via dot notation, you must call the .get() method to actually execute the request and fetch the data.

    For example, to fetch specific fields from a query result, pass an object containing the desired fields to .get().

    import { createClient } from './generated'
    const client = createClient()
    
    // Accessing a single field after a mutation
    const name = await client.chain.mutation.createUser({ name: 'john' }).name.get()
    
    // Accessing multiple fields after a query with arguments
    const user = await client.chain.query
        .countries({ filter: { name: 'BG' } })
        .get({ name: true, code: true })