Skip to content

honeypc/active_data_source

Repository files navigation

ActiveDataSource

Gem Version CI Coverage

The definitive Rails-native DataSource backend.

Define a Resource once and get filtering, sorting, searching, pagination, authorization, serialization, schema endpoints, dynamic form metadata, and broadcasting — all without writing boilerplate controllers.

Works with any frontend: Stimulus · React · Vue · Alpine · HTMX · Turbo · TanStack Table · Kendo UI


Philosophy

Developers should never manually write pagination, filtering, sorting, searching, includes, serialization, or authorization inside every controller.

# Define once
class UserResource < ActiveDataSource::Resource
  model User

  searchable  :name, :email
  sortable    :name, :created_at
  filterable  :status, :organization_id
  includable  :organization, :roles
  serializer  UserSerializer

  field :status do
    type    :select
    options User.statuses.keys
    required true
  end

  default_sort :created_at, direction: :desc
end

# Use everywhere
class UsersController < ApplicationController
  include ActiveDataSource::Controller
  resource UserResource
end

That's all. Every standard REST action is provided automatically.


Features

Feature Description
🎛 Resource DSL Declare filters, sorts, search, includes, fields, relations, auth, serializer in one place
🔍 30+ Filter Operators eq, contains, between, in, null, today, this_week, nested associations, custom scopes
📄 Pagination Pagy, Kaminari, WillPaginate, cursor-based, offset
🔒 Authorization Pundit, CanCanCan, attribute-level permissions, policy_scope auto-applied
🏢 Multi-Tenancy acts_as_tenant, Apartment, automatic scoping
🔎 Search Ransack, Searchkick, PgSearch, Meilisearch
📦 Serialization Blueprinter, jsonapi-serializer, AMS, Jbuilder, plain JSON
📡 Broadcasting Turbo Streams + ActionCable after create/update/destroy
📋 Schema Endpoint GET /schema for dynamic forms, tables, and AI interfaces
🚀 Bulk Actions bulk_update, bulk_delete built-in
📤 Export CSV export with one DSL flag
🧩 TypeScript Client Fluent DataSource and QueryBuilder for any frontend
🧪 Testing Helpers RSpec matchers: be_filterable_by, be_sortable_by, allow_including
🔌 Pluggable Every adapter, parser, responder, and pipeline step is replaceable

Quick Start

Installation

# Gemfile
gem "active_data_source"

# Optional adapters — only install what you use
gem "pagy"          # pagination
gem "ransack"       # filtering + search
gem "pundit"        # authorization
gem "blueprinter"   # serialization
gem "acts_as_tenant"
bundle install

Configuration

# config/initializers/active_data_source.rb
ActiveDataSource.configure do |config|
  config.pagination    = :pagy          # :pagy | :kaminari | :will_paginate
  config.search        = :ransack       # :ransack | :searchkick | :pg_search
  config.authorization = :pundit        # :pundit | :cancancan | :none
  config.serializer    = :blueprinter   # :blueprinter | :jsonapi_serializer | :active_model_serializer | :none
  config.multi_tenancy = :acts_as_tenant # :acts_as_tenant | :apartment | :none
  config.response_format = :json        # :json | :json_api | :graphql

  config.default_page_size = 25
  config.maximum_page_size = 200
end

Define a Resource

class UserResource < ActiveDataSource::Resource
  model User

  searchable  :name, :email
  sortable    :name, :email, :created_at
  filterable  :status, :organization_id, :created_at
  includable  :organization, :roles

  serializer  UserSerializer

  field :id
  field :name
  field :email
  field :status do
    type    :select
    options User.statuses.keys
    required true
    default "active"
  end

  computed_field :display_name do |record|
    "#{record.name} <#{record.email}>"
  end

  belongs_to :organization, resource: OrganizationResource
  has_many   :roles,        resource: RoleResource

  default_sort :created_at, direction: :desc
  default_page_size 25
  maximum_page_size 100

  exportable
  broadcastable
end

Controller

class UsersController < ApplicationController
  include ActiveDataSource::Controller
  resource UserResource

  # Optional: override permitted params
  private

  def ads_permitted_params
    params.require(:user).permit(:name, :email, :status, :organization_id)
  end
end

Routes

