Skip to content

Instantly share code, notes, and snippets.

@Coridyn
Forked from kohheepeace/memo.md
Created April 27, 2024 06:28
Show Gist options
  • Save Coridyn/6797d197c76d8368a53e595f369fd882 to your computer and use it in GitHub Desktop.
Save Coridyn/6797d197c76d8368a53e595f369fd882 to your computer and use it in GitHub Desktop.
Rails ajax comparison (fetch, Rails.ajax, axios, @rails/request.js, Turbo)

Rails ajax comparison (fetch, Rails.ajax, axios, @rails/request.js, Turbo)

I wrote this gist because I felt that the Rails documentation was lacking a description of ajax requests.

πŸ“Œ Options for ajax request

There are various ways to send ajax requests in Rails.

  1. Browser default Fetch API
  2. Rails.ajax (No Official docs and request for docs)
  3. http client like axios
  4. @rails/request.js πŸ‘ˆ I'm using this one now !
  5. Turbo πŸ‘ˆ I'm using this one now !

1. Fetch API

[pros]:

  • Browser default http client, so no need to install something.

[cons]:

[Usage]:

const csrfToken = document.getElementsByName("csrf-token")[0].content;

fetch("/posts", {
  method: "POST",
  headers: {
    "X-CSRF-Token": csrfToken,          // πŸ‘ˆπŸ‘ˆπŸ‘ˆ you need to set token
    "Content-Type": "application/json", // πŸ‘ˆπŸ‘ˆπŸ‘ˆ To send json in body, specify this
     Accept: "application/json",         // πŸ‘ˆπŸ‘ˆπŸ‘ˆ Specify the response to be returned as json. For api only mode, this may not be needed
  },
  body: JSON.stringify({ title, body }),
})
  .then((response) => response.json())
  .then((data) => {
    console.log("Success:", data);
  })
  .catch((error) => {
    console.error("Error:", error);
  });

Ref: official docs about how to get csrf token

2. Rails.ajax

source code

[pros]:

  • No need to set csrf token manually
  • Code become short
  • Turbolikns support

[cons]:

  • No official docs
  • you need to send data as URL scheme

[Usage]:

Rails.ajax({
  url: "/posts", // or "/posts.json"
  type: "POST",
  data: `post[title]=${title}&post[body]=${body}`, // πŸ‘ˆπŸ‘ˆπŸ‘ˆ You need to pass data in URL scheme
  success: function (data) {
    console.log("Success:", data);
  },
  error: function (error) {
    console.error("Error:", error);
  },
});

In app/controllers/posts_controller.rb

# POST /posts or /posts.json
  def create
    @post = Post.new(post_params)

    respond_to do |format|
      if @post.save
        format.html { redirect_to @post, notice: "Post was successfully created." }
        format.json { render :show, status: :created, location: @post }
      else
        format.html { render :new, status: :unprocessable_entity }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end

In console, response of /posts will look like this.

Success: Turbolinks.clearCache()
Turbolinks.visit("http://localhost:3000/posts/rHBcQ9K-4bssCxz7VB4VXw", {"action":"replace"})

In console, response of /posts.json will look like this.

Success: {id: 139, title: "This is a title", body: "This is a body", user_id: 1, created_at: "2021-06-16T23:28:53.563Z", …}

3. axios

axios is better version of Fetch API (πŸ‘ˆ just a opinion). You can write api fetch code cleaner way.

[pros]:

  • Widely used
  • Clean code

[cons]:

[Usage]:

  1. Create custom axios file src/custom-axios.js
import axios from "axios";

const instance = axios.create({
	headers: {
		"Content-Type": "application/json",
		Accept: "application/json",
	},
});

instance.interceptors.request.use(
	function (config) {
		const csrfToken = document.getElementsByName("csrf-token")[0].content;
		config.headers["X-CSRF-Token"] = csrfToken;

		return config;
	},
	function (error) {
		// Do something with request error
		return Promise.reject(error);
	}
);

export default instance;
  1. In js file where you want to call api some-js-file.js
import axios from "../src/custom-axios";

...

axios
  .post("/posts", { title, editorData })
  .then((data) => {
    console.log("Success:", data);
  })
  .catch((error) => {
    console.error("Error:", error);
  });

Refs of axios

Similar http client like axios

4. @rails/request.js

Source

Rails Request.JS encapsulates the logic to send by default some headers that are required by rails applications like the X-CSRF-Token. https://github.com/rails/requestjs-rails

[pros]:

[cons]:

  • Not yet found...

[Usage]:

yarn add @rails/request.js
import { FetchRequest } from '@rails/request.js'

window.FetchRequest = FetchRequest; // πŸ‘ˆ I'm asigning it to window.

const request = new FetchRequest('post',
  'localhost:3000/my_endpoint',
  { body: JSON.stringify({ name: 'Request.JS' }) }
)
const response = await request.perform()
if (response.ok) {
  const body = await response.text
  // Do whatever do you want with the response body
  // You also are able to call `response.html` or `response.json`, be aware that if you call `response.json` and the response contentType isn't `application/json` there will be raised an error.
}

*There is a gem of Request.JS for Rails for Asset Pipeline.

5. Turbo

[Source]

By using Turbo, you don't need to write Javascript, but it works like javascript.

πŸ‘‡ The following Todo apps, which normally require javascript, can be implemented without (or with little) need to write Javascript by using Turbo.

*Credit: https://www.colby.so/posts/turbo-rails-101-todo-list

[Pros]:

  • You don't need to write (much) javascript

[Cons]:

  • Unfamiliar syntax, grammer
  • For simple processes, it may be simpler to write javascript as usual.

βœ… Which one should I use?

In this issue, rails team member encourage you to use fetch api, so I decided to use fetch api. rails/rails#38191 (comment)

I decided to use Rails.ajax because I wanted to use Turbulinks. I know that both Rails.ajax and Turbolinks are supposed to be deprecated, but it's easier to use this method for now, and I felt it would be easy to rewrite even if they were deprecated.

I'm using axios now. There is a cons of axios bundle size, however, I chose this one because it gives me the feeling that I am writing clean code.

I am currently using both Turbo and @rails/request.js.

Turbo:

  • Search modals like Algoria (with complex UI changes).

@rails/request.js:

  • Requests to a very simple backend, like "Fav" functionality

πŸ“Œ About CSRF

CSRF tokens are required if cookies or sessions are used. https://security.stackexchange.com/questions/166724/should-i-use-csrf-protection-on-rest-api-endpoints/166798#166798

πŸ“Œ Refs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment