text: Vue SPA with Rails JSON:API using Graphiti
  Mikias Abera
  ・6 min read
  Standards are good, they make our lives easier. Graphiti and it's client library Spraypaint make it easy to build JSON:API compliant APIs that integrate seamlessly with front-end frameworks like Vue.
  I’m using graphiti in a production application to serve JSON requests to Vue components embedded in our Rails views. It's been reliable, flexible and a pleasure to use.
  In this tutorial we’ll walk through setting up Vue as an SPA with a JSON:API compliant Rails 5 API using graphiti. You can clone the demo app to see the finished product.
  # follow along git clone git@github.com:mikeeus/demos-rails-webpack.git cd demos-rails-webpack git checkout ma-vue-graphiti
  Set up Rails API with Webpacker
  Create Rails app with webpacker and Vue. I use postgresql but you can use any database you like.
  mkdir rails-vue rails new . --webpack=vue —database=postgresql rails db:create db:migrate
  And… done! That was easy right? Now we can move on to setting up graphiti to handle parsing and serializing our records according to the JSON API spec.
  Set up Graphiti
  Install graphiti, you can find the full instructions in the docs. We’ll need to add the following gems.
  # The only strictly-required gem gem 'graphiti' # For automatic ActiveRecord pagination gem 'kaminari' # Test-specific gems group :development, :test do gem 'rspec-rails' gem 'factory_bot_rails' gem 'faker' gem 'graphiti_spec_helpers' end group :test do gem 'database_cleaner' end
  We’ll need to add
  Graphiti::Rails to our Application controller so graphiti can handle parsing and serializing our requests. And we’ll register the
  Graphiti::Errors::RecordNotFound exception so we can return 404.
  # app/application_controller.rb class ApplicationController < ActionController::API include Graphiti::Rails # When #show action does not find record, return 404 register_exception Graphiti::Errors::RecordNotFound, status: 404 rescue_from Exception do |e| handle_exception(e) end end
  Now lets create a Post model.
  rails g model Post title:string content:string rails db:migrate
  We’ll also need to create a PostResource for graphiti and a controller to handle requests. Graphiti has a generator that makes it easy to set this up.
  rails g graphiti:resource Post -a index
  We’re going to declare our attributes and add
  ActionView::Helpers::TextHelper to format our Post content using
  simple_format so we can render it nicely on our client.
  class PostResource < Graphiti::Resource include ActionView::Helpers::TextHelper self.adapter = Graphiti::Adapters::ActiveRecord primary_endpoint '/api/v1/posts' attribute :title, :string attribute :content, :string do simple_format(@object.content) end end
  The generator will also create specs and a controller at
  app/controllers/posts_controller.rb. We’re going to move that to a namespaced folder
  app/api/v1 which will allow us to manage API versions in the future.
  # app/controllers/api/v1/posts_controller.rb module Api module V1 class PostsController < ApplicationController def index posts = PostResource.all(params) render jsonapi: posts end end end end
  We use
  render jsonapi: posts to render the posts according to the JSON:API spec so we can parse it on our client using graphiti's js client spraypaint.
  Now let’s add the route.
  # config/routes.rb Rails.application.routes.draw do namespace :api do namespace :v1 do resources :posts, only: :index end end end
  Alright looking good! All we need now is a client to consume our API.
  Setup Vue
  Webpacker comes with a generator for vue which you can see in the docs. It makes it super easy to add Vue or any other front-end framework like React or Angular to our application.
  bundle exec rails webpacker:install:vue
  Running the above will generate files at
  app/javascript
  We’re going to edit
  app/javascript/packs/application.js so that we can render our App.vue component.
  // app/javascript/packs/application.js import Vue from 'vue/dist/vue.esm' import App from '../app.vue' document.addEventListener('DOMContentLoaded', () => { const app = new Vue({ el: '#app', components: { App } }) })
  For now we can disregard the Vue component, we’ll fill it in later once we’ve setup our resources and endpoints.
  Setup Rails to Serve Static Content
  We can’t use our ApplicationController to serve our
  index.html page since it inherits from
  ActionController::Api and we want to keep it that way since our other controllers will inherit from it.
  In order to serve our index page for the SPA, we’ll use a
  PagesController that inherits from
  ActionController::Base so it can serve html files without issue.
  # app/pages_controller.rb class PagesController < ActionController::Base def index end end
  Next we’ll add a route for our homepage and redirect all 404 requests to it so our SPA can take care of business.
  # config/routes.rb Rails.application.routes.draw do root 'pages#index' get '404', to: 'pages#index' namespace :api do # ... end end
  Looking good, friends! Now let’s add our index.html page which will render our Vue component.
  # app/views/pages/index.html <%= javascript_pack_tag 'application' %> <%= stylesheet_pack_tag 'application' %> <div id="app"> <app></app> </div>
  It’s super simple: it just pulls in our javascript and stylesheets compiled by webpacker. Then we add a div with id=“app” and a nested
  <app></app> so our Vue wrapper component can pick it up and render the main component.
  This is the only Rails view we need to write for our application to work.
  Create Models on Client
  Usually when I build an SPA I’ll write services that use libraries like axios to make Ajax requests to the backend. Graphiti comes with a client library called spraypaint that handles parsing and serializing JSON:API payloads. It supports including associations, advanced filtering, sorting, statistics and more.
  Let’s set it up!
  yarn add spraypaint isomorphic-fetch
  Next let’s create an
  ApplicationRecord class that will store our spraypaint configuration.
  // app/javascript/models/application_record.js import { SpraypaintBase } from 'spraypaint'; export const ApplicationRecord = SpraypaintBase.extend({ static: { baseUrl: '', apiNamespace: '/api/v1', } })
  We set the
  baseUrl and
  apiNamespace to ‘’ and ‘/api/v1‘ respectively so that spraypaint uses relative paths and avoids CORS requests. It also namespaces our requests so we can manage API versions easily.
  Now the Post model
  // app/javascript/models/post.model.js import { ApplicationRecord } from './application_record'; export const Post = ApplicationRecord.extend({ static: { jsonapiType: 'posts' }, attrs: { id: attr(), title: attr(), content: attr() }, methods: { preview() { return this.content.slice(0, 50).trim() + '...' } } })
  We declare the
  id,
  title and
  content attributes. We also add a method to return a truncated preview of the content to show how we declare methods.
  The
  jsonapiType property is needed to generate the endpoint and parse and serialize the JSON payload.
  Now we’re ready to hook up the client to the API.
  Hook up SPA
  To hook everything up we’ll create a Vue component that uses our spraypaint models to communicate with our endpoints.
  // app/javascript/app.vue <template> <div> <h1>Posts</h1> <div v-{{error}}</div> <div class="loading" v-Loading...</div> <ul> <li v- <h3>{{post.title}}</h3> <p v-</p> </li> </ul> </div> </template> <script> import {Post} from './models/post.model' export default { data: function () { return { posts: [], error: null, loading: true } }, created() { Post.all() .then(res => { this.posts = res.data this.loading = false }) .catch(err => { this.error = err this.loading = false }) } } </script> <style scoped> h1 { text-align: center; } ul { list-style: none; } </style>
  Marvellous! If we add some posts in the console and run the application we will see the posts load and render in the page.
  Notice that we import our Post model and use it in our
  created() call like it was a Rails model. Calling
  Post.all() returns a promise that we can chain to set our
  posts and
  loading data properties. The spraypaint model can chain more useful methods like
  where and
  page.
  Post.where({ search: 'excerpt' }) .stats({ total: 'count' }) .page(1) .per(10) .order({ created_at: 'desc' }) .all() .then(res => ...)
  Spraypaint is a very powerful library that supports pagination, sorting, statistics, complex filtering and much more. You can check out the spraypaint docs for detailed guides.
  Conclusion
  Standards are good. Vue is awesome, and so is Rails. Gems and libraries like Graphiti and Spraypaint make it super easy to build scalable REST APIs that comply with said standards and integrate seamlessly with frameworks like Vue.
  I hope you enjoyed the article, don’t forget to like if you did. I would love to hear your thoughts or suggestions for other articles. Just leave a comment below :)
  Hello,
  so I have been working on vue and jsorm/spraypaint for some time and i am stuck with, like how to make a fetch request instead of Post and send some metadata also with it.
  like send data to customer/:id, where the id would be fetched from the url and send as meta data, while secrets from the URL to be send as normal data.
  import { ApplicationRecord } from './application_record';
  export const Post = ApplicationRecord.extend({
  static: {
  jsonapiType: 'posts/:id'
  },
  })