{"id":22052686,"url":"https://github.com/digitalnz/contentful-redis","last_synced_at":"2026-05-03T21:34:53.641Z","repository":{"id":56843816,"uuid":"145938503","full_name":"DigitalNZ/contentful-redis","owner":"DigitalNZ","description":"Contentful CMS API caching using Redis","archived":false,"fork":false,"pushed_at":"2021-12-15T15:01:12.000Z","size":55,"stargazers_count":1,"open_issues_count":5,"forks_count":0,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-06-01T22:53:15.452Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DigitalNZ.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-08-24T03:36:32.000Z","updated_at":"2021-06-15T03:06:44.000Z","dependencies_parsed_at":"2022-09-05T14:51:00.848Z","dependency_job_id":null,"html_url":"https://github.com/DigitalNZ/contentful-redis","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/DigitalNZ/contentful-redis","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DigitalNZ%2Fcontentful-redis","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DigitalNZ%2Fcontentful-redis/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DigitalNZ%2Fcontentful-redis/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DigitalNZ%2Fcontentful-redis/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DigitalNZ","download_url":"https://codeload.github.com/DigitalNZ/contentful-redis/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DigitalNZ%2Fcontentful-redis/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265444857,"owners_count":23766436,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-30T15:13:57.591Z","updated_at":"2026-05-03T21:34:53.590Z","avatar_url":"https://github.com/DigitalNZ.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ContentfulRedis\nA lightweight read-only contentful API wrapper which caches your responses in Redis.\n\n# Features\n- Lightweight easy to configure ruby contentful integration.\n- Faster load times due to having a Redis cache.\n- All content models responses are cached.\n- Webhooks update\n- Multiple space support\n- Preview and production API support on a single environment\n\n## WIP\n- logger\n- Experiment Redis size optimization\n- auto clean up of dead Redis keys\n- code clean up\n\nContentfulRedis also supports multiple API endpoints(preview and published) within a single application.\n\n## Installation\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'redis-store' # optional\ngem 'contentful_redis'\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install contentful_redis\n\n## Configuration\n\nHeres a default example, however, I will go over all of the individual configurations options below\n\n```ruby\n# config/initializers/contentful_redis.rb\nContentfulRedis.configure do |config|\n  config.default_env = :preview # unless production env\n  config.model_scope = 'Contentful' # models live in a Contentful module\n  config.logging = true\n\n  config.spaces = { \n    test_space: {\n      id: 'xxxx',\n      access_token: 'xxxx',\n      preview_access_token: 'xxxx'\n    }\n  }\n\n  config.redis = Redis::Store.new(\n    host: (ENV['REDIS_HOST']) || 'localhost',\n    port: 6379,\n    db: 1,\n    namespace: 'contentful'\n  )\n\n```\n\n### Spaces (required)\nContentful Redis supports multiple space configurations with your first space being the default\n\n```ruby\n# config/initializers/contentful_redis.rb\nContentfulRedis.configure do |config|\n  config.spaces = { \n    test_space: {\n      id: 'xxxx',\n      access_token: 'xxxx',\n      preview_access_token: 'xxxx'\n    },\n\n    test_space_2: {\n      id: 'xxxy',\n      access_token: 'xxxx',\n      preview_access_token: 'xxxx'\n    }\n  }\n```\n\nTo use a different space for a model override the classes `#space` method\n\n```ruby\n# app/models/my_model.rb\nclass MyModel \u003c ContentfulRedis::ModelBase\n  \n  # override default space\n  def self.space\n    ContentfulRedis.configuration.spaces[:test_space_2]\n  end\nend\n```\n\n### Redis (required)\nThere are various ways you can integrate with Redis.\nI suggest using [redis-store](https://github.com/redis-store/redis-store) unless your application already has Redis adapter installed.\nI recommend having a separate Redis database for all of your contentful data so that you can isolate your application Redis from your content.\n\n```ruby\n# config/initializers/contentful_redis.rb\nContentfulRedis.configure do |config|\n  config.redis = Redis::Store.new(\n    host: (ENV['REDIS_HOST']) || 'localhost',\n    port: 6379,\n    db: 1,\n    namespace: 'contentful'\n  )\nend\n```\n\n### Default env\n\nIf unset the default call is to the `:published` data. however, setting default_env to `:preview` will request to the preview API.\nThe Find methods can have an additional argument to force the non-default endpoint.\n\n```ruby\n# config/initializers/contentful_redis.rb\nContentfulRedis.configure do |config|\n  # if unset defaults to :published\n  config.default_env = :preview\nend\n```\n\n### Model scope\nSet the scope for where your models live.\n\n```ruby\n# config/initializers/contentful_redis.rb\nContentfulRedis.configure do |config|\n  config.model_scope = 'Contentful'\nend\n\n# app/models/contentful/page.rb\nmodule Contentful\n  class Page \u003c ContentfulRedis::ModelBase\n\n  end\nend\n```\n\n## Models\nAll content models will need to be defined, prior to integration especially when using references.\nThe example model we are going to define has a slug(input field) and a body(references other content models)\n\n```ruby\n# app/models/page.rb\nclass Page \u003c ContentfulRedis::ModelBase\n  # allows the field to be queried from\n  define_searchable_fields :slug\n\n  # Set default readers which can return nil\n  attr_reader: :slug\n  \n  # define your desired return types manually\n  def body\n    @body || []\n  end\nend\n```\n\n### Querying\nAll content models are found by their contentful ID. Contentful Redis only stores only one cache of the content model\nThis Redis key is generated and is unique to a content model, space and endpoint.\n\nIn these examples within my application I have created a class called `Contentful::Page`\n\n```ruby\n  Contentful::Page.find('\u003ccontentful_uid\u003e')\n```\n\nContentful Redis does not store a duplicate object from searchable attributes,\nInstead, it builds a glossary of searchable attributes mapping to their content models ids.\nThese attributes are defined in the class declaration as `define_searchable_fields :slug`\n\n```ruby\n  Contentful::Page.find_by(slug: 'about-us') \n```\n\n### Deleting\nAn entry is done by calling destroy on a ContentfulRedis model object or destroy by passing id. This will delete all the redis keys, find and search keys for the entry.\n\nIn these examples within my application I have created a class called `Contentful::Page`\n\n```ruby\n  Contentful::Page.destroy('\u003ccontentful_uid\u003e')\n```\n\nor\n\n```ruby\n  page = Contentful::Page.find('\u003ccontentful_uid\u003e')\n  page.destroy\n```\n\n### Query optimization\nThere are three optimizations that can be made on a query.\nDue to the nature of content trees and Contentful's references, we need to be able to control which references to follow.\n\nIn these examples within my application I have created a class called `Contentful::Page`\n\n#### Only\nFetch all selected reference(s)\n\n```ruby\n  # Content model which has child references called descendants\n  Contentful::Page.find('\u003ccontentful_uid\u003e', only: :descendants)\n  Contentful::Page.find_by(slug: '\u003ccontentful_slug\u003e', options: { only: :descendants } )\n\n  # Supports Arrays as well\n  Contentful::Page.find('\u003ccontentful_uid\u003e', only: [:descendants])\n  Contentful::Page.find_by(slug: '\u003ccontentful_slug\u003e', options: { only: [:descendants] } )\n```\n\n#### Except\nFetch all references except the targeted field(s)\n\n```ruby\n  # Content model which has many references including child references called descendants\n\n  Contentful::Page.find('\u003ccontentful_uid\u003e', except: :descendants)\n  Contentful::Page.find_by(slug: \u003ccontentful_slug\u003e, options: { except: :descendants } )\n\n  # Supports Arrays as well\n  Contentful::Page.find('\u003ccontentful_uid\u003e', except: [:descendants])\n  Contentful::Page.find_by(slug: \u003ccontentful_slug\u003e, options: { except: [:descendants] } )\n```\n\n#### Depth\nLimit the reference traversal depth.\nThe value is the amount of edges which to travese down.\n\n```ruby\n  Contentful::Page.find(\u003ccontentful_uid\u003e, depth: 1 )\n  Contentful::Page.find_by(slug: \u003ccontentful_slug\u003e, options: { depth: 1 })\n```\n\n### Content model overriding\n\nClasses should match their content model name, however, if they don't you can override the classes `#name` method.\n\n```ruby\n# app/models/page.rb\nclass Page \u003c ContentfulRedis::ModelBase\n\n  # Overwrite to match contentful model using ruby class syntax\n  def self.name\n    'NameThatMatchesContentfulModel'\n  end\nend\n```\n\n## Webhooks\n\nInstead of creating rails specific implementation it is up to the developers to create your controllers and manage your webhook into your applications.\nSee the [Contentful webhooks docs](https://www.contentful.com/developers/docs/concepts/webhooks/) creating your own\n\nThese are the setings which we have found to work the best\nWe do need need any of the assests as they are handled by contentfuls image server.\n\n### Staging / UAT Update Settings\n|              | Create | Save | Autosave | Archive | Unarchive | Publish | Unpublish | Delete |\n|--------------|--------|------|----------|---------|-----------|---------|-----------|--------|\n| Content Type |        | ✔︎    |          |         |           | ✔︎       |           |        |\n| Entry        |        | ✔︎    | ✔︎        |         | ✔︎         | ✔︎       |           |        |\n| Asset        |        |      |          |         |           |         |           |        |\n|              |        |      |          |         |           |         |           |        |\n\n### Staging / UAT Update Delete\n|              | Create | Save | Autosave | Archive | Unarchive | Publish | Unpublish | Delete |\n|--------------|--------|------|----------|---------|-----------|---------|-----------|--------|\n| Content Type |        |      |          |         |           |         |           | ✔︎      |\n| Entry        |        |      |          | ✔︎       |           |         |           | ✔︎      |\n| Asset        |        |      |          |         |           |         |           |        |\n|              |        |      |          |         |           |         |           |        |\n\n\n### Production Update Settings\n|              | Create | Save | Autosave | Archive | Unarchive | Publish | Unpublish | Delete |\n|--------------|--------|------|----------|---------|-----------|---------|-----------|--------|\n| Content Type |        |      |          |         |           | ✔︎       |           |        |\n| Entry        |        |      |          |         |           | ✔︎       |           |        |\n| Asset        |        |      |          |         |           |         |           |        |\n|              |        |      |          |         |           |         |           |        |\n\n### Production Delete Settings\n|              | Create | Save | Autosave | Archive | Unarchive | Publish | Unpublish | Delete |\n|--------------|--------|------|----------|---------|-----------|---------|-----------|--------|\n| Content Type |        |      |          |         |           |         | ✔︎         | ✔︎      |\n| Entry        |        |      |          | ✔︎       |           |         | ✔︎         | ✔︎      |\n| Asset        |        |      |          |         |           |         |           |        |\n|              |        |      |          |         |           |         |           |        |\n\nRequired Contentful webhooks to update the Redis cache are:\n```json\n{\n  \"id\": \"{ /payload/sys/id }\",\n  \"environment\": \"{ /payload/sys/environment/sys/id }\",\n  \"model\": \"{ /payload/sys/contentType/sys/id }\"\n}\n```\n\nWhen pushing text attributes make sure you are using the correct language endpoint.\n```json\n{\n  \"title\": \"{ /payload/fields/title/en-US }\",\n  \"slug\": \"{ /payload/fields/slug/en-US }\",\n}\n```\n\n### Webhook Controllers\n\n#### Rails\n```ruby\n# app/controllers/contentful/webhook_controller.rb\nmodule Contentful\n  class WebhookController \u003c ApplicationController\n    # before_action :some_auth_layer\n\n    def update\n      payload = JSON.parse request.raw_post\n      \n      contentful_model = ContentfulRedis::ClassFinder.search(payload['model'])\n      contentful_model.update(payload['id'])\n\n      render json: { status: :ok }\n    end\n\n    def delete\n      payload = JSON.parse request.raw_post\n      \n      contentful_model = ContentfulRedis::ClassFinder.search(payload['model'])\n      contentful_model.destroy(payload['id'])\n\n      render json: { status: :ok }\n    end\n  end\nend\n\n# config/routes\n#...\nnamespace :contentful do\n  resource 'webhooks', only: :update, :delete\nend\n# ...\n```\n\n#### Other\nFeel free to create a PR for other ruby frameworks :)\n\n## Content Seeding\nSeeding the data is a great way to get started in building your content models, there is a couple of ways this can be done.\n\nCreate a service object inside your application and get it to fetch the root pages of your content tree by their ID.\nThe find method will build your Redis cache as well as link your content models with their searchable fields.\n\n```ruby\n# app/services/seed_content.rb\nclass SeedContent\n  # trigger a cascading content model seeding process\n  def call\n    ['xxContentfulModelIdxx'].each do |page|\n      Contentful::Page.find(page)\n    end\n  end\nend\n```\n\n## Development\nAfter checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.\n\nTo install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a Git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/contentful-redis.\n\n## License\n\nThe gem is available as open source under the terms of the [GNU General Public License](https://www.gnu.org/licenses/#GPL)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdigitalnz%2Fcontentful-redis","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdigitalnz%2Fcontentful-redis","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdigitalnz%2Fcontentful-redis/lists"}