RailsAuditLog::Graphql

CI Gem Version Total Downloads Ruby codecov

A graphql-ruby API layer for the rails_audit_log gem. Provides ready-made GraphQL types, queries, and subscriptions for querying audit log entries — without coupling graphql-ruby to the base gem.

Table of Contents

Installation

Add to your application's Gemfile:

gem "rails_audit_log"
gem "rails_audit_log-graphql"

Then run the install generator to wire up your schema:

rails g rails_audit_log:graphql:install

This injects include RailsAuditLog::Graphql::Queries::AuditLogEntriesQueryMixin into app/graphql/types/query_type.rb. If that file doesn't exist, the generator prints the line for you to add manually.

↑ Back to top

Usage

AuditLogEntryType

RailsAuditLog::Graphql::Types::AuditLogEntryType is a graphql-ruby object type that maps directly to RailsAuditLog::AuditLogEntry.

GraphQL field Type Nullable
id ID no
event String no
itemType String no
itemId ID no
createdAt ISO8601DateTime no
objectChanges JSON yes
object JSON yes
metadata JSON yes
reason String yes
whodunnitSnapshot String yes
actorType String yes
actorId ID yes
tenantId String yes
actor AuditLogActor yes
auditedResource AuditedResource no
diff [AuditLogDiff!] yes

↑ Back to top

AuditLogActor

RailsAuditLog::Graphql::Types::ActorType — a polymorphic reference to the actor who performed the audited action. Returned by the actor field on AuditLogEntry. null when no actor was recorded.

GraphQL field Type Nullable
id ID no
typeName String no

↑ Back to top

AuditedResource

RailsAuditLog::Graphql::Types::AuditedResourceType — a reference to the model record that was changed. Returned by the auditedResource field on AuditLogEntry. Always present.

GraphQL field Type Nullable
id ID no
typeName String no

↑ Back to top

AuditLogDiff

RailsAuditLog::Graphql::Types::DiffType — a single attribute change parsed from objectChanges. Returned as a list by the diff field on AuditLogEntry. null when objectChanges is not recorded (e.g. destroy events).

GraphQL field Type Nullable Description
attribute String no Name of the changed attribute
from JSON yes Value before the change
to JSON yes Value after the change

Example:

{
  auditLogEntries(event: "update") {
    diff {
      attribute
      from
      to
    }
  }
}

↑ Back to top

AuditLogEntriesQueryMixin

Include RailsAuditLog::Graphql::Queries::AuditLogEntriesQueryMixin into your app's QueryType to add two fields:

# app/graphql/types/query_type.rb
class Types::QueryType < Types::BaseObject
  include RailsAuditLog::Graphql::Queries::AuditLogEntriesQueryMixin
end

auditLogEntry(id: ID!): AuditLogEntry

Fetch a single entry by ID. Returns nil if not found.

auditLogEntries(...): [AuditLogEntry!]!

List entries with optional filters and offset pagination.

Argument Type Default Description
event String Filter by event type (create, update, destroy)
itemType String Filter by audited model class name
itemId ID Filter by audited record ID
actorId ID Filter by actor ID
actorType String Filter by actor model class name (e.g. "User")
since ISO8601DateTime Return entries created at or after this time
until ISO8601DateTime Return entries created at or before this time
touching String Filter to entries that changed a specific attribute
orderBy AuditLogEntrySortInput CREATED_AT DESC Sort field and direction
forTenant String Scope to a specific tenant ID; overrides auto-tenant
page Int 1 Page number (1-based)
perPage Int 25 Results per page

Results default to created_at DESC ordering.

auditLogEntriesConnection(...): AuditLogEntryConnection!

Same filters as auditLogEntries, but returns a Relay-style connection for cursor-based pagination.

Argument Type Description
event String Filter by event type (create, update, destroy)
itemType String Filter by audited model class name
itemId ID Filter by audited record ID
actorId ID Filter by actor ID
actorType String Filter by actor model class name (e.g. "User")
forTenant String Scope to a specific tenant ID; overrides auto-tenant
first Int Return the first N edges after after
after String Cursor to paginate forward from
last Int Return the last N edges before before
before String Cursor to paginate backward from

Results are ordered by created_at DESC.

Example — first page:

