Ruby on Rails

OG images in
Ruby on Rails

after_commit hook, Active Job, URL saved on the record.

Where it plugs in

Rails has no equivalent of the edge image libraries the JavaScript world uses, so the usual answer is running Chrome next to your app through Grover or Ferrum — and babysitting it. A job that makes one HTTP call keeps Chrome off your servers entirely and retries on its own when the network blips.

app/jobs/generate_og_image_job.rb

require "net/http"

class GenerateOgImageJob < ApplicationJob
  queue_as :default
  retry_on Net::OpenTimeout, Net::ReadTimeout, wait: :polynomially_longer, attempts: 3

  ENDPOINT = URI("https://imageapi.dev/v1/images?wait=true")

  def perform(post)
    body = {
      template: "blog-og",
      modifications: {
        title: post.title,
        author: post.author.name,
        date: post.published_at.to_date.iso8601,
        site: "yourblog.com"
      }
    }

    response = Net::HTTP.start(ENDPOINT.host, ENDPOINT.port, use_ssl: true, read_timeout: 20) do |http|
      request = Net::HTTP::Post.new(ENDPOINT)
      request["Authorization"] = "Bearer #{Rails.application.credentials.image_api_key}"
      request["Content-Type"] = "application/json"
      request.body = body.to_json
      http.request(request)
    end

    response.value # raises on anything that is not 2xx
    post.update!(og_image_url: JSON.parse(response.body).fetch("url"))
  end
end

# app/models/post.rb
# after_commit -> { GenerateOgImageJob.perform_later(self) },
#   on: [:create, :update], if: -> { saved_change_to_title? && published? }
The trap

Use `after_commit`, not `after_save`. With `after_save` the job can be picked up before the transaction commits, and the worker loads a post whose new title is not in the database yet — so the image shows the old title.

What comes back

With ?wait=true the finished image arrives on the same connection — usually in about a second.

{
    "id": "img_01m08s3ez1zcv28h335g9923bw",
    "status": "completed",
    "url": "https://cdn.imageapi.dev/renders/img_01m08s3ez1zcv28h335g9923bw.png",
    "duration_ms": 912
}

What it costs

A hundred images a month free, then $9 for two thousand. The same post rendered twice returns the existing URL and uses no quota — so rebuilding your site costs nothing.

Other frameworks