Houdini GraphQL Framework

repository·main·Indexed 21 days ago

https://github.com/houdinigraphql/houdini

A compile-time GraphQL framework that optimizes for minimal bundle size by shifting runtime logic to a build step. It features colocated data requirements, a normalized cache, automatic type generation, and support for Svelte and React. The ecosystem includes the create-houdini CLI, a dedicated VS Code extension for GraphQL intelligence via houdini-lsp, and a Svelte CLI add-on (@houdinigraphql/sv).

Tokens
110.3K
Snippets
378
Records
467
Agent score
76%

What's inside Houdini

  1. Overview of Houdini GraphQL Framework

    main

    Houdini is a GraphQL framework designed to provide a high-quality developer experience by shifting traditional runtime logic into a compile step. This approach results in an incredibly lean GraphQL abstraction for applications, minimizing bundle size while providing powerful features.

    Key Features:

    • Composable and Colocated Data Requirements: Define data needs directly alongside your components.
    • Normalized Cache: Includes declarative updates for managing application state.
    • Generated Types: Automatic type generation for your GraphQL schema and queries.
    • Subscriptions: Support for real-time data updates.
    • Pagination: Supports both cursor-based and offset-based pagination.
  2. Overview of Houdini GraphQL

    main
    Houdini is a GraphQL client designed to provide a high-quality developer experience while maintaining minimal bundle sizes. It follows a philosophy similar to Svelte by shifting traditional runtime logic into a compile step. This allows Houdini to generate a lean GraphQL abstraction for your application, avoiding the bloat typically associated with GraphQL runtimes.
  3. Features of Houdini GraphQL intelligence

    main

    The Houdini GraphQL extension provides deep integration with the Houdini compiler, offering features beyond standard GraphQL support:

    • Diagnostics as you type: Live enforcement of Houdini compiler rules, including @paginate constraints, list operations, and per-spread @with arguments.
    • Completions: Intelligent suggestions for schema fields, directives, project fragments (including generated list operations like *_insert), fragment arguments inside @with(...), and list filters inside @when(...). Required arguments are prioritized.
    • Inline documents: Full support for GraphQL syntax inside graphql( ... ) calls and GraphQL< ... > props within .ts, .js, and .svelte files.
    • Hover & go-to-definition: Access schema field documentation on hover and jump to the definition of fragment spreads.
    • Syntax highlighting: Support for .gql and .graphql files as well as inline documents.
  4. Structure and organization of Houdini documentation

    main

    The Houdini documentation site is organized into framework-specific directories and shared content sections:

    • svelte/: Contains documentation for houdini-svelte.
    • react/: Contains documentation for houdini-react.
    • shared/: Contains sidebar sections that are automatically appended to the bottom of every framework's sidebar. These sections are injected using numeric prefixes starting at 100-, 101-, etc., to ensure they follow framework-specific content.
    • _partials/: Contains MDX partials used for component reuse within doc pages.
  5. Overview of the Houdini Cache API

    main

    The Cache API provides programmatic access to Houdini's runtime cache. It is intended as an advanced escape hatch for situations where automatic cache updates or list operation fragments are insufficient. Use it to manually read, write, subscribe to, or manage data outside of standard document stores.

    Warning: If your business logic relies heavily on this API, consider opening an issue or discussion on GitHub to improve Houdini's core capabilities.

  6. Implement onSignIn to create a session

    main

    The onSignIn function in your server configuration is responsible for transforming a verified provider user into your application's session object.

    Best Practices:

    • Use user.sub for identity: Always use user.sub (the provider's stable, unique ID) to look up or create accounts. Do not rely on user.email, as it may be undefined if the provider has not marked the email as verified.
    • Keep sessions opaque: The object returned by onSignIn is what gets stored in the session cookie. It should contain only an opaque reference (like a userId) and never the provider's raw tokens.
    • Store tokens securely: If you need to call the provider's API later, store the tokens in your own database, not in the session cookie.
    onSignIn: async ({ user, tokens }) => {
        // save the user to the database, etc.
        return { userId: user.sub } 
    },
  7. Control field visibility with Fragment Masking

    main

    Fragment masking ensures components only access fields they explicitly declared, preventing them from accidentally using fields pulled in by sibling fragments.

    By default, masking is enabled. You can:

    1. Disable globally: Set defaultFragmentMasking: "disable" in your Houdini configuration.
    2. Disable per-fragment: Use the @mask_disable directive on a specific fragment to make its fields accessible directly on the parent object.
    query CurrentUser {
      me {
        uuid
        ...UserProfile @mask_disable
        ...UserMeta
      }
    }
    # With @mask_disable, UserProfile fields are accessible on 'me' alongside 'uuid'.
  8. Use the @loading directive for predictable loading states

    main

    The @loading directive allows you to define a predictable data shape for fields while a query is in flight. Instead of receiving null or undefined, fields marked with @loading are replaced with pending placeholders.

    Houdini processes queries top-down. Intermediate nodes retain their original type (objects remain objects, lists remain lists), and the deepest field tagged with @loading becomes the actual pending value. This allows you to safely iterate over lists or access object properties even before the data has arrived.

    query ShowList {
      shows @loading {
        title
      }
    }
  9. Define loading state shapes with the @loading directive

    main

    The @loading directive allows you to define the specific shape of your data while a network request is pending. Instead of the entire query result being null or undefined, Houdini populates the data object with the structure you've defined, using a special sentinel value PendingValue to mark fields that haven't loaded yet.

    Single Directive

    If you place @loading at the top level of a field, that entire field will be set to PendingValue while fetching:

    query SpeciesInfo($id: Int = 1) {
      species(id: $id) @loading {
        name
        description
      }
    }

    In this case, data.species will be PendingValue during the fetch.

    Granular/Composed Loading States

    You can use @loading on multiple nested fields to create a more granular loading state. Houdini will walk down the query and set the deepest fields tagged with @loading to PendingValue. This ensures that intermediate objects and lists are always available and safe to access, even during a fetch.

    For lists, you can use @loading(count: N) to specify how many placeholder elements should be generated in the array while loading.

    query SpeciesInfo($id: Int = 1) {
      species(id: $id) @loading {
        name @loading
        description
        evolutionChain @loading(count: 3) {
          name
          ...Sprite_species
        }
        ...Sprite_species
      }
    }
  10. Compose fragments within fragments

    main

    Fragments can be nested, allowing you to build complex data requirements by composing smaller, reusable fragments. This enables a highly decoupled architecture where components can rely on other components' data requirements.

    Example of nesting: If ComponentA uses fragmentA and ComponentB uses fragmentB, ComponentB can include ...fragmentA inside its own fragmentB definition. When the parent query includes ...fragmentB, it automatically satisfies the requirements for both components.

    fragment SpeciesPreview on Species {
        id
        pokedexNumber
        name
    
        # Nesting an existing fragment
        ...SpriteInfo
    }
  11. Understand Houdini's Architecture

    main

    Houdini uses a compiler-first model designed to move as much work as possible out of the browser and into code generation. Instead of shipping a large GraphQL interpretation layer to the client, Houdini reads GraphQL documents at build time, validates them against your schema, and generates strongly typed runtime code and TypeScript types.

    The architecture consists of three main components:

    1. Houdini Client: The runtime library in your application that manages network requests, normalized caching, optimistic updates, pagination, and subscriptions.
    2. Code Generation: The compiler pipeline that parses GraphQL documents, validates them, and writes the resulting artifacts into the $houdini directory.
    3. Framework Plugins: Integrations (like Svelte or React plugins) that transform the generated artifacts into framework-native APIs.

    By using this model, the GraphQL document serves as the single source of truth, and all types, cache metadata, and framework wrappers are derived from it.

    <script lang="ts">
      import { graphql } from '$houdini'
    
      const UserList = graphql(`
        query UserList {
          users {
            name
          }
        }
      `)
    
      // trigger UserList.fetch() somewhere
    </script>
    
    {$UserList.data?.users?.map((user) => user.name).join(', ')}