I wrote this gist because I felt that the Rails documentation was lacking a description of ajax requests.
There are various ways to send ajax requests in Rails.
- Browser default Fetch API
Rails.ajax
(No Official docs and request for docs)- http client like axios
- @rails/request.js π I'm using this one now !
- Turbo π I'm using this one now !
[pros]:
- Browser default http client, so no need to install something.
[cons]:
- You need to set csrf token everytime. But you can wrap fetch api.
- Code is not clean (π just an opinion)
[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
[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",Β β¦}
axios is better version of Fetch API (π just a opinion). You can write api fetch code cleaner way.
[pros]:
- Widely used
- Clean code
[cons]:
- Bundle size: 11.2kB [email protected] when compressed using GZIP.
[Usage]:
- 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;
- 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);
});
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]:
- npm from rails repo
- Clean code
- Better new version of
Rails.ajax
- Bundle size: 1.9kB(MINIFIED + GZIPPED)
[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.
[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.
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
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
fetch wrapper for rails
https://github.com/MiguelSavignano/fetch-rails/blob/master/src/fetch-rails.ts
and Should I use window.variable or var?