Project

hq-graphql

0.03
No release in over 3 years
OneHQ GraphQL Library
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Development

Runtime

 Project Readme

HQ::GraphQL

OneHQ GraphQL interface to Ruby Graphql.

Test and Lint GitHub tag

Configuration

Default Scope

Define a global default scope.

::HQ::GraphQL.config do |config|
  config.default_scope = ->(scope, context) do
    scope.where(organization_id: context[:organization_id])
  end
end

Generated collection associations can apply that scope before sorting and pagination. Unauthorized records can also be removed before GraphQL resolves non-null list elements:

::HQ::GraphQL.configure do |config|
  config.apply_default_scope_to_associations = true
  config.filter_unauthorized_collection_items = true
end

Both settings are off by default. An individual generated association can override either setting in its field block:

query do
  update :audit_entries do
    apply_default_scope false
    filter_unauthorized_items false
  end
end

Object-level authorization is enabled by default for every GraphQL object type. A projection that intentionally exposes records outside the normal authorization boundary can opt out explicitly:

class OrganizationOptionType < ::GraphQL::Schema::Object
  authorize_objects false
end

This skips only HQ::GraphQL's configured object authorization callback for that type. GraphQL's built-in type authorization still runs.

Excluded Inputs

Define a global excluded input fields. Useful for excluding (autogenerated | auto set) fields like below.

::HQ::GraphQL.configure do |config|
  config.excluded_inputs = [:id, :created_at, :updated_at]
end

GraphQL Resource

Connect to ActiveRecord to auto generate queries and mutations.

Queries

Include ::HQ::GraphQL::Resource and set self.model_name to start using queries. Fields will be created for all active record columns. Association fields will be created if the association is also a GraphQL Resource.

class AdvisorResource
  include ::HQ::GraphQL::Resource

  # ActiveRecord Model Name
  self.model_name = "Advisor"
end

Customize

class AdvisorResource
  include ::HQ::GraphQL::Resource
  self.model_name = "Advisor"

  # Turn off fields for active record associations
  query attributes: true, associations: false do
    # Create field for addresses
    add_association :addresses

    # add a custom field
    field :custom_field, String, null: false

    def custom_field
      "Fizz"
    end
  end
end

Mutations

Mutations will not be created by default. Add mutations to a resource to build mutations for create, update, and destroy.

class AdvisorResource
  include ::HQ::GraphQL::Resource
  self.model_name = "Advisor"

  # Builds the following mutations:
  #   createAdvisor
  #   updateAdvisor
  #   destroyAdvisor
  mutations create: true, update: true, destroy: true

  # Turn off fields for active record associations
  inputs attributes: true, associations: false do
    # Create input argument for addresses
    add_association :addresses
  end
end

Authorizing a record through its owner

A mutation's own check only knows the model (updateSalesManager asks "may I update SalesManager?"). A host that scopes its restrictions per parent (Advisor -> SalesManager) authors them under the parent, which that question can never reach. record_parents says whose a record is, and the gem asks about it as a child of each:

HQ::GraphQL.configure do |config|
  # Who a record belongs to: the parent classes to ask about, an empty list for none.
  # EVERY owner it has, not just the first: a model that allows several owners is restricted
  # under each of them, and a restriction on one that was skipped would be bypassed.
  config.record_parents = ->(record, context) do
    record.class.owner_associations.filter_map { |name| record.public_send(name)&.class }.uniq
  end

  # Answers every question: the nested-attribute rows and, now, a record as a child of its owner.
  config.authorize_nested_attributes = ->(action, klass, parent_klass, context) do
    # may context[:current_user] `action` a `klass` under a `parent_klass`?
  end
end

Inert until both are set. create, update, destroy and copy ask about the record the mutation found (or built); an update also asks, as a :create, about any new owner it would leave the record with. A refusal writes nothing and answers { "base" => ["You are not allowed to update sales managers"] }.

Enums

Auto generate enums from the database using ActiveRecord This comes in handy when we have constants that we want represented as enums.

Example

Let's assume we're saving data into a user types table

# select * from user_types;
  id |    name
 --- +-------------
  1  | Admin
  2  | Support User
 (2 rows)
class Enums::UserType < ::GraphQL::Schema::Enum
  with_model
end

This class automatically uses the UserType ActiveRecord model to generate an enum:

enum UserType {
    Admin
    SupportUser
}

Root Mutations

Add mutations to your schema

class RootMutation < ::HQ::GraphQL::RootMutation; end

class Schema < ::GraphQL::Schema
  mutation(RootMutation)
end

Default Root Queries

Create a root query:

class AdvisorResource
  include ::HQ::GraphQL::Resource
  self.model_name = "Advisor"

  root_query
end

class RootQuery < ::HQ::GraphQL::RootQuery; end

class Schema < ::GraphQL::Schema
  mutation(RootQuery)
end

Custom Root Queries

class AdvisorResource
  include ::HQ::GraphQL::Resource
  self.model_name = "Advisor"

  def_root :advisors, is_array: true, null: false do
    argument :active, ::GraphQL::Types::Boolean, required: false

    def resolve(active: nil)
      scope = Advisor.all

      if active
        scope = scope.where(active: true)
      end
    end
  end
end

Create a new ::GraphQL::Schema::Object

class AdvisorType < ::GraphQL::Schema::Object
  # Supports graphql-ruby functionality
  field :id, Int, null: false

  # Lazy Loading
  # Useful for loading data from the database to generate a schema
  lazy_load do
    load_data_from_db.each do |name|
      field name, String, null: false
    end
  end

  # Attach the GraphQL object to an ActiveRecord Model
  # First argument is the string form of your ActiveRecord model.
  #
  # attributes:
  #  Set it to false if you don't want to auto-include your model's attributes.
  #  Defaults to true.
  #
  # associations:
  #  Set it to false if you don't want to auto-include your model's associations.
  #  Defaults to true.
  with_model "Advisor", attributes: true, associations: false

  # Remove attributes that were included by `with_model`
  remove_attrs :id, :created_at, :updated_at

  # Remove associations that were included by `with_model`
  remove_associations :organization, :created_by
end