JSONAPI Resources Anchor

Configuration

Quick Reference

NameTypeDefault
field_case:camel | :snake | :kebab | :camel_without_inflectionnil
ar_column_to_typeProcnil
use_active_record_commentBooleannil
ar_comment_to_stringProcnil
use_active_record_validationsBooleantrue
infer_nullable_relationships_as_optionalBooleannil
empty_relationship_typeProcnil
use_type_as_schema_nameBooleannil
infer_default_as_non_nullBooleannil
maybe_as_unionBooleannil
array_bracket_notationBooleannil
infer_ar_enumsBooleannil
rbs"off" | "fallback""off"
rbs_sig_pathStringRails.root.join("sig")

Example

config/initializers/anchor.rb
module Anchor
  configure do |c|
    c.field_case = :camel_without_inflection
    c.use_active_record_comment = true
    c.ar_comment_to_string = lambda { |comment|
      begin
        res = JSON.parse(comment)
        res["description"]
      rescue JSON::ParserError
        comment
      end
    }

    c.use_active_record_validations = true
    c.infer_nullable_relationships_as_optional = true

    c.ar_column_to_type = lambda { |column|
      return Types::Literal.new("never") if column.name == "loljk"
      Types::Inference::ActiveRecord::SQL.default_ar_column_to_type(column)
    }

    c.empty_relationship_type = -> { Anchor::Types::Object }
  end
end

Options

field_case

  • Type: :camel | :snake | :kebab | :camel_without_inflection
  • Default: nil

Determines the format for properties of attributes and relationships.

  • nil - no changes to key name
  • camel, snake, and kebab format keys as their names suggest
  • camel_without_inflection - camelize the key without using inflections defined in your Rails app
    • e.g. given inflect.acronym "LOL" and key "lol_what"
      • camel: "lol_what".camellize => "LOLWhat"
      • camel_without_inflection: camel_without_inflection("lol_what") => "LolWhat"

ar_column_to_type

  • Type: Proc
  • Default: nil

Input: ActiveRecord::Base.columns_hash[attribute]

Output: member of Anchor::Types

Example

module Anchor
  configure do |c|
    c.ar_column_to_type = lambda { |column|
      return Types::Literal.new("never") if column.name == "loljk"
      Types::Inference::ActiveRecord::SQL.default_ar_column_to_type(column)
    }
  end
end

class UserResource < ApplicationResource
  attribute :loljk
end
schema.ts
// ...
export type User = {
  id: string;
  type: "users";
  loljk: "never";
};
// ...

use_active_record_comment

  • Type: Boolean
  • Default: nil

Whether to use ActiveRecord comments as the default value of the description option. In TypeScript, this would become a comment.

ar_comment_to_string

  • Type: Proc
  • Default: nil

Input: String

Output: String

Requires use_active_record_comment to be enabled.

Determines how the comment will be serialized in the serializer.

Example

After running this migration to add a JSON string formatted comment to get the schema below:

class AddWithParsedCommentToExhaustives < ActiveRecord::Migration[8.0]
  def change
    comment = <<~JSON
      {
        "description": "This is a parsed JSON comment.",
        "test": 2
      }
    JSON
    add_column :exhaustives, :with_parsed_comment, :string, comment:
  end
end

ActiveRecord::Schema[8.0].define(version: 2025_09_20_024446) do
  create_table "exhaustives", force: :cascade do |t|
    t.string "with_comment", comment: "This is a comment."
    t.string "with_parsed_comment", comment: "{\n  \"description\": \"This is a parsed JSON comment.\",\n  \"test\": 2\n}\n"
  end
end
module Anchor
  configure do |c|
    c.field_case = :camel_without_inflection
    c.use_active_record_comment = true
    c.ar_comment_to_string = lambda { |comment|
      begin
        res = JSON.parse(comment)
        res["description"]
      rescue JSON::ParserError
        comment
      end
    }
  end
end

The config above will serialize the comment for with_parsed_comment like:

type Exhaustive = {
  id: number;
  type: "exhaustives";
  /** This is a comment. */
  withComment: Maybe<string>;
  /** This is a parsed JSON comment. */
  withParsedComment: Maybe<string>;
};

use_active_record_validations

  • Type: Boolean
  • Default: true

Use ActiveModel validations on the ActiveRecord model to determine whether an attribute is nullable.

For example, validates :role, presence: true will infer role as non-null irrespective of the presence of the database's non-null constraint on the role column.

infer_nullable_relationships_as_optional

  • Type: Boolean
  • Default: nil

true infers nullable relationships as optional.

For example, in TypeScript, true infers { relation?: Relation } over { relation: Maybe<Relation> }.

empty_relationship_type

  • Type: Proc
  • Default: nil

By default, if there are no relationships for a resource then the relationships property is not included in the TypeScript serializer.

If this config is defined, the relationships property will be included with the returned Anchor type as the type.

Example

Anchor.configure { |c| c.empty_relationship_type = -> { Anchor::Types::Object } }

class UserResource < ApplicationResource
  attribute :name, Anchor::Types::String
end
schema.ts
// ...
export type User = {
  id: string;
  type: "users";
  name: string;
  relationships: {};
};
// ...

use_type_as_schema_name

  • Type: Boolean
  • Default: nil

By default, the demodulized name of the class minus "Resource" is used as the schema name for the resource, e.g. SomeModule::UserResource => User.

When true, the String#classify'd type that JSONAPI::Resource determines from the resource will be used.

infer_default_as_non_null

  • Type: Boolean
  • Default: nil

By default, columns without non-null validations will be considered nullable.

When true, columns with default values will be considered non-null.

maybe_as_union

  • Type: Boolean
  • Default: nil

By default, in the TypeScript serializer, nullable types will be serialized as e.g. Maybe<string>.

When true, nullable types will be serialized as e.g. string | null.

array_bracket_notation

  • Type: Boolean
  • Default: nil

By default, in the TypeScript serializer, array types will be serialized as e.g. Array<User>.

When true, array types will be serialized as e.g. (User)[].

infer_ar_enums

  • Type: Boolean
  • Default: nil

By default, in the TypeScript serializer, enums defined by ActiveRecord::Base.enum will be inferred as unknown.

When true, they will be serialized as a union of literals, e.g. type User = { role: "admin" | "member" }

rbs

  • Type: "off" | "fallback"

  • Default: "off"

  • "off" - RBS signatures will not be used.

  • "fallback" - Uses RBS signatures as a fallback for uninferrable attributes.

Currently supports getting signatures for attributes. The type of relationships will still be inferred from a combo of the underlying model and resource.

Fallback cases:

Custom method for field on the Resource?Field type inferrable on Model?Type Used
falsefalseRBS signature of Model
falsetrueInferred type for Model
truefalseRBS signature of Resource
truetrueRBS signature of Resource

rbs_sig_path

  • Type: String
  • Default: Rails.root.join("sig")

Path to load RBS signatures from.