resources :users do
  collection do
    get  :schema
    get  :statistics
    get  :export
    post :bulk_update
    delete :bulk_delete
  end
end

Query Language

GET /users
  ?page=2
  &per_page=25
  &sort=-created_at,name
  &search=john
  &include=organization,roles
  &fields=id,name,email
  &filter[name][contains]=john
  &filter[status][eq]=active
  &filter[age][gte]=18
  &filter[organization.name][contains]=OpenAI
  &group=organization_id
  &aggregate=count,avg:age

Operators Reference

Operator SQL Equivalent Example
eq = ? filter[status][eq]=active
neq != ? filter[status][neq]=inactive
contains LIKE '%?%' filter[name][contains]=john
starts_with LIKE '?%' filter[name][starts_with]=J
ends_with LIKE '%?' filter[email][ends_with]=@gmail.com
gt / gte / lt / lte comparison filter[age][gte]=18
in IN (?) filter[status][in]=active,pending
not_in NOT IN (?) filter[status][not_in]=inactive
between BETWEEN ? AND ? filter[age][between]=18,65
null IS NULL filter[deleted_at][null]=1
not_null IS NOT NULL filter[confirmed_at][not_null]=1
today today's date range filter[created_at][today]=1
this_week this week range filter[created_at][this_week]=1
this_month this month range filter[created_at][this_month]=1

Nested association filters:

filter[organization.name][contains]=OpenAI

Response Format

{
  "data": [
    { "id": 1, "name": "Alice", "email": "alice@example.com", "status": "active" }
  ],
  "meta": {
    "page": 1,
    "per_page": 25,
    "total": 512,
    "pages": 21,
    "from": 1,
    "to": 25,
    "resource": "users"
  },
  "links": {
    "self":  "/users?page=1&per_page=25",
    "next":  "/users?page=2&per_page=25",
    "last":  "/users?page=21&per_page=25"
  },
  "included":   [],
  "aggregates": { "count": 512, "avg_age": 32.4 },
  "errors":     []
}

Schema Endpoint

GET /users/schema

Returns field types, options, validation rules, relationships, and permissions — enabling dynamic form generation:

{
  "resource": "UserResource",
  "fields": [
    {
      "name": "status",
      "type": "select",
      "options": ["active", "inactive", "pending"],
      "required": true,
      "default": "active"
    }
  ],
  "filterable": ["status", "organization_id"],
  "sortable":   ["name", "created_at"],
  "pagination": { "default_page_size": 25, "maximum_page_size": 100 }
}

TypeScript Client

npm install @active-data-source/client
import { DataSource } from "@active-data-source/client";

const users = new DataSource<User>("/users", {
  csrfToken: () => document.querySelector<HTMLMetaElement>('meta[name="csrf-token"]')?.content ?? "",
});

// Fluent API
const result = await users
  .eq("status", "active")
  .contains("name", "john")
  .sort("created_at", "desc")
  .include("organization")
  .page(2)
  .per(25)
  .fetch();

console.log(result.data);  // User[]
console.log(result.meta);  // AdsMeta

// CRUD
const created = await users.create({ name: "Bob", email: "bob@example.com" });
const updated = await users.update(1, { status: "inactive" });
await users.destroy(1);

// Schema for dynamic forms
const schema = await users.schema();

Supported Adapters

Category Adapters
Pagination Pagy, Kaminari, WillPaginate, Cursor
Search Ransack, Searchkick, PgSearch, Meilisearch
Authorization Pundit, CanCanCan
Multi-Tenancy acts_as_tenant, Apartment
Serialization Blueprinter, jsonapi-serializer, AMS
Broadcasting Turbo Streams, ActionCable

Rails Compatibility

Rails Ruby Status
8.0 3.2, 3.3, 3.4 ✅ Supported
7.1 3.2, 3.3, 3.4 ✅ Supported

Testing

# spec/resources/user_resource_spec.rb
RSpec.describe UserResource do
  it { is_expected.to be_filterable_by(:status) }
  it { is_expected.to be_filterable_by(:name).with_operator(:contains) }
  it { is_expected.to be_sortable_by(:created_at) }
  it { is_expected.to be_searchable }
  it { is_expected.to allow_including(:organization) }
end

Documentation


License

MIT

About

A Rails-native resource and data source framework for building searchable, filterable, sortable, and metadata-driven APIs.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors