Asynchronous HTTP requests in Rails
[2020-05-10 Update: If you’re more confortable following along a video, I’ve recorded a screencast.]
Alright. Let’s say you have a list of essays from your app’s blog. They’re sorted from newest to oldest. But you’d like your readers to have the ability to filter them by topic.
You’d start by adding some buttons at the top of your feed. Now, when readers click the button
Ruby, you’d like your feed to only display essays with the category
Ruby. All of this, without reloading the whole page.
Something like this 👇
This sounds like a job for some asynchronous HTTP requests.
The Rails bit
Let’s dive right in and add a new route:
This will be the endpoint our requests ping. Now, here’s a basic controller:
And the corresponding view:
- Each filter has an ID.
- The DOM element embedding the list of posts has an ID.
- The list of posts is in a partial: this will make server-side rendering much easier.
Speaking of partial, here’s
We’re looping over a collection of posts. For each post, we generate a clickable title and an excerpt.
Building asynchronous requests in Rails with
The basic syntax for
- Find and store the DOM element embedding my list of posts.
- Find and store every filter-type button on our page.
- Add an event listener on each button that’ll trigger our asynchronous request.
- Store the URL where I’ll send my asynchronous request (i.e
- Fetch then handle the response from my controller.
fetch() takes an URL as the first parameter. We’ve already done that. Now, we’ll add details to the second parameter (it’s called the
What’s happening here?
- I specify the HTTP request I want to do.
- I fill in the request’s headers with:
- my CRSF token (so Rails doesn’t think your request is illegitimate)
- the type of content I expect to receive (in our case, HTML)
- I specify that the request comes from our app.
This bit will ping our
Blog::PostsController#index (through the route stored in
actionUrl). Remember, we want our controller to filter our essays based on the category sent through our
fetch() method. So we need to add that category to our request:
I’ve added a parameter
category to my
filterPosts function. Then, I’m building my some Rails
category and, I’m adding them to my
actionUrl. Now, I can access my
category in my
Blog::PostsController through my
Filtering data in our controller
I can update our controller based on the presence of the
category key in my
Let’s break it down:
params['category']is absent, my controller returns a list of posts (
index.html.erbwhich renders the partial
params['category']is present, my controller directly returns the partial
posts_list.html.erbwith the filtered list of
If your partial is somewhere else (like in your
shared directory, just give your controller the relative path - i.e.
Handling the response from our controller
The first thing I like to do, is to check the HTTP status sent from my controller. If it’s a
200, I’ll use the response to replace the posts list in my view.
What did I do? I applied the
text() method to my controller
text() takes this stream and turns it into a UTF-8-encoded string.
In our case, here’s what happens:
- Our controller returns a partial with a loop in it.
text()kicks in and turns the stream into a string (which is a stringified version of our HTML).
- I assign
content, and I replace the
postsListsection of my DOM with the stringify partial.
response status is an error (like a
500), I can show the user an error. Whatever’s tickling your fancy.
That’s is for today folks! I hope you enjoyed it as much as I did.
If anything seems odd, ping me on Twitter or create an issue on GitHub.
Rémi - @mercier_remi
👋 Doctor Who fans. ↩