{
  auditLogEntriesConnection(first: 25) {
    nodes {
      id
      event
      itemType
      itemId
      createdAt
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}

Example — next page using a cursor:

{
  auditLogEntriesConnection(first: 25, after: "eyJpZCI6NDJ9") {
    nodes { id event }
    pageInfo { hasNextPage endCursor }
  }
}

↑ Back to top

auditLogEntriesCount(...): Int!

Returns the count of matching audit log entries. Respects auto-tenant when RailsAuditLog.current_tenant is configured.

Argument Type Description
event String Filter by event type (create, update, destroy)
itemType String Filter by audited model class name
actorType String Filter by actor model class name (e.g. "User")
since ISO8601DateTime Count entries created at or after this time
forTenant String Scope to a specific tenant ID; overrides auto-tenant
{ auditLogEntriesCount(event: "update", itemType: "Post") }

↑ Back to top

Tenant scoping

When RailsAuditLog.current_tenant is configured, all queries automatically filter to the current tenant:

RailsAuditLog.configure do |c|
  c.current_tenant { Current.tenant_id }
end

To override or explicitly specify a tenant per query, use the forTenant: argument (available on auditLogEntry, auditLogEntries, and auditLogEntriesConnection):

{ auditLogEntries(forTenant: "acme") { id event } }

↑ Back to top

Authentication

If RailsAuditLog.authenticate is configured, the block is called with the GraphQL context before every query. Return a truthy value to allow access; return falsy to raise GraphQL::ExecutionError with "Unauthorized".

RailsAuditLog.configure do |config|
  config.authenticate { |ctx| ctx[:current_user]&.admin? }
end

If no authenticate block is set, all queries are permitted.

↑ Back to top

SchemaPlugin

Include RailsAuditLog::Graphql::SchemaPlugin into your schema to enable query protection and dataloader batching in one step:

class MySchema < GraphQL::Schema
  include RailsAuditLog::Graphql::SchemaPlugin
  query Types::QueryType
end

This applies the following defaults (all overridable via RailsAuditLog::Graphql.*=):

Setting Default Description
max_complexity 200 Reject queries whose field-complexity sum exceeds this
max_depth 10 Reject queries nested deeper than this
default_max_page_size 25 Assumed page size for connection complexity calculation

Override in an initializer:

RailsAuditLog::Graphql.max_complexity = 500
RailsAuditLog::Graphql.max_depth = 15

The plugin also adds AuditLogActor.record and AuditedResource.record fields — nullable JSON fields that load the actual database record via RecordByIdSource, a GraphQL::Dataloader::Source that batches loads by class name to eliminate N+1 queries on list responses.

↑ Back to top

auditLogReify(itemType:, itemId:, at:): AuditLogJson

Reconstructs the attribute state of a record at a given point in time. Returns the attributes as AuditLogJson, or nil when no entry exists at or before at or the record was destroyed at that time. Accepts forTenant: and respects auto-tenant.

{
  auditLogReify(itemType: "Post", itemId: "42", at: "2026-01-15T12:00:00Z") {
    title
    publishedAt
  }
}

↑ Back to top

Subscriptions

Requires Action Cable in the host application.

AuditLogSubscriptionsMixin

Include RailsAuditLog::Graphql::Subscriptions::AuditLogSubscriptionsMixin into your app's SubscriptionType to add the auditLogEntryCreated field. Your schema must also use GraphQL::Subscriptions::ActionCableSubscriptions.

# app/graphql/types/subscription_type.rb
class Types::SubscriptionType < Types::BaseObject
  include RailsAuditLog::Graphql::Subscriptions::AuditLogSubscriptionsMixin
end

# app/graphql/my_schema.rb
class MySchema < GraphQL::Schema
  query Types::QueryType
  subscription Types::SubscriptionType
  use GraphQL::Subscriptions::ActionCableSubscriptions
end

auditLogEntryCreated

Fires when a new RailsAuditLog::AuditLogEntry is created. Accepts one of two argument combinations:

Argument Type Description
itemType String Model class name to scope the subscription to
itemId ID Record ID to scope the subscription to
actorId ID Actor ID — subscribe to all entries by a specific actor

Subscribe to all changes on a specific record:

subscription {
  auditLogEntryCreated(itemType: "Post", itemId: "42") {
    id
    event
    diff { attribute from to }
  }
}

Subscribe to all entries by a specific actor:

subscription {
  auditLogEntryCreated(actorId: "7") {
    id
    event
    itemType
    itemId
  }
}

Broadcaster

RailsAuditLog::Graphql::Subscriptions::Broadcaster bridges ActiveSupport::Notifications (fired by RailsAuditLog::Streaming::NotificationsAdapter or ActiveJobAdapter) to GraphQL subscription triggers. Start it in an initializer:

# config/initializers/rails_audit_log_graphql.rb
RailsAuditLog.configure do |c|
  c.streaming_adapter = RailsAuditLog::Streaming::NotificationsAdapter.new
end

Rails.application.config.after_initialize do
  RailsAuditLog::Graphql::Subscriptions::Broadcaster.new(schema: MySchema).start
end

For each entry, the broadcaster triggers:

  • auditLogEntryCreated(itemType:, itemId:) — notifies record-specific subscribers
  • auditLogEntryCreated(actorId:) — notifies actor-specific subscribers (when an actor is present)

↑ Back to top

Development

bin/setup         # install dependencies
bundle exec rake  # lint + tests

↑ Back to top

Contributing

See CONTRIBUTING.md.

↑ Back to top

License

The gem is available as open source under the terms of the MIT License.