DEV Community

Rails Designer
Rails Designer

Posted on • Originally published at railsdesigner.com

How to Send Requests from Stimulus Controllers

This article was originally published on Rails Designer


When working with the Hotwire stack, most can be done via typical post or get-requests to the server. It then sends a HTML response back to the browser and everybody is happy. But sometimes you need to make a request in your Stimulus controller, either to your own back-end or an third-party API.

Working with a recent client, there was a need to capture events from a video player. Luckily JavaScript has all the tools to make this really easy. Let's look at a basic example that captures events by your users:

// app/javascript/controllers/video_player.js
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  async play() {
    // play logic here

    await fetch("/events", {
      method: "POST",
      headers: {
        "X-CSRF-Token": document.querySelector('[name="csrf-token"]').content,
        "Content-Type": "application/json"
      },
      body: JSON.stringify({ event: "start_play" })
    })
  }
}
Enter fullscreen mode Exit fullscreen mode

This is using JavaScript's Fetch API. Along with the body that sends the event name start_play to the /events endpoint, extra headers are sent as well. Easy to follow, right? But things can be made simpler when using the @rails/request.js library. Let's refactor!

// app/javascript/controllers/video_player.js
import { Controller } from "@hotwired/stimulus"
import { post } from "@rails/request.js" // make sure it's installed

export default class extends Controller {
  async play() {
    // play logic here

    await post("/events", {
      body: JSON.stringify({ event: "start_play" })
    })
  }
}
Enter fullscreen mode Exit fullscreen mode

The library is taking care of sending the required headers (like X-CSRF-Token) for Rails applications for you. Sweet!

Refactor into helper method

It's very likely events need to be captured in more places than the one controller, when play was paused or ended. To make that easy, let's refactor the inline code from above controller into a separate function like I did for this client.

// app/javascript/controllers/helpers/events.js
export const trackEvent = async (eventName, metadata = {}) => {
  return post("/events", {
    body: JSON.stringify({
      event: eventName,
      ...metadata
    })
  })
}
Enter fullscreen mode Exit fullscreen mode

And this is how it could be used:

import { Controller } from "@hotwired/stimulus"
import { trackEvent } from "helpers/events"

export default class extends Controller {
  async play() {
    // play logic here

    await trackEvent("start_play", { resource_id: this.videoIdValue })
  }
}
Enter fullscreen mode Exit fullscreen mode

The new trackEvent method can be simply be imported and used wherever needed. An additional metadata object was added to keep track of more data-points beyond the event name.

And there you have it, an easy way to send request from a stimulus controller to track events.

Top comments (0)