Model or dataset
ruby-grape/grape-entity avatar
ruby-grape/grape-entity

grape-entity keeps API response shapes out of your Grape endpoints

An API focused facade that sits on top of an object model.

730 stars155 forksRubyMIT

At a glance

What is it?
grape-entity is a Ruby gem that adds an entity layer to API frameworks such as Grape, letting you declare exposed fields once and reuse them across endpoints, conditions and nested resources. It is a presentation layer, not a serializer of arbitrary objects, and the lookup rules are strict.
Who is it for?
grape-entity suits a Ruby API already built on Grape where the same resource shape appears in several endpoints and you want that shape declared in one place instead of copied into each handler. It does not suit a codebase that needs to present arbitrary objects or needs a full serializer with type coercion, because the exposure model is a declared field list with a strict lookup chain rather than a reflection based dump.
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 123 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 October 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

An API facade over the object model, not a replacement for it

grape-entity adds entity support to API frameworks such as Grape, and the README describes Grape's entity as an API focused facade that sits on top of an object model. That phrase sets the boundary. Your model classes stay where they are, with their own concerns and their own persistence, and the entity sits between them and the wire. Entities are a reusable means for converting Ruby objects to API responses, and they are the unit you reuse: define a Status entity once, then present it from a list endpoint, from a detail endpoint and from a nested resource without repeating the field list. The gem is MIT licensed and lives in the same organisation as Grape, with the repository holding lib/, spec/, a gemspec, a Rakefile and a CHANGELOG.md. It has no GitHub releases, and the last push was on 2026-06-23. The intended user is a Ruby developer with a Grape application who has noticed the same JSON structure being assembled by hand in a dozen endpoints, and who wants the response shape to be a named, reviewable object.

Field lookup is a strict chain, and the last step raises

The mechanism that decides everything about an entity is how it finds a field. The README documents the lookup in order. First it tries the entity instance method, named entity-instance.exposure. Next it tries the method on the object itself, object.exposure. Next it tries object.fetch(exposure). The last step is to raise an exception. That ordering is short and worth memorising, because it explains most surprises. If your model responds to the field, the object method wins for anything the entity itself does not define. If the object is a Hash and does not respond to the method, fetch is used, which means a missing key raises a key error rather than producing nil. There is an escape hatch for the Hash case. The exposure name is a Symbol by default, so for a Hash with stringified keys you can set the hash accessor at the entity class level and the members expose correctly.

ruby
class Status < GrapeEntity
  self.hash_access = :to_s

  expose :code
  expose :message
end

There is a companion escape hatch for the opposite problem. The safe option makes a field that cannot be evaluated come back as nil instead of raising.

ruby
expose :ip, safe: true

So a field you expose must actually exist on the object, unless you have marked it safe, and that is a deliberate design: it turns a typo into an exception at request time rather than a silent missing key in production.

Conditional exposure reads the runtime options hash

Grape endpoints pass options down to the presenter, and grape-entity uses those options to decide what a client sees. The :if and :unless keys accept several shapes, all documented with examples.

ruby
expose :ip, if: { type: :full }

expose :ip, if: lambda { |instance, options| options[:type] == :full } # exposed if the function evaluates to true
expose :ip, if: :type # exposed if :type is available in the options hash
expose :ip, if: { type: :full } # exposed if options :type has a value of :full

expose :ip, unless: ... # the opposite of :if

The distinction between the hash form and the symbol form matters. A symbol such as :type exposes the field whenever that key is present in the options at all, whatever its value, while the hash form with type: :full only exposes it when the value matches. The lambda form gets both the instance and the options hash, which is what you need for a rule that depends on the record itself. The README notes that runtime options are also available to :proc. The practical pattern is to keep the option names in one place, because every entity that reads them is coupled to whichever endpoint sets them, and an option that is never set simply means the field never appears.

Nesting, presenters and merging decide the shape of the response

Entities compose. A nested exposure is a block that opens up a sub hash.

ruby
expose :contact_info do
  expose :phone
  expose :address, using: API::Entities::Address
end

Nested blocks can carry their own conditions, so an email address can appear only for some callers while the rest of the contact block always renders. To point at another entity, use the :using option with the class and an :as alias to control the key name.

ruby
expose :replies, using: "API::Entities::Status", as: :responses

The string form of the class exists to break circular dependencies, where Status exposes replies using Status and a plain class reference would not load in the right order. The README also says you should not derive your model classes from Grape::Entity but expose them through a presenter instead, keeping the domain clean. The merge option is the one that changes the shape rather than the content. With merge: true the exposed fields are merged into the surrounding hash or root instead of being nested under their own key.

ruby
expose :status, merge: true

The result of that merge is a flat hash rather than a nested one, which is how you get an address inlined into a contact object. Collections have their own set of controls. The root method wraps an object or a collection under a root key, with the singular form available for the individual item.

ruby
root 'users', 'user'
expose :id, :name, ...

By default each object in a collection is wrapped in its own instance of the entity class, and present_collection changes that so the whole collection is wrapped once, with the collection name optional and defaulting to items.

Inheritance is how you get variants without duplication

The README frames entities as the way to build ever larger responses using inheritance, and the example shows the pattern. A base Status entity declares the common exposures, including a formatted timestamp helper and a nested contact block, and a subclass adds only what differs.

ruby
module API
  module Entities
    class StatusDetailed < API::Entities::Status
      expose :internal_id
    end
  end
end

That gives you a public shape and a privileged shape from one definition, without the second one copying the first. The base class in the README example also shows the other mechanisms at once: a format_with helper that converts a timestamp with iso8601, an exposure with documentation metadata describing its type and description, a conditional ip field, a lambda condition on the user type, a merged location, a computed digest built from the status text, and a with_options block that applies the timestamp format to several fields at once. That last one matters for consistency, since formatting rules applied in one place are far easier to keep uniform than the same format call repeated on each field. The table of contents in the README also lists aliases, unexpose, default values, format before exposing, expose nil, overriding exposures and the exposure DSL for reusing a set of exposures across entities, so the surface is broader than the basics.

How it differs from presenting by hand or with a serializer

Three alternatives sit nearby. The first is doing nothing special, building the hash in the endpoint and handing it to Grape. That is the fastest to start and the worst to maintain, since the shape of a resource ends up duplicated across handlers and nobody can see the contract in one place. The second is a general purpose serializer that reflects over the object and dumps its attributes. The difference in approach is fundamental. grape-entity exposes only the fields you declare, in the order you declare them, with names you choose, and it supports per-call conditions that a reflective dump cannot express without extra configuration. What you lose is automatic coverage: a new column on the model does not appear in the response until you add an exposure, which is the point for an API contract, and a nuisance if you wanted the dump. The third is using Grape's own plain presenter support without the entity gem, which is thinner but loses nesting, conditions and reuse. Grape-entity is best understood as the opinionated choice among these: fewer fields, more declaration, one place to read the contract.

MIT terms, no tagged releases, and an upgrading file to read

The gem is MIT licensed, which is the permissive end of the Ruby ecosystem and means no copyleft obligation on the application that embeds it, though your own review should cover how you distribute it. The repository ships both a CHANGELOG.md and an UPGRADING.md, and that second file is a good sign: it tells you that the maintainers expect behaviour to change between versions and have committed to writing down how. There are no GitHub releases on the repository, so versions are distributed through RubyGems and the tags on the default branch are the record of what shipped. That combination, a changelog and an upgrading guide without tagged releases, means you should read both before a version bump rather than relying on release notes in the usual place. The project is not archived, and its last push was on 2026-06-23, so it is a maintained dependency rather than an abandoned one, but the documentation in the README is structured around the entity DSL rather than around a quick start, and it points at testing helpers, preloading associations and organisation of entity files for larger codebases. For a team adopting the gem, those sections are where the day to day decisions live.

Editorial conclusion

grape-entity suits a Ruby API already built on Grape where the same resource shape appears in several endpoints and you want that shape declared in one place instead of copied into each handler. It does not suit a codebase that needs to present arbitrary objects or needs a full serializer with type coercion, because the exposure model is a declared field list with a strict lookup chain rather than a reflection based dump. Before you adopt it, check the lookup order against your models, since a missing method raises rather than returning nil unless you ask for safe exposure, and read UPGRADING.md and CHANGELOG.md in the repository because the gem has no GitHub releases and its last push was on 2026-06-23.

Frequently asked questions

What is grape-entity and what is it for?

grape-entity is a Ruby gem that adds entity support to API frameworks such as Grape. Entities are a reusable means for converting Ruby objects to API responses, so a field list is declared once and reused across endpoints, with conditional fields, nested entities and inheritance.

How does grape-entity look up an exposed field?

It tries the entity instance method first, then the method on the object, then object.fetch(exposure), and raises an exception if none of those work. For a Hash with stringified keys you can set self.hash_access = :to_s on the entity class, and safe: true makes an unevaluable field come back as nil instead of raising.

How do I expose a field only for some requests in grape-entity?

Use :if or :unless with a hash such as if: { type: :full }, a symbol such as if: :type, or a lambda that receives the instance and the options hash. The README notes that runtime options are also available to :proc.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. ruby-grape/grape-entity on GitHub
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/ruby-grape-grape-entity.svg)](https://hysenlabs.com/projects/ruby-grape-grape-entity)

Was this useful?