Rails Request.JS

repository·main·Indexed 19 days ago

https://github.com/rails/request.js

A tiny Fetch API wrapper for Rails applications that simplifies HTTP requests by automatically handling CSRF tokens, Turbo Stream responses, and JSON serialization. It provides the FetchRequest and FetchResponse classes, shorthand methods for common HTTP verbs (get, post, put, patch, destroy), and RequestInterceptor for managing authentication headers.

Tokens
3.3K
Snippets
12
Records
16
Agent score
66%

What's inside @rails/request.js

  1. Handle Turbo Stream responses

    main

    Request.JS automatically processes Turbo Stream responses. To ensure this works, you must ensure the window.Turbo global variable is set. If you are using @hotwired/turbo-rails version 7.0.0-beta.6 or later, this is handled automatically. Otherwise, set it manually:

    import { Turbo } from "@hotwired/turbo-rails"
    window.Turbo = Turbo

    Request.JS also uses Turbo's fetch to include the X-Turbo-Request-ID header.

    import { Turbo } from "@hotwired/turbo-rails"
    window.Turbo = Turbo
  2. Install @rails/request.js

    main

    Depending on your Rails asset pipeline, use one of the following methods to install Rails Request.JS:

    Asset Pipeline

    Install the requestjs-rails gem and follow the instructions provided by that gem.

    Webpacker/Esbuild

    Use npm or yarn to add the package to your project.

    ### npm
    ```bash
    npm i @rails/request.js

    yarn

    yarn add @rails/request.js
  3. Use FetchRequest and shorthand methods

    main

    You can make requests using the FetchRequest class or by using shorthand methods for common HTTP verbs like get, post, put, patch, and destroy.

    Using FetchRequest

    Instantiate FetchRequest with the method, URL, and options, then call .perform().

    Using Shorthand Methods

    Import the shorthand functions directly for a more concise syntax.

    import { FetchRequest, get, post, put, patch, destroy } from '@rails/request.js'
    
    // Using FetchRequest
    async function useClass() {
      const request = new FetchRequest('post', 'localhost:3000/my_endpoint', { body: JSON.stringify({ name: 'Request.JS' }) })
      const response = await request.perform()
    }
    
    // Using shorthand
    async function useShorthand() {
      const response = await post('localhost:3000/my_endpoint', { body: JSON.stringify({ name: 'Request.JS' }) })
    }
  4. Use Request Interceptors for authentication

    main

    You can use RequestInterceptor to intercept requests and add headers, such as a Bearer token, before the request is sent. This is useful for asynchronous token retrieval.

    import { RequestInterceptor } from '@rails/request.js'
    
    // Set interceptor
    RequestInterceptor.register(async (request) => {
      const token = await getSessionToken(window.app)
      request.addHeader('Authorization', `Bearer ${token}`)
    })
    
    // Reset interceptor
    RequestInterceptor.reset()
  5. Access Response data and status

    main

    The response object returned by perform() or shorthand methods provides several properties and methods to inspect the result:

    • ok: Boolean indicating if the response was successful.
    • statusCode: The HTTP status code.
    • unauthenticated: Boolean indicating if the response has a 401 status code.
    • authenticationURL: The value from the WWW-Authenticate header.
    • contentType: The response's content-type.
    • headers: The response headers.
    • json: A promise that resolves to the JSON body. Rejects if the content-type is not application/json.
    • html: A promise that resolves to the HTML body. Rejects if the content-type is not html.
  6. Configure Request Options

    main

    When making a request, you can pass an options object as the last argument. Supported options include:

    • body: The request body (Object, FormData, Files, strings, etc.). If contentType is application/json, Request.JS automatically stringifies objects.
    • contentType: Sets the Content-Type header. If omitted, Request.JS handles it based on the body type (e.g., application/json for objects, or the specific file type for File instances).
    • headers: An object containing additional headers. X-CSRF-Token and Content-Type are automatically included.
    • credentials: Specifies the credentials mode (defaults to same-origin).
    • query: An Object, FormData, or URLSearchParams to be appended as query parameters. These are merged with existing URL parameters.
    • responseKind: Specifies the expected response format. Options: html (default), turbo-stream, json, and script.
    • keepalive: Specifies the keepalive flag (defaults to false).
    • priority: Sets the fetch priority. Options: "high", "low", or "auto" (default).
    post("/my_endpoint", {
      body: {},
      contentType: "application/json",
      headers: {},
      query: {},
      responseKind: "html"
    })
  7. Use FetchRequest, FetchResponse, and RequestInterceptor

    main

    The core classes for managing the request lifecycle are exported from the main entrypoint:

    • FetchRequest: The primary class used to construct and configure an HTTP request.
    • FetchResponse: A wrapper around the native Fetch API response, providing enhanced utility methods.
    • RequestInterceptor: A class used to define logic that intercepts and modifies requests (e.g., for adding authentication headers).
    import { FetchRequest, FetchResponse, RequestInterceptor } from '@rails/request.js';
  8. Configure request responseKind

    main

    The responseKind option determines the Accept header sent with the request. This is useful for telling the Rails backend what format you expect in return.

    Supported values:

    • 'html': Sets Accept to text/html, application/xhtml+xml (default).
    • 'turbo-stream': Sets Accept to text/vnd.turbo-stream.html, text/html, application/xhtml+xml.
    • 'json': Sets Accept to application/json, application/vnd.api+json.
    • 'script': Sets Accept to text/javascript, application/javascript.
    • Any other value defaults to */*.
    const request = new FetchRequest('GET', '/api/data', {
      responseKind: 'json'
    })
  9. Register a global RequestInterceptor

    main

    The RequestInterceptor class allows you to register a single global interceptor that can be used to extend or modify requests. You can register an interceptor using RequestInterceptor.register(interceptor), retrieve the current interceptor with RequestInterceptor.get(), or clear it using RequestInterceptor.reset().

    import { RequestInterceptor }
    
    // Register an interceptor
    RequestInterceptor.register(myInterceptor);
    
    // Retrieve the registered interceptor
    const interceptor = RequestInterceptor.get();
    
    // Remove the interceptor
    RequestInterceptor.reset();
  10. Identify response content types

    main

    You can use the following boolean getters to check the Content-Type of a FetchResponse:

    • isTurboStream: Returns true if the content type is text/vnd.turbo-stream.html.
    • isScript: Returns true if the content type contains javascript (e.g., application/javascript or text/ecmascript).
    • contentType: Returns the base MIME type (e.g., application/json) with parameters stripped away.
  11. Inspect response status and authentication with FetchResponse

    main

    The FetchResponse class provides high-level getters to inspect the status of a response. You can check if a request was successful, if it was redirected, or if it failed due to authentication or validation errors.

    Key properties:

    • statusCode: Returns the HTTP status code.
    • ok: Returns true if the status is in the range 200-299.
    • redirected: Returns true if the response was the result of a redirect.
    • unauthenticated: Returns true if the status code is 401.
    • unprocessableEntity: Returns true if the status code is 422.
    • authenticationURL: Returns the value of the WWW-Authenticate header, useful for identifying where to redirect for authentication.
  12. Create and perform requests with FetchRequest

    main

    The FetchRequest class is used to create HTTP requests that automatically include Rails-specific headers (like X-CSRF-Token and X-Requested-With). When perform() is called, it handles interceptors, authentication redirects, Turbo Stream rendering, and script execution automatically.

    Key features:

    • Automatic CSRF Protection: If the request is to the same hostname, the X-CSRF-Token is automatically pulled from the csrf-param cookie or the csrf-token meta tag.
    • Automatic Content-Type: Defaults to application/json unless the body is FormData or a File.
    • Turbo Integration: Automatically renders Turbo Streams if the response is a valid Turbo Stream and the status is ok or unprocessableEntity.
    • Response Handling: Automatically redirects to an authenticationURL if the response is unauthenticated.
    import { FetchRequest } from '@rails/request.js'
    
    // Create a GET request
    const request = new FetchRequest('GET', '/api/data')
    
    // Create a POST request with JSON body
    const postRequest = new FetchRequest('POST', '/api/data', {
      body: { name: 'New Item' },
      responseKind: 'json'
    })
    
    // Perform the request
    const response = await postRequest.perform()