DEV Community

Alessandro Rodi
Alessandro Rodi

Posted on • Edited on

Rails Blog with DatoCMS

DatoCMS is an amazing Headless CMS. I wrote already in the past how to integrate it into your Ruby On Rails application, and today I want to show you how to implement a real blog mimicking what is already done for other frameworks, for example nextjs.

Create a Dato project

You can clone the existing template for this tutorial. This template will setup all the models you need on Dato and create some blog posts.

Head to the Project Settings and save your GraphQL API Token for later.

Setup Rails App

Our Rails 8 application is set up using a simple rails new dato-blog command. In a few seconds you'll have your Rails 8 app ready and you can start it with bin/dev.

You can now include the dato-rails gem in your Gemfile and set the API token in your credentials as dato.api_token.

Image description

You can finally write a simple test to verify that the installation is successful:

# test/models/dato_queries_test.rb

require 'test_helper'

class DatoQueriesTest < ActiveSupport::TestCase
  def test_homepage_query
    homepage_query =
      GQLi::DSL.query do
        allPosts {
          title
        }
      end

    client = Dato::Client.new
    response = client.execute(homepage_query)
    assert_not_empty response.data.allPosts
  end
end
Enter fullscreen mode Exit fullscreen mode

What this test does, is to run a GraphQL Query and test the connection to DatoCMS.

You can always head to the CDA Playground in DatoCMS to create new queries.

Simple styling

Let's also set up Bootstrap for some simple, default styles. The Dato Rails integration does not provide any CSS, so you can easily integrate your favorite CSS Framework.

You can add Bootstrap in your application.html.erb to have some styling:

# app/views/layouts/application.html.erb

<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">


<body>
  <div class="container">
    <%= yield %>
  </div>
</body>
Enter fullscreen mode Exit fullscreen mode

Blog Post Page

Head to the Blog Example to see a Preview of the website we want to build. We will start with the single Blog Post page and then we will implement the Homepage.

The first thing you need is the query to fetch a single blog post.

You can create a models/dato_queries.rb file to include your queries.

# models/dato_queries.rb

module DatoQueries
  def homepage_query
    GQLi::DSL.query do
      allPosts {
        title        
      }
    end
  end

  def blog_post_query(slug)
    GQLi::DSL.query do
      post(filter: { slug: { eq: slug } }) {
        title
        slug
        date
        author {
          name
        }
      }
    end  
  end
end
Enter fullscreen mode Exit fullscreen mode

Before digging further, let's test it:

# test/models/dato_queries_test.rb

require 'test_helper'

class DatoQueriesTest < ActiveSupport::TestCase
  include DatoQueries

  def test_homepage_query
    client = Dato::Client.new
    response = client.execute(homepage_query)
    assert_not_empty response.data.allPosts
  end

  def test_blog_post_query
    client = Dato::Client.new
    response = client.execute(blog_post_query('mistakes-tourists-make-on-their-first-trip-abroad'))
    assert_not_empty response.data.post.title
  end
end
Enter fullscreen mode Exit fullscreen mode

Now that we have the queries, we need to implement the page.

Head to your routes.rb and add

resources :blog_posts, only: [:show], param: :slug
Enter fullscreen mode Exit fullscreen mode

and our controller will look like this:

# app/controllers/blog_posts_controller.rb

class BlogPostsController < ApplicationController
  include DatoQueries

  def show
    response = blog_post_query(params[:slug])
    render BlogPostComponent.new(response.data)
  end
end
Enter fullscreen mode Exit fullscreen mode

here is a simple implementation of the BlogPostComponent that renders the blog post.

# app/components/blog_post_component.rb

class BlogPostComponent < Dato::BaseComponent
end
Enter fullscreen mode Exit fullscreen mode
# app/components/blog_post_component.html.erb

<h1><%= data.post.title %></h1>
Enter fullscreen mode Exit fullscreen mode

You can now head to http://localhost:3000/blog_posts/mistakes-tourists-make-on-their-first-trip-abroad to see your post title.

You now rendered content from DatoCMS into your Rails Application. Let's add the header image now.

The first thing we need to do is to fetch the image from Dato. Our query will become:

def blog_post_query(slug)
  GQLi::DSL.query do
    post(filter: { slug: { eq: slug } }) {
      title
      slug
      date
      author {
        name
      }
      coverImage {
        responsiveImage(imgixParams: { fm: :jpg, fit: :crop, w: 2000, h: 1000 }) {
          ___ Dato::Fragments::ResponsiveImage
        }
      }
    }
  end
end
Enter fullscreen mode Exit fullscreen mode

dato-rails offers a GraphQL fragment to fetch images from DatoCMS in the right format to be displayed. Read more about it on DatoCMS. We are fetching the coverImage and using the existing ResponsiveImage fragment provided by dato-rails to get all the data we need.

With this updated query, we can now display the header image.

# app/components/blog_post_component.html.erb

<h1><%= data.post.title %></h1>

<div class="d-flex">
  <span class="mr-auto"><%= data.post.author.name %></span>
</div>

<%= render Dato::ResponsiveImage.new(data.post.coverImage.responsiveImage) %>
Enter fullscreen mode Exit fullscreen mode

Image description

From now on, you can look at the component code and the query code on the Github Repository to see the final version.

Homepage

Rendering the Homepage has nothing special at this point. You can check the Github Repo for the final code, but you basically need a root in your routes.rb, a controller, a new component, and a query.

# app/config/routes.rb

root "homepage#show"
Enter fullscreen mode Exit fullscreen mode
# app/controllers/homepage_controller.rb

class HomepageController < ApplicationController
  include DatoQueries

  def show
    render HomepageComponent.new(homepage_query)
  end
end
Enter fullscreen mode Exit fullscreen mode

Preview mode

We have the following concepts:

  • graphQL Query: logic to fetch data
  • controller: responsible for choosing a query and using it to renderer a component
  • component: responsible for the view logic.

DatoCMS offers a preview mode. You are responsible of deciding how and when a preview is displayed, this can be enabled for specific users of your application, or with a secret token in the URL. In our example we will use the preview parameter of the URL.

You can change the BlogPostsController as follows:

render Dato::Wrapper.new(BlogPostComponent, blog_post_query(params[:slug]), preview: params[:preview])
Enter fullscreen mode Exit fullscreen mode

to enable preview rendering when the preview parameter is passed.

Head to http://localhost:3000/blog_posts/mistakes-tourists-make-on-their-first-trip-abroad?preview=true

and you will see the Draft version of your Blog Post. You can edit it on DatoCMS and see the new version until you publish it.

Live mode

The second, interesting feature is live mode so you don't need to manually refresh your Browser window anymore. Similar to what we did before change your controller in:

render Dato::Wrapper.new(BlogPostComponent, blog_post_query(params[:slug]), preview: params[:preview], live: params[:live])
Enter fullscreen mode Exit fullscreen mode

to enable preview rendering when the preview parameter is passed.

Head to http://localhost:3000/blog_posts/mistakes-tourists-make-on-their-first-trip-abroad?preview=true&live=true

Caching

This is a very important topic since we don't want to hit DatoCMS every time we render a Blog Post page. The Dato::Wrapper component that you used previously will take care also of this and will cache the entire rendered component so that subsequent calls to the same page will be instant and will not require invoking DatoCMS again.

There are cases where you might not be using Dato::Wrapper, for such cases you can use the method data = execute_dato_query(your_query) to cache your query results automatically or directly Dato::Cache.fetch { ... } method similar to how you would use the Rails cache.

You should then configure Dato to automatically expire the cache when you publish your changes. Read more about it the README.

Top comments (0)