### Static Query Example (Good) Source: https://github.com/github-community-projects/graphql-client/blob/master/guides/dynamic-query-error.md Use static constants for queries to ensure they are parsed and validated at startup. This improves performance and catches validation errors early. ```ruby HeroNameQuery = SWAPI::Client.parse <<-'GRAPHQL' query($id: ID!) { hero(id: $id) { name } } GRAPHQL result = SWAPI::Client.query(HeroNameQuery, variables: { id: params[:id] }) ``` -------------------------------- ### Configure GraphQL Client with HTTP Adapter Source: https://github.com/github-community-projects/graphql-client/blob/master/README.md Set up the GraphQL client to connect to a GraphQL endpoint using the HTTP network adapter. This example configures the client for the SWAPI GraphQL Wrapper and includes custom headers. ```ruby require "graphql/client" require "graphql/client/http" # Star Wars API example wrapper module SWAPI # Configure GraphQL endpoint using the basic HTTP network adapter. HTTP = GraphQL::Client::HTTP.new("https://example.com/graphql") do def headers(context) # Optionally set any HTTP headers { "User-Agent": "My Client" } end end # Fetch latest schema on init, this will make a network request Schema = GraphQL::Client.load_schema(HTTP) # However, it's smart to dump this to a JSON file and load from disk # # Run it from a script or rake task # GraphQL::Client.dump_schema(SWAPI::HTTP, "path/to/schema.json") # # Schema = GraphQL::Client.load_schema("path/to/schema.json") Client = GraphQL::Client.new(schema: Schema, execute: HTTP) end ``` -------------------------------- ### GraphQL Response Example Source: https://github.com/github-community-projects/graphql-client/blob/master/guides/implicitly-fetched-field-error.md An example of a raw GraphQL response JSON, showing that both 'fullName' and 'location' were fetched. This highlights that the data is available in the response, but access is restricted by the client-side fragment definitions. ```json { "user": { "fullName": "Joshua Peek", "location": "Chicago" } } ``` -------------------------------- ### Dynamic Query Example (Bad) Source: https://github.com/github-community-projects/graphql-client/blob/master/guides/dynamic-query-error.md Avoid assigning parsed queries to local variables, as this can lead to the DynamicQueryError. Static constants are preferred for query definitions. ```ruby hero_query = SWAPI::Client.parse <<-'GRAPHQL' query($id: ID!) { hero(id: $id) { name } } GRAPHQL result = SWAPI::Client.query(HeroNameQuery, variables: { id: params[:id] }) ``` -------------------------------- ### Custom HTTP Adapter using Faraday Source: https://context7.com/github-community-projects/graphql-client/llms.txt Implement a custom HTTP adapter by creating an object that responds to `execute`. This example uses Faraday for flexibility in retries, logging, and middleware. The adapter must be configured before initializing the `GraphQL::Client`. ```ruby require "faraday" require "json" class FaradayAdapter def initialize(url, token:) @conn = Faraday.new(url: url) do |f| f.request :json f.response :json f.adapter Faraday.default_adapter end @token = token end def execute(document:, operation_name: nil, variables: {}, context: {}) response = @conn.post do |req| req.headers["Authorization"] = "Bearer #{context[:token] || @token}" req.body = { "query" => document.to_query_string, "operationName" => operation_name, "variables" => variables } end response.body rescue Faraday::Error => e { "errors" => [{ "message" => e.message }] } end end adapter = FaradayAdapter.new("https://api.example.com/graphql", token: ENV["API_TOKEN"]) Client = GraphQL::Client.new(schema: GraphQL::Client.load_schema(adapter), execute: adapter) ``` -------------------------------- ### Load GraphQL Schema from HTTP Adapter Source: https://context7.com/github-community-projects/graphql-client/llms.txt Load the GraphQL schema from a live remote endpoint using an HTTP adapter. This performs an introspection query and is useful for initial setup. ```ruby require "graphql/client" require "graphql/client/http" HTTP = GraphQL::Client::HTTP.new("https://api.example.com/graphql") # Option 1: Load live from a remote endpoint (makes a network request) Schema = GraphQL::Client.load_schema(HTTP) # Option 2: Load from a cached JSON file on disk (recommended for production) # Schema = GraphQL::Client.load_schema("db/schema.json") # Option 3: Load from a GraphQL SDL string/file # Schema = GraphQL::Client.load_schema("path/to/schema.graphqls") puts Schema.query.graphql_name # => "Query" ``` -------------------------------- ### Handle Execution Errors in Rails Controller Source: https://github.com/github-community-projects/graphql-client/blob/master/guides/handling-errors.md This example demonstrates handling both successful data retrieval and various error scenarios within a Rails controller. It checks for nil data, specific field errors, and top-level errors. ```ruby class IssuesController < ApplicationController ShowQuery = FooApp::Client.parse <<-'GRAPHQL' query($id: ID!) { issue: node(id: $id) { ...Views::Issues::Show::Issue } } GRAPHQL def show # Always returns a GraphQL::Client::Response response = FooApp::Client.query(ShowQuery, variables: { id: params[:id] }) # Response#data is nullable. In the case of nil, a well behaved server # will populate Response#errors with an explanation. if data = response.data # A Relay node() lookup is nullable so we should conditional check if # the id was found. if issue = data.issue render "issues/show", issue: issue # Otherwise, the server will likely give us a message about why the node() # lookup failed. elsif data.errors[:issue].any? # "Could not resolve to a node with the global id of 'abc'" message = data.errors[:issue].join(", ") render status: :not_found, plain: message end # Parse/validation errors will have `response.data = nil`. The top level # errors object will report these. elsif response.errors.any? # "Could not resolve to a node with the global id of 'abc'" message = response.errors[:issue].join(", ") render status: :internal_server_error, plain: message end end end ``` -------------------------------- ### Initialize GraphQL Client Source: https://context7.com/github-community-projects/graphql-client/llms.txt Create a client instance by binding a schema and an execution adapter. Configure options like `enforce_collocated_callers` and `raise_on_unknown_enum_value`. ```ruby require "graphql/client" require "graphql/client/http" module MyApp HTTP = GraphQL::Client::HTTP.new("https://api.example.com/graphql") do def headers(_ctx) { "Authorization" => "Bearer #{ENV['API_TOKEN']}" } end end Schema = GraphQL::Client.load_schema("db/schema.json") Client = GraphQL::Client.new( schema: Schema, execute: HTTP, enforce_collocated_callers: true, # raises on implicit cross-module field access raise_on_unknown_enum_value: true # raises when server returns an unrecognized enum value ) end ``` -------------------------------- ### Execute Local GraphQL Query with Client Source: https://github.com/github-community-projects/graphql-client/blob/master/guides/local-queries.md Initialize a GraphQL client to execute queries against an in-memory schema. Ensure the client is configured with the correct schema and execution object. ```ruby # client.rb require "server" require "graphql/client" Client = GraphQL::Client.new(schema: Server::Schema, execute: Server::Schema) Query = Client.parse <<-'GRAPHQL' query { version } GRAPHQL result = Client.query(Query) puts result.data.version ``` -------------------------------- ### Configure GraphQL Client in Rails Application Source: https://github.com/github-community-projects/graphql-client/blob/master/guides/rails-configuration.md Configure the GraphQL client instance by setting `config.graphql.client` before initializers run. Ensure the client is set early in the application's lifecycle. ```ruby require "graphql/client/railtie" require "graphql/client/http" module Foo HTTP = GraphQL::Client::HTTP.new("https://foo.com/") # TODO: Rails.root isn't available yet :( Client = GraphQL::Client.new(schema: "db/schema.json", execute: HTTP) class Application < Rails::Application # Set config.graphql.client to configure the client instance ERB templates # will be parsed against. # # client must be set before initializers run. config/initializers/* # are ran after graphql-client initializers so thats too late. config.graphql.client = Client end end ``` -------------------------------- ### Rails Configuration for GraphQL Client Source: https://context7.com/github-community-projects/graphql-client/llms.txt Configure the GraphQL client in `config/application.rb` or an early initializer. This includes schema caching, Railtie integration, and setting up a schema updater Rake task. The client must be configured before initializers run. ```ruby # config/application.rb (or an early initializer) require "graphql/client/railtie" require "graphql/client/http" module MyApp HTTP = GraphQL::Client::HTTP.new("https://api.example.com/graphql") do def headers(context) { "Authorization" => "Bearer #{context[:token] || ENV['API_TOKEN']}" } end end # Load schema from JSON on disk (fast, no network request at boot) Client = GraphQL::Client.new(schema: "db/schema.json", execute: HTTP) class Application < Rails::Application # Tell graphql-client which Client instance to use for ERB parsing config.graphql.client = Client end end # lib/tasks/schema.rake namespace :schema do desc "Fetch the latest GraphQL schema from the API" task :update do GraphQL::Client.dump_schema(MyApp::HTTP, "db/schema.json") puts "Schema written to db/schema.json" end end ``` -------------------------------- ### Runtime Query String Construction (Horrible) Source: https://github.com/github-community-projects/graphql-client/blob/master/guides/dynamic-query-error.md Constructing GraphQL query strings at runtime with user input is a security risk and should be avoided. Always use static queries with variables. ```ruby hero_query = SWAPI::Client.parse <<-GRAPHQL query { hero(id: "#{id}") { name } } GRAPHQL result = SWAPI::Client.query(hero_query) ``` -------------------------------- ### Avoid Under-fetching by Declaring All Data Dependencies Source: https://github.com/github-community-projects/graphql-client/blob/master/guides/over-under-fetching.md Ensure all fields used in a template are declared in the GraphQL fragment. Failing to do so can lead to `NoFieldError` or `nil` values, and latent bugs if the data is unexpectedly provided by a parent. ```erb <%graphql fragment IssueHeader on Issue { title # forgot to declare # author { login } } %> <%= issue["title"] %> by <%= issue["author"]["login"] %> ``` ```erb <%graphql fragment Issue on Issue { number # parent view happens to include author.login the child will need author { login } ...Views::Issues::Issue::IssueHeader } %> <%= render "issue/header", issue: issue %> ``` ```erb - # parent view happens to include author.login the child will need - author { login } ``` -------------------------------- ### Explicitly Declaring Dependencies in Ruby Source: https://github.com/github-community-projects/graphql-client/blob/master/guides/collocated-call-sites.md Shows how to define a fragment and use it explicitly within a Ruby helper method. This ensures that the method's data requirements are clear and collocated. ```ruby PageTitleFragment = SWAPI::Client.parse <<-'GRAPHQL' fragment on Human { name homePlanet } GRAPHQL def page_title(human) tag(:title, "#{human.name} from #{human.home_planet}") end ``` -------------------------------- ### Execute a GraphQL Query Source: https://github.com/github-community-projects/graphql-client/blob/master/README.md Execute a predefined GraphQL query using the client. The result provides access to data via Ruby-ish accessors. ```ruby result = SWAPI::Client.query(Hero::Query) # The raw data is Hash of JSON values # result["data"]["luke"]["homePlanet"] # The wrapped result allows to you access data with Ruby methods result.data.luke.home_planet ``` -------------------------------- ### Configure HTTP Adapter with Custom Headers and Connection Source: https://context7.com/github-community-projects/graphql-client/llms.txt Configure the built-in HTTP adapter to communicate with a GraphQL endpoint. Customize request headers and override the connection for SSL or timeout settings. ```ruby require "graphql/client" require "graphql/client/http" HTTP = GraphQL::Client::HTTP.new("https://api.example.com/graphql") do def headers(context) { "Authorization" => "Bearer #{context[:token]}", "User-Agent" => "MyApp/1.0" } end # Override connection for SSL customization or timeouts def connection Net::HTTP.new(uri.host, uri.port).tap do |http| http.use_ssl = uri.scheme == "https" http.read_timeout = 10 end end end # After a query, the raw Net::HTTP response headers are accessible: # HTTP.last_response # => {"content-type"=>["application/json"], ...} ``` -------------------------------- ### Define GraphQL Queries and Fragments with Client#parse Source: https://context7.com/github-community-projects/graphql-client/llms.txt Use Client#parse to define GraphQL queries and fragments. The query is validated against the schema at parse time. Fragment spreads using Ruby constant syntax are automatically resolved and inlined. ```ruby module SWAPI # Simple query with no variables HeroNameQuery = Client.parse <<-'GRAPHQL' query { hero { name } } GRAPHQL # Query with variables HeroFromEpisodeQuery = Client.parse <<-'GRAPHQL' query($episode: Episode) { hero(episode: $episode) { name appearsIn } } GRAPHQL # Fragment definition (no operation type) HumanFragment = Client.parse <<-'GRAPHQL' fragment on Human { name homePlanet } GRAPHQL # Query composing multiple fragments via Ruby constant spread syntax HeroDetailQuery = Client.parse <<-'GRAPHQL' { luke: human(id: "1000") { ...HumanFragment } leia: human(id: "1003") { ...HumanFragment } } GRAPHQL # ValidationError is raised at parse time if fields don't exist on the schema end ``` -------------------------------- ### Add graphql-client Gem to Gemfile Source: https://github.com/github-community-projects/graphql-client/blob/master/guides/rails-configuration.md Include the graphql-client gem in your application's Gemfile to add its functionality. ```ruby gem 'graphql-client' ``` -------------------------------- ### Execute Parsed Operations with Client#query Source: https://context7.com/github-community-projects/graphql-client/llms.txt Executes a parsed OperationDefinition against the configured adapter. Accepts optional variables and context. Returns a GraphQL::Client::Response object. ```ruby # Simple query result = SWAPI::Client.query(SWAPI::HeroNameQuery) puts result.data.hero.name # => "R2-D2" # Query with variables and context result = SWAPI::Client.query( SWAPI::HeroFromEpisodeQuery, variables: { episode: "JEDI" }, context: { token: current_user.api_token } ) if result.data puts result.data.hero.name # snake_case access to camelCase fields puts result.data.hero.appears_in # => ["NEWHOPE", "EMPIRE", "JEDI"] else # Handle total failure (parse/network error) result.errors.each { |field, msg| warn "#{field}: #{msg}" } end ``` -------------------------------- ### Client#query — Query Execution Source: https://context7.com/github-community-projects/graphql-client/llms.txt Executes a parsed OperationDefinition against the configured adapter. Supports optional variables and context, returning a GraphQL::Client::Response object. ```APIDOC ## Client#query — Query Execution Executes a parsed `OperationDefinition` against the configured adapter. Accepts optional `variables:` (Hash) and `context:` (Hash, forwarded to the HTTP adapter's `headers` method). Returns a `GraphQL::Client::Response` with `.data` (a typed struct), `.errors`, and `.extensions`. ### Example Usage ```ruby # Simple query result = SWAPI::Client.query(SWAPI::HeroNameQuery) puts result.data.hero.name # => "R2-D2" # Query with variables and context result = SWAPI::Client.query( SWAPI::HeroFromEpisodeQuery, variables: { episode: "JEDI" }, context: { token: current_user.api_token } ) if result.data puts result.data.hero.name # snake_case access to camelCase fields puts result.data.hero.appears_in # => ["NEWHOPE", "EMPIRE", "JEDI"] else # Handle total failure (parse/network error) result.errors.each { |field, msg| warn "#{field}: #{msg}" } end ``` ``` -------------------------------- ### Pitfall: Sharing Fragment Definitions Source: https://github.com/github-community-projects/graphql-client/blob/master/guides/collocated-call-sites.md Illustrates the pitfall of sharing a single GraphQL fragment definition across multiple helper methods, leading to over-fetching. Each helper should have its own dedicated fragment. ```ruby # bad SharedFragment = SWAPI::Client.parse <<-'GRAPHQL' fragment on Human { name homePlanet } GRAPHQL def human_header(human) human = SharedFragment.new(human) content_tag(:h1, human.name.capitalize) end def page_title(human) human = SharedFragment.new(human) content_tag(:title, "#{human.name} from #{human.home_planet}") end ``` -------------------------------- ### Custom HTTP Adapter with Faraday Source: https://github.com/github-community-projects/graphql-client/blob/master/guides/remote-queries.md Implement a custom HTTP adapter using the Faraday library to interact with a GraphQL endpoint. This adapter handles the POST request, serializes the query, and parses the JSON response. ```ruby require "faraday" class FaradayAdapter def self.execute(document:, operation_name:, variables:, context:) response = Faraday.post("http://graphql-swapi.parseapp.com/", { "query" => document.to_query_string, "operationName" => operation_name, "variables" => variables }) JSON.parse(response.body) end end ``` -------------------------------- ### Define GraphQL Server Schema Source: https://github.com/github-community-projects/graphql-client/blob/master/guides/local-queries.md Define the GraphQL schema for your server using graphql-ruby. This sets up the types and fields available for queries. ```ruby # server.rb require "graphql" module Server QueryType = GraphQL::ObjectType.define do name "Query" field :version, !types.Int end Schema = GraphQL::Schema.define(query: QueryType) end ``` -------------------------------- ### Updating Ruby Method Collocation Source: https://github.com/github-community-projects/graphql-client/blob/master/guides/collocated-call-sites.md Shows how to update a Ruby method and its associated fragment to use new fields. Demonstrates the ease of refactoring when code and queries are collocated. ```diff PageTitleFragment = SWAPI::Client.parse <<-'GRAPHQL' fragment on Human { name - homePlanet + age } GRAPHQL def page_title(human) human = PageTitleFragment.new(human) tag(:title, "#{human.name} is #{human.age} years old") end ``` -------------------------------- ### Define a Simple GraphQL Query Source: https://github.com/github-community-projects/graphql-client/blob/master/README.md Declare a static GraphQL query and assign it to a Ruby constant using the `parse` method. This query fetches the 'name' field of the 'hero'. ```ruby HeroNameQuery = SWAPI::Client.parse <<-'GRAPHQL' query { hero { name } } GRAPHQL ``` -------------------------------- ### Handle Parse/Validation Errors in Ruby Source: https://github.com/github-community-projects/graphql-client/blob/master/guides/handling-errors.md Use this when a query has invalid syntax or references non-existent fields. The entire operation fails, returning no data. ```ruby response = Client.query(BadQuery) response.data #=> nil response.errors[:data] #=> "Field 'missing' doesn't exist on type 'Query'" ``` -------------------------------- ### Execute Query with Variables and Context Source: https://github.com/github-community-projects/graphql-client/blob/master/README.md Execute a GraphQL query, passing in variables and context parameters. This allows for dynamic query execution and passing request-specific information. ```ruby result = SWAPI::Client.query(Hero::HeroFromEpisodeQuery, variables: {episode: "JEDI"}, context: {user_id: current_user_id}) ``` -------------------------------- ### GraphQL Query Definition and Execution in Rails Controller Source: https://github.com/github-community-projects/graphql-client/blob/master/guides/controllers.md Defines a static GraphQL query as a constant and executes it against URL parameters. It handles checking for data existence and rendering the appropriate template or returning a 404. ```ruby class IssuesController < ApplicationController # Statically define any GraphQL queries as constants. This avoids query string # parsing at runtime and ensures we can statically validate all queries for # errors. # # This defines how params data maps to a GraphQL query to find an Issue node. ShowQuery = FooApp::Client.parse <<-'GRAPHQL' query($user: String!, $repository: String!, number: Int!) { user(login: $user) { repository(name: $repository) { issue(number: $number) { ...Views::Issues::Show::Issue } } } } GRAPHQL def show # Execute our static query against URL params. All queries are executed in # the context of a "current_user". data = query ShowQuery, params.slice(:user, :repository, :number) # Check if the Issue node was found, if not the issue might not exist or # we just don't have permission to see it. if issue = data.user.repository.issue # Render the "issue/show" template with our data hash. render "issues/show", issue: issue else head :not_found end end end ``` -------------------------------- ### Querying User with Fragment Source: https://github.com/github-community-projects/graphql-client/blob/master/guides/implicitly-fetched-field-error.md Defines a GraphQL query that fetches a user's fullName and includes a fragment for user details. This is used to demonstrate how a parent fragment might fetch data needed by nested components. ```ruby UserQuery = Client.parse <<-'GRAPHQL' query { user(name: "Josh") { fullName ...Views::Users::Show::User } } GRAPHQL ``` -------------------------------- ### Client#create_operation — Operation from Fragment Source: https://context7.com/github-community-projects/graphql-client/llms.txt Automatically generates an operation definition from a fragment definition, inferring operation type and building the variable list. ```APIDOC ## Client#create_operation — Operation from Fragment Automatically generates an operation definition (query/mutation/subscription) from a fragment definition, inferring the operation type from the fragment's root type and building the variable list from referenced variable uses. Useful for dynamic or reusable mutation patterns. ### Example Usage ```ruby UpdateIssueMutation = MyApp::Client.parse <<-'GRAPHQL' fragment on Mutation { updateIssue(id: $id, title: $title) { issue { id title } } } GRAPHQL # Creates: mutation($id: ID!, $title: String!) { ...UpdateIssueMutation } UpdateIssueOperation = MyApp::Client.create_operation(UpdateIssueMutation) result = MyApp::Client.query( UpdateIssueOperation, variables: { id: "MDExOklzc3VlMQ==", title: "New Title" } ) puts result.data.update_issue.issue.title # => "New Title" ``` ``` -------------------------------- ### Accessing Queried and Missing Fields Source: https://github.com/github-community-projects/graphql-client/blob/master/guides/implicitly-fetched-field-error.md Demonstrates accessing fields from a UserFragment object. Accessing 'location', which was explicitly queried, succeeds. Accessing 'fullName', which was omitted from the query, raises an UnfetchedFieldError. ```ruby user = UserFragment.new(user) # ok as `location` was explicitly queried user.location # raises UnfetchedFieldError, missing fullName field in query user.full_name ``` -------------------------------- ### Dump GraphQL Schema to JSON File Source: https://context7.com/github-community-projects/graphql-client/llms.txt Execute an introspection query against a live adapter and save the schema as a pretty-printed JSON file. This is useful for keeping a local schema file updated. ```ruby require "graphql/client" require "graphql/client/http" namespace :schema do task :update do HTTP = GraphQL::Client::HTTP.new("https://api.example.com/graphql") do def headers(_context) { "Authorization" => "Bearer #{ENV['API_TOKEN']}" } end end # Writes introspection result to db/schema.json GraphQL::Client.dump_schema(HTTP, "db/schema.json") puts "Schema updated." end end # Run: bin/rake schema:update && git add db/schema.json ``` -------------------------------- ### Define Namespaced Fragment in Query Source: https://github.com/github-community-projects/graphql-client/blob/master/README.md Demonstrates how to reference a fragment within a nested module scope. The `parse` method resolves these namespaced fragment spreads. ```ruby module Hero Query = SWAPI::Client.parse <<-'GRAPHQL' { luke: human(id: "1000") { ...Human::Fragment } leia: human(id: "1003") { ...Human::Fragment } } GRAPHQL end ``` -------------------------------- ### Define Static Query Fragment for Helper Data Source: https://github.com/github-community-projects/graphql-client/blob/master/guides/helpers.md Define a static query fragment for fetching data required by a helper. This fragment is parsed by the GraphQL client. ```ruby module MilestoneHelper # Define static query fragment for fetching data for helper. MilestoneProgressFragment = FooApp::Client.parse <<-'GRAPHQL' fragment on Milestone { closedIssueCount totalIssueCount } GRAPHQL def milestone_progress(milestone) milestone = MilestoneProgressFragment.new(milestone) percent = (milestone.closed_issue_count / milestone.total_issue_count) * 100 content_tag(:span, "#{percent}%", class: "progress", style: "width: #{percent}%") end # A simpler version may use keyword arguments to define the functions # requirements. This avoids any dependency on the shape of data result # classes. This maybe a fine alternative if theres only a handful of # arguments. def milestone_progress(closed:, total:) percent = (closed / total) * 100 content_tag(:span, "#{percent}%", class: "progress", style: "width: #{percent}%") end end ``` -------------------------------- ### Create Operations from Fragments with Client#create_operation Source: https://context7.com/github-community-projects/graphql-client/llms.txt Automatically generates an operation definition from a fragment definition. Useful for dynamic or reusable mutation patterns. The operation type is inferred from the fragment's root type. ```ruby UpdateIssueMutation = MyApp::Client.parse <<-'GRAPHQL' fragment on Mutation { updateIssue(id: $id, title: $title) { issue { id title } } } GRAPHQL # Creates: mutation($id: ID!, $title: String!) { ...UpdateIssueMutation } UpdateIssueOperation = MyApp::Client.create_operation(UpdateIssueMutation) result = MyApp::Client.query( UpdateIssueOperation, variables: { id: "MDExOklzc3VlMQ==", title: "New Title" } ) puts result.data.update_issue.issue.title # => "New Title" ``` -------------------------------- ### Accessing Unfetched Field Source: https://github.com/github-community-projects/graphql-client/blob/master/guides/unfetched-field-error.md Demonstrates accessing a field (firstName) that is present in the query, and attempting to access a field (lastName) that is missing, which raises an UnfetchedFieldError. ```ruby UserFragment = Client.parse <<-'GRAPHQL' fragment on User { firstName } GRAPHQL user = UserFragment.new(user) # ok user.first_name # raises UnfetchedFieldError, missing lastName field in query user.last_name ``` -------------------------------- ### Define GraphQL Query with Variables Source: https://github.com/github-community-projects/graphql-client/blob/master/README.md Declare a GraphQL query that accepts variables. The `episode` variable is used to filter the hero. ```ruby HeroFromEpisodeQuery = SWAPI::Client.parse <<-'GRAPHQL' query($episode: Episode) { hero(episode: $episode) { name } } GRAPHQL ``` -------------------------------- ### Compose GraphQL Fragment for Static Subviews in ERB Source: https://github.com/github-community-projects/graphql-client/blob/master/guides/templates.md Composes a GraphQL fragment that includes the data dependencies for the current partial and its static subviews. This ensures all required data is fetched efficiently. ```erb <%graphql fragment IssueFragment on Issue { title ...Views::Issues::Header::Issue ...Views::Issues::Body::Issue } %> ``` -------------------------------- ### Define a GraphQL Fragment Source: https://github.com/github-community-projects/graphql-client/blob/master/README.md Declare a GraphQL fragment named 'HumanFragment' that selects 'name' and 'homePlanet' fields for a 'Human' type. ```ruby HumanFragment = SWAPI::Client.parse <<-'GRAPHQL' fragment on Human { name homePlanet } GRAPHQL ``` -------------------------------- ### ERB Template Integration with `<%graphql` Tag Source: https://context7.com/github-community-projects/graphql-client/llms.txt Use the `<%graphql` tag in Rails ERB templates to declare static GraphQL fragments. Configure `config.graphql.client` in your Rails application. The fragment is accessible via the view's namespaced constant path. ```erb <%# app/views/issues/show.html.erb %> <%graphql fragment Issue on Issue { title bodyHTML author { login } comments { ...Views::Issues::Comment::Comment } } %> <%# Cast the raw hash to a typed struct at the top of the template %> <% issue = Views::Issues::Show::Issue.new(issue) %>
<%= issue.body_html %>
by <%= issue.author.login %>
<% issue.comments.each do |comment| %> <%= render "issues/comment", comment: comment %> <% end %> <%# Partial data error handling %> <% if issue.errors[:comments].any? %>Some comments could not be loaded.
<% end %> ``` ```ruby # Access the fragment constant from Ruby code: # Views::Issues::Show::Issue # => #<%= human.name %> lives on <%= human.home_planet %>.
``` -------------------------------- ### Resolving UnfetchedFieldError Source: https://github.com/github-community-projects/graphql-client/blob/master/guides/unfetched-field-error.md Shows how to resolve the UnfetchedFieldError by adding the missing 'lastName' field to the GraphQL query fragment. ```ruby UserFragment = Client.parse <<-'GRAPHQL' fragment on User { firstName lastName } GRAPHQL user = UserFragment.new(user) # now ok! user.last_name ``` -------------------------------- ### Ruby Method Collocation with Fragment Source: https://github.com/github-community-projects/graphql-client/blob/master/guides/collocated-call-sites.md Defines a GraphQL fragment and a Ruby method that uses it. Ensures that only necessary fields are queried and used by the helper method. ```ruby PageTitleFragment = SWAPI::Client.parse <<-'GRAPHQL' fragment on Human { name homePlanet } GRAPHQL def page_title(human) human = PageTitleFragment.new(human) tag(:title, "#{human.name} from #{human.home_planet}") end ``` -------------------------------- ### Define Schema Updater Rake Task Source: https://github.com/github-community-projects/graphql-client/blob/master/guides/rails-configuration.md Create a Rake task to update the GraphQL schema file. It's recommended to commit the downloaded schema and keep it up-to-date. ```ruby namespace :schema do task :update do GraphQL::Client.dump_schema(Foo::HTTP, "db/schema.json") end end ``` -------------------------------- ### Handle Empty Search Results Collection in ERB Source: https://github.com/github-community-projects/graphql-client/blob/master/guides/handling-errors.md Display a message if the search results collection is empty due to an error, otherwise iterate and display the results. ```erb <% if repository.errors[:search_results].any? %>Search is down
<% else %> <% repository.search_results.nodes.each do |result| %> <%= result.title %> <% end %> <% end %> ``` -------------------------------- ### Compose GraphQL Fragment for Looping Over Collection in ERB Source: https://github.com/github-community-projects/graphql-client/blob/master/guides/templates.md Composes a GraphQL fragment that includes the data dependencies for the current partial and a collection of items, including the fragment for the item's subview. This is used when iterating over a list of associated data. ```erb <%graphql fragment IssueFragment on Issue { title comments { ...Views::Issues::Comment::CommentFragment } } %> ``` -------------------------------- ### Define Static GraphQL Query Fragment in ERB Source: https://github.com/github-community-projects/graphql-client/blob/master/guides/templates.md Declares the data dependencies for an ERB template directly within the template using a GraphQL fragment. This ensures that all necessary data is fetched for the view and its subviews. ```erb <%graphql fragment on Issue { title repository { name } bodyHTML author { login } comments { # issues/show is only concerned with rendering a collection of # comments, not the comment itself. However, we do need to statically # include the data dependencies of the issues/comment partial we # intend to render. ...Views::Issues::Comment::Comment } } %> ``` -------------------------------- ### Compose GraphQL Fragment for Branching on Associated Data Presence in ERB Source: https://github.com/github-community-projects/graphql-client/blob/master/guides/templates.md Composes a GraphQL fragment that includes data dependencies for the current partial and an associated object that may or may not be present. This is used for conditionally rendering sections based on the existence of related data. ```erb <%graphql fragment Issue on Issue { title milestone { ...Views::Issues::Milestone::Milestone } } %> ``` -------------------------------- ### Client#parse — Query and Fragment Definition Source: https://context7.com/github-community-projects/graphql-client/llms.txt Parses a GraphQL query string and returns an OperationDefinition or FragmentDefinition. Validates against the schema and inlines fragment spreads. ```APIDOC ## Client#parse — Query and Fragment Definition Parses a GraphQL query string (using a single-quoted heredoc) and returns an `OperationDefinition` or `FragmentDefinition` constant. The query is validated against the schema at parse time. Fragment spreads using Ruby constant syntax (`...Module::Fragment`) are automatically resolved and inlined. ### Example Usage ```ruby module SWAPI # Simple query with no variables HeroNameQuery = Client.parse <<-'GRAPHQL' query { hero { name } } GRAPHQL # Query with variables HeroFromEpisodeQuery = Client.parse <<-'GRAPHQL' query($episode: Episode) { hero(episode: $episode) { name appearsIn } } GRAPHQL # Fragment definition (no operation type) HumanFragment = Client.parse <<-'GRAPHQL' fragment on Human { name homePlanet } GRAPHQL # Query composing multiple fragments via Ruby constant spread syntax HeroDetailQuery = Client.parse <<-'GRAPHQL' { luke: human(id: "1000") { ...HumanFragment } leia: human(id: "1003") { ...HumanFragment } } GRAPHQL # ValidationError is raised at parse time if fields don't exist on the schema end ``` ``` -------------------------------- ### Compose GraphQL Fragment for Branching on Arbitrary Flag in ERB Source: https://github.com/github-community-projects/graphql-client/blob/master/guides/templates.md Composes a GraphQL fragment that includes data dependencies for conditionally rendered UI elements, such as an edit toolbar. This fragment ensures that data needed for optional UI components is always fetched, even if it leads to some overfetching. ```erb <%graphql fragment Comment on Comment { bodyHTML editableByViewer ...Views::Issues::CommentEditToolbar::Comment } %> ``` -------------------------------- ### GraphQL::Client::Errors — Error Handling Source: https://context7.com/github-community-projects/graphql-client/llms.txt An ActiveModel::Errors-compatible object available as `response.errors`, providing structured access to field-level and top-level errors. ```APIDOC ## GraphQL::Client::Errors — Error Handling An `ActiveModel::Errors`-compatible object available as `response.errors` (top-level) or `response.data.errors` (field-level). Field errors map path components from the server's `"errors"` array to the corresponding data fields. ### Example Usage ```ruby response = MyApp::Client.query(ShowIssueQuery, variables: { id: params[:id] }) if response.data issue = response.data.issue if issue render "issues/show", issue: issue elsif response.data.errors[:issue].any? # e.g. "Could not resolve to a node with the global id of 'abc'" render status: :not_found, plain: response.data.errors[:issue].join(", ") end else # Total failure: parse/validation or network error render status: :internal_server_error, plain: response.errors.messages.inspect end # Errors API reference: # response.errors[:field] # => ["message1", "message2"] # response.errors.include?(:field) # => true/false # response.errors.size # => total error count ``` ``` -------------------------------- ### Handle Truncated Collection in ERB Source: https://github.com/github-community-projects/graphql-client/blob/master/guides/handling-errors.md Iterate through available diff entries and display a message if the collection was truncated due to a timeout or other execution error. ```erb <% pull.diff_entries.nodes.each do |diff_entry| %> <%= diff_entry.path %> <% end %> <% if pull.errors[:diff_entries].any? %>Sorry, we couldn't display all your diffs.
<% end %> ``` -------------------------------- ### User Fragment Definition Source: https://github.com/github-community-projects/graphql-client/blob/master/guides/implicitly-fetched-field-error.md Defines a GraphQL fragment for a User type, explicitly querying for 'location' but omitting 'fullName'. This snippet illustrates a scenario where a nested component might rely on a field not explicitly requested by the current fragment, leading to an error. ```ruby UserFragment = Client.parse <<-'GRAPHQL' fragment on User { location # forgot fullName } GRAPHQL ``` -------------------------------- ### Fragment Casting for Data Masking Source: https://context7.com/github-community-projects/graphql-client/llms.txt Fragment definitions act as typed views over raw data hashes. Calling `.new(obj)` on a fragment casts a raw hash into an accessor that only exposes declared fields, preventing implicit data coupling. Accessing undeclared fields raises `NoFieldError`. ```ruby HumanCardFragment = SWAPI::Client.parse <<-'GRAPHQL' fragment on Human { name homePlanet } GRAPHQL def render_human_card(human_data) # Cast raw hash (or parent view object) to this fragment's scope human = HumanCardFragment.new(human_data) # Only fields declared in the fragment are accessible: puts human.name # => "Luke Skywalker" puts human.home_planet # camelCase → snake_case automatically # Attempting to access an undeclared field raises NoFieldError: # human.height # => raises GraphQL::Client::Schema::ObjectType::NoFieldError end ``` -------------------------------- ### GraphQL::Client::Response Object for Query Results Source: https://context7.com/github-community-projects/graphql-client/llms.txt The response object returned by Client#query. It wraps the raw JSON hash and provides typed, snake_case access to data, a structured errors collection, and server extensions. `.data` is nil on network or validation errors. ```ruby response = MyApp::Client.query(MyQuery, variables: { id: "123" }) # Typed data access (camelCase → snake_case automatically) response.data.some_field # Raw JSON hash if needed response.to_h # => { "data" => { ... }, "errors" => [...] } # Extensions (e.g. rate limit info from GitHub's API) response.extensions # => { "rateLimit" => { "cost" => 1, "remaining" => 4999 } } # Full raw Net::HTTP response metadata (when using GraphQL::Client::HTTP) response.full_response # => { "x-request-id" => ["abc123"], "content-type" => ["application/json"] } ``` -------------------------------- ### GraphQL::Client::Errors for Error Handling Source: https://context7.com/github-community-projects/graphql-client/llms.txt An ActiveModel::Errors-compatible object available as `response.errors`. Field errors map path components from the server's "errors" array to corresponding data fields. Useful for handling both field-level and total failures. ```ruby response = MyApp::Client.query(ShowIssueQuery, variables: { id: params[:id] }) if response.data issue = response.data.issue if issue render "issues/show", issue: issue elsif response.data.errors[:issue].any? # e.g. "Could not resolve to a node with the global id of 'abc'" render status: :not_found, plain: response.data.errors[:issue].join(", ") end else # Total failure: parse/validation or network error render status: :internal_server_error, plain: response.errors.messages.inspect end # Errors API reference: # response.errors[:field] # => ["message1", "message2"] # response.errors.include?(:field) # => true/false # response.errors.size # => total error count ``` -------------------------------- ### Display Default Value with Error Indicator in ERB Source: https://github.com/github-community-projects/graphql-client/blob/master/guides/handling-errors.md Show a default value for a non-nullable scalar, like watchers_count, and display an error indicator if fetching the data failed. ```erb <% if repository.errors[:watchers_count].any? %>
<% end %>
<%= repository.watchers_count %> Watchers
```
--------------------------------
### Handle Nullable Assignee Field in ERB
Source: https://github.com/github-community-projects/graphql-client/blob/master/guides/handling-errors.md
Safely render an assignee's details by checking if the assignee object exists or if there was an error loading it.
```erb
<% if issue.assignee %>
<%= render "assignee", user: issue.assignee %>
<% elsif issue.errors[:assignee] %>
Something went wrong loading the assignee.
<% end %> ```