Open-source project
graphiti-api/graphiti avatar
graphiti-api/graphiti

Graphiti replaces controllers and serializers with a Resource class, and the URL does the rest

Stylish Graph APIs, built on JSON:API

1,070 stars160 forksRubyMIT

At a glance

What is it?
A Ruby gem that sits on your models, usually ActiveRecord, and exposes them over a JSON:API compliant interface with filtering, sorting, pagination, sparse fieldsets and nested reads and writes across relationships. The whole query vocabulary lives in the query string, which is what makes a client layer hand-rollable.
Who is it for?
Adopt Graphiti if you are building a Ruby API and want the JSON:API shape, the query vocabulary and the relationship handling without hand writing controllers and serializers for every model. Do not adopt it if your clients need an idiosyncratic response format, because the point of the gem is to conform to a published specification rather than to your house style.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 7 days ago.
What is it written in?
Mainly Ruby, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 7, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • graphiti-api/graphiti1,070 ★ · +0
Star history
Repository2026-10-192026-11-07
graphiti-api/graphiti1.1k1.1k

0 Stars in the last 19 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

A Resource class replaces the controller and the serializer

Graphiti sits on top of your models, usually ActiveRecord, and exposes them over a JSON:API compliant interface. The README describes the substitution plainly: you define Resources instead of controllers and serializers, and get filtering, sorting, pagination, sparse fieldsets, statistics, and nested reads and writes across relationships, all over one endpoint.

A Resource looks like this.

ruby
class EmployeeResource < ApplicationResource
  attribute :first_name, :string
  attribute :last_name, :string
  attribute :age, :integer

  has_many :positions
end

The class is small on purpose. Attributes declare name and type, and `has_many` declares the relationship. Everything else, the query vocabulary, is not in the class at all.

That Resource serves `?filter[age][gt]=30`, `?sort=-age`, `?page[size]=10`, `?include=positions` and more, without writing any of them. This is the central design decision and it is a good one for a large API, because the alternative is that every filter, every sort option and every pagination mode gets implemented once per endpoint, and the implementations drift.

The `attribute :age, :integer` line is worth pausing on, because the type declaration is doing work. It is what lets the serializer know how to render the value, and it is what lets the framework decide that an age filter should be a numeric comparison rather than a string match. Declaring the type is what makes `?filter[age][gt]=30` mean greater than thirty rather than greater than the string thirty.

JSON:API is the leverage, and the client side is the payoff

The README is explicit about why the project is built on the JSON:API specification. The claim is that the spec settles the decisions every API accumulates, namely response shapes, filtering, sorting, pagination, error formats, and how related data rides along, and that the client layer speaks the protocol in return.

That last part is the real payoff and it is underplayed in most API framework documentation. Because the protocol is published and implemented widely, a client can be hand-rolled or taken from the list of available client libraries on the JSON:API site. A team writing a mobile client does not have to learn a bespoke query language, and a team with a Ruby backend and a JavaScript frontend is not negotiating a private contract.

The README also makes the point that the protocol is not complicated, which is what makes hand-rolling plausible. A JSON:API endpoint is a URL, a query string and a JSON body, so a client that does not want a dependency can still implement it in a few hundred lines, and that resilience is a property of choosing a published specification over an invented one.

The nested relationship handling is the other piece of the value. `?include=positions` fetches related records alongside the primary ones, and the writes work in the same shape, so a client can create an employee and their positions in one request. Doing that by hand means writing the nested write logic, the transaction boundaries and the error handling for each case, which is exactly the code a framework should be taking off your hands.

The repository is a gem, a documentation site and three example apps

The repository layout shows a project with a public surface that is larger than the library.

There is the gem itself, with `lib/`, `graphiti.gemspec`, `bin/` and `exe/`, a `spec/` directory for RSpec tests with a `.rspec` file, and a `Rakefile` with a `Guardfile` for file watching. Style is enforced with the standard gem, with a `.standard.yml` configuration and a Ruby style guide badge.

Then there are the examples, and they are the interesting part for anyone evaluating the library, because the directory listing shows three separate hosts: `examples/plain_ruby/`, `examples/rails/` and `examples/sinatra/`. The gem is not Rails-only, and the Sinatra example is the proof. Graphiti sits on top of your models and produces a Rack-style interface, so it can be dropped into a Rails app, a Sinatra app or plain Ruby, and the choice of host does not change the API shape.

The documentation is rendered from the repository. The markdown is in `docs/` and the Docusaurus site is in `website/`, with a `package.json` whose only script is semantic-release, and the release script runs from the root. To preview the docs locally the README gives a two step sequence.

bash
cd website
npm install
npm run start

Every documentation page has an Edit this page link at the bottom, which is a small thing that materially changes how a documentation contribution works, because you can go straight from a typo to a pull request.

There is also an `examples/` directory referenced by a separate employee directory example application, which the README points to as a full working API.

Semantic release, appraisal across dependency sets, and a maintained 1.x branch

The release automation is configured in detail in the root `package.json`, and it explains the shape of the version history you will see.

Semantic-release runs on four branches: `1.x`, `main`, a `beta` branch and an `alpha` branch, with the latter two marked as prerelease. The custom release rules are worth reading, because they define what a commit means. A commit message containing the breaking marker `!` triggers a major release, as does a commit with the breaking flag set. A revert is a patch. A `feat` is a minor. A `fix` or a `perf` type is a patch, while `build`, `ci`, `chore` and `docs` are set to release nothing at all.

That last set of rules is a deliberate signal to contributors. A commit marked as build, ci, chore or docs does not move the version number, which means a pull request that only touches documentation can be merged without consuming a release.

The `Appraisals` file and the `gemfiles/` directory indicate that the gem is tested against multiple dependency sets, which is the standard way to check that a library still works across supported versions of its dependencies rather than only with the newest.

The version history is busy at the moment. v2.2.0 was published on 2026-10-13, v2.1.3 on 2026-10-12 and v2.1.2 on 2026-10-12, and the last push to the repository was on 2026-10-18. The 1.x branch is still in the release configuration, so if you are on 1.x you are not abandoned, and `UPGRADING.md` at the repository root is the document to read before moving between major versions.

The licence is MIT, in a `LICENSE.txt` file, and the gem is published as `graphiti` on RubyGems with a RubyGems version badge and a Discord community link.

What Graphiti is not, and the question to answer first

The interesting question for an adopter is where the boundary sits, and the README gives the clearest answer by describing what it replaces rather than what it does.

Graphiti is not a model layer. It sits on top of your models, usually ActiveRecord, and does not replace them. That means your queries, associations, validations and scopes remain what you already have, and Graphiti translates them into the JSON:API request vocabulary. If your data layer is a NoSQL store or an internal service, the graphiti model layer is where you would write the adapter, and that work is not free.

It is also not a validation framework, an authentication layer or a background job system. The example resources have no authorisation code in them, and the specification covers API shape rather than policy. A production API still needs authentication, authorisation per resource, rate limiting and audit, and Graphiti does not claim to provide them.

The filtering semantics are the detail most likely to surprise. The query string is expressive, and expressions like `?filter[age][gt]=30` involve a grammar for operators, and each operator has a defined relationship to the underlying column type. Two frameworks that both support a greater-than filter can still differ in how they treat null values, how they combine filters, and what happens when a client sends an operator the type does not support. The 2.x releases are a good place to look for that behaviour, and the spec documentation and the example app are where you can confirm it before you commit to it for a client that already exists.

The statistics feature is also worth knowing about. The README lists statistics alongside filtering and sorting as something a Resource gets, which means a count or an aggregate over the filtered set is available without a separate endpoint. It is the sort of feature that turns a collection endpoint into something a dashboard can be built on directly.

Editorial conclusion

Adopt Graphiti if you are building a Ruby API and want the JSON:API shape, the query vocabulary and the relationship handling without hand writing controllers and serializers for every model. Do not adopt it if your clients need an idiosyncratic response format, because the point of the gem is to conform to a published specification rather than to your house style. Verify first that the JSON:API version you need is supported by the client libraries you plan to use, that the relationship and filtering semantics match what your front end expects, and that you read UPGRADING.md, because the project is on a 2.x line with a 1.x branch still maintained for releases.

Frequently asked questions

What is Graphiti and what does it replace?

It is a Ruby gem that sits on top of your models, usually ActiveRecord, and exposes them over a JSON:API compliant interface. You define Resources instead of controllers and serializers, and get filtering, sorting, pagination, sparse fieldsets, statistics and nested relationship reads and writes from one endpoint.

How do I define a Graphiti resource?

Subclass your application resource and declare attributes with their types, for example attribute :age, :integer, and declare relationships with has_many. That single class then serves filter, sort, page and include query parameters without extra code.

Does Graphiti only work with Rails?

No. The repository ships three example applications, in examples/plain_ruby/, examples/rails/ and examples/sinatra/, so the gem works with plain Ruby, Rails and Sinatra, since it produces a Rack-style interface over your models.

How is Graphiti released?

An automated semantic-release process runs on the 1.x, main, beta and alpha branches. Commits marked feat are minor releases, fix and perf are patches, a breaking change or a ! marker is a major release, and build, ci and docs commits release nothing.

Where are the Graphiti docs and how do I preview them?

The documentation is rendered from the repository, with the markdown in docs and the Docusaurus site in website. To preview it locally, cd website, run npm install, then npm run start.

Official sources

  1. graphiti-api/graphiti on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/graphiti-api-graphiti.svg)](https://hysenlabs.com/projects/graphiti-api-graphiti)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/graphiti-api-graphiti.svg)](https://hysenlabs.com/projects/graphiti-api-graphiti)

Was this useful?