🤠 An opinionated AJAX client for Ruby on Rails APIs

Overview

Rails Ranger

Exploring the routes and paths of Ruby on Rails APIs

Github Repository | Documentation

npm version Travis build status Test Coverage Dependency Status devDependency Status

Rails Ranger is a thin layer on top of Axios, which gives you an opinionated interface to query APIs built with Ruby on Rails.

Main features

  • URL building following Ruby on Rails routes conventions
  • Automatic transformation of camelCase into snake_case and back to camelCase when exchanging data between the front-end and the API

Installation

npm install --save rails-ranger

or

yarn add rails-ranger

Getting started

If you prefer a blog post, checkout our getting started guide here.

The following example illustrates a simple usage of the library:

// api-client.js
import RailsRanger from 'rails-ranger'

const config = {
  axios: {
    baseURL: 'http://api.myapp.com',
    headers: {
      'Content-Type': 'application/json',
      'Accept': 'application/json'
    }
  }
}

export default new RailsRanger(config)
// some-front-end-component.js
import api from 'api-client'

api.list('users').then((response) => {
  const users = response.data
})

The list function makes a request to the index path of the users resource, following Rails routing conventions. This means a GET request to the /users path.

Also we converted the snake_cased JSON generated by Ruby on Rails automatically to camelCase, as preferred in Javascript.

Observation: you can use api.index('users') as well. The list function is just an alias for it.


Important notice for non api-only Ruby on Rails servers

You must setup the headers correctly, passing down the content type and accept keys as application/json (as shown in the example above) for Rails to serve the endpoint in the json format instead of presuming the HTTP default.

A slightly more advanced example:

api.resource(users, 1).list('blogPosts', { someParameter: false })
// => GET request to /users/1/blog_posts?some_parameter=false

Build your own client object

You can build your own client object to centralize the API routes used by your front-end app.

This is indeed recommended for non-trivial applications, to avoid duplication, allow manipulating the parameters before performing the request and make your life easier in the event of removal/replacement of this dependency from your project.

Below is an example of such implementation:

// api-client.js
import RailsRanger from 'rails-ranger'

const client = new RailsRanger

export default {
  users: {
    list(params) {
      return client.list('users', params)
    }
  }

  blogPosts: {
    list(params) {
      return client.list('blogPosts', params)
    }
  }
}
// some-front-end-component.js
import api from 'api-client'

api.users.list({ limit: 3 }).then((response) => {
  const users = response.data
})

Options

As the first argument when creating a new instance of Rails Ranger you can pass an object of options to customize the behavior of the client.

dataTransform

default: true

By default RailsRanger will convert camelCased keys in your jsons to snake_case when sending a request to Rails, and will convert the Rails response back from snake_case to camelCase for better usage within your javascript code.

You can disable this behavior by setting dataTransform to false:

const api = new RailsRanger({ dataTransform: false })

axios

default: {}

Any object passed to the axios option will be handled to Axios. Here an example using the baseUrl configuration of Axios:

const api = new RailsRanger({ axios: { baseUrl: 'http://myapp.com/api/v1' } })

api.list('users')
// => GET request to http://myapp.com/api/users

See more configuration options in the Axios documentation


Use Rails Ranger just for path building

You don't need to use Rails Ranger as an ajax client if you don't want to. It can also be used just to generate the resource routes and then make the request with another tool. The following is an example of this usage:

import { RouteBuilder } from RailsRanger
const routes = new RouteBuilder

routes.create('users', { name: 'John' })
// => { path: '/users', params: { name: 'John' }, method: 'post' }

routes.show('users', { id: 1, hidePassword: true })
// => { path: '/users/1?hide_password=true', params: {}, method: 'get' }

routes.get('/:api/documentation', { api: 'v1', page: 3 })
// => { path: 'v1/documentation?page=3', params: {}, method: 'get' }

Nested resources

You can access your nested resources by using the .resource function:

api.resource('users').list('blogPosts')
//=> GET request to /users/blog_posts


api.resource('users', 1).list('blogPosts')
//=> GET request to /users/1/blog_posts

Namespaced routes

The .namespace function can help you to build a path nested within a Rails namespace:

api.namespace('users').list('blogPosts')
//=> GET request to /users/blog_posts


api.namespace('admin_roles/:type', { type: 1 }).list('blogPosts')
//=> GET request to /admin_roles/1/blog_posts

Available actions

List/Index

api.list('users', { limit: 3 })
// => GET request to /users?limit=3

api.index('users', { limit: 3 })
// => GET request to /users?limit=3

Show

api.show('users', { id: 1 })
// => GET request to /users/1

New

api.new('users')
// => GET request to /users/new

Create

api.create('users', { email: '[email protected]' })
// => POST request to /users

Edit

api.edit('users', { id: 1 })
// => GET request to /users/1/edit

Update

api.update('users', { id: 1, name: 'John Doe' })
// => PATCH request to /users/1

Destroy

api.destroy('users', { id: 1 })
// => DELETE request to /users/1

Available HTTP methods

GET

api.get('users/:id', { id: 1, hidePassword: true })
// => GET request to users/1&hide_password=true

POST

api.post('users/:id', { id: 1, name: 'John' })
// => POST request to users/1 with a JSON payload containing: { "name": "John" }

PATCH

api.patch('users/:id', { id: 1, name: 'John' })
// => PATCH request to users/1 with a JSON payload containing: { "name": "John" }

PUT

api.put('users/:id', { id: 1, name: 'John' })
// => PUT request to users/1 with a JSON payload containing: { "name": "John" }

DELETE

api.delete('users/:id', { id: 1, hidePassword: true })
// => DELETE request to users/1&hide_password=true

Request Cancellation

Since rails-ranger is built on top of Axios, request cancellation works the same way.

import api from 'api-client'

import axios from 'axios';
const CancelToken = axios.CancelToken;
const source = CancelToken.source();

const request = api.get('/users/:id', {id: 1}, {cancelToken: source.token})
request.cancel = (optionalMessage) => source.cancel(optionalMessage);
Comments
  • Get method doesn't use existing resource

    Get method doesn't use existing resource

    I have the route current_sbm_employees_path | GET | /merchandising/sbm_employees/current(.:format) I tried to access it with api.resource('sbm_employees').get('current') but that gives me /merchandising/current It works correctly if I use api.resource('sbm_employees').list('current')

    Based on the documentation it appears that accessing the url with the get method should work too.

    bug 
    opened by ryanmk54 7
  • Parameter substitution only seems to work for one parameter

    Parameter substitution only seems to work for one parameter

    I'm having trouble getting the parameter substitution to work with more than one parameter:

    api.get('/first/:a/second/:b/third/:c', { a: 'A', b: 'B', c: 'C', d: 'D' }).then((r) => {
      console.log(r)
    }).catch((e) => {
      console.log(e)
    })
    

    should perform a GET to /first/A/second/B/third/C?d=D, but instead tries to GET /first/:a/second/:b/third/C?d=D

    I've managed to work around this by using api.get(`first/${a}/second/${b}/third/:c`) for now, but it doesn't look very clean (just like attempting to put backticks in a code block in GitHub 🙄 )

    I can't see anything in _paramsToPath that could be causing this — do you know what could be going wrong here?

    (I did try replicating this with the RouteBuilder, but the import in the README seems to be wrong:

    import { RouteBuilder } from RailsRanger
    const routes = new RouteBuilder
    

    produces Uncaught Error: Module build failed: SyntaxError: Unexpected token. Replacing the import with

    import { RouteBuilder } from 'rails-ranger'
    

    produces TypeError: __WEBPACK_IMPORTED_MODULE_3_rails_ranger__.RouteBuilder is not a constructor. What is the correct way to import the RouteBuilder?)

    bug 
    opened by unikitty37 5
  • API calls fail for Rails apps that respond to HTML and JSON

    API calls fail for Rails apps that respond to HTML and JSON

    Given the following controller:

    class ThingController < ApplicationController
      def index
        @things = Things.all
        respond_to do |format|
          format.html
          format.json { render json: @things }
        end
      end
    

    and the following JavaScript:

    api.list('things').then((response) => {
      console.log(response.data)
    })
    

    then response.data contains the HTML of /things rather JSON. If I visit /things.json in the browser I get the expected JSON.

    I can't see how to configure rails-ranger or axios to append .json to the URL. Is this possible? Sadly, calling api.list('things.json') produces a request to things_json, which doesn't help much.

    (I realise that rails-ranger's really intended for use with API-only Rails backends, but it would be extremely useful to be able to have it work for this case, even if that's not the default behaviour, as we don't always have control over the other end…)

    Thanks!

    opened by unikitty37 4
  • Upgrade axios dependency to 0.21.1

    Upgrade axios dependency to 0.21.1

    opened by ryanmk54 3
  • Recommended way to send FormData

    Recommended way to send FormData

    What is the recommended way to send a file using rails-ranger. I tried copying the form-data example from the axios repo, but it doesn't seem to work. The form has one file in it, but Chrome just shows it sending an empty json object to the server.

    opened by ryanmk54 3
  • Bump extend from 3.0.1 to 3.0.2

    Bump extend from 3.0.1 to 3.0.2

    Bumps extend from 3.0.1 to 3.0.2.

    Changelog

    Sourced from extend's changelog.

    3.0.2 / 2018-07-19

    • [Fix] Prevent merging __proto__ property (#48)
    • [Dev Deps] update eslint, @ljharb/eslint-config, tape
    • [Tests] up to node v10.7, v9.11, v8.11, v7.10, v6.14, v4.9; use nvm install-latest-npm
    Commits
    • 8d106d2 v3.0.2
    • e97091f [Dev Deps] update tape
    • e841aac [Tests] up to node v10.7
    • 0e68e71 [Fix] Prevent merging proto property
    • a689700 Only apps should have lockfiles
    • f13c1c4 [Dev Deps] update eslint, @ljharb/eslint-config, tape
    • f3570fe [Tests] up to node v10.0, v9.11, v8.11, v7.10, v6.14, v4.9; use...
    • See full diff in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot ignore this [patch|minor|major] version will close this PR and stop Dependabot creating any more for this minor/major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 2
  • Bump fstream from 1.0.11 to 1.0.12

    Bump fstream from 1.0.11 to 1.0.12

    Bumps fstream from 1.0.11 to 1.0.12.

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot ignore this [patch|minor|major] version will close this PR and stop Dependabot creating any more for this minor/major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 2
  • Bump axios from 0.18.0 to 0.18.1

    Bump axios from 0.18.0 to 0.18.1

    Bumps axios from 0.18.0 to 0.18.1.

    Release notes

    Sourced from axios's releases.

    v0.18.1

    Security Fix:

    • Destroy stream on exceeding maxContentLength (fixes #1098) (#1485) - Gadzhi Gadzhiev
    Changelog

    Sourced from axios's changelog.

    0.18.1 (May 31, 2019)

    Security Fix:

    • Destroy stream on exceeding maxContentLength (fixes #1098) (#1485) - Gadzhi Gadzhiev
    Commits
    • face016 Releasing 0.18.1
    • 0628763 Update Changelog for release (0.18.1)
    • dc9b29c adjust README to match IE support
    • 16326d5 Remove usages of isOldIE in tests
    • 5a4228b Remove IE10 launcher from karma config
    • 695b5f7 Remove isOldIE check in tests
    • e314ab0 Remove HTTP 1223 handling
    • 7efa822 Remove btoa polyfill tests
    • f3cdcc7 Delete btoa polyfill
    • efc0b58 Remove ie8/9 special CORS treatment and btoa polyfill
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot ignore this [patch|minor|major] version will close this PR and stop Dependabot creating any more for this minor/major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 2
  • Add support for custom actions

    Add support for custom actions

    Description

    This PR adds support for custom actions (like blog_posts/1/publish, and etc). The interface I thought for now was something like:

    const publish = { action: 'publish', on: 'member', method: 'get' }
    api.do(publish, 'blog_posts', { id: 1 })
    

    Closes #3

    enhancement 
    opened by victor-am 2
  • Add documentation for request cancellation (#1)

    Add documentation for request cancellation (#1)

    This PR adds documentation for request cancellation. It is added to the bottom of the README because the example uses the HTTP methods, and that is where the documentation for the HTTP methods are.

    opened by ryanmk54 1
  • this.client is undefined in readme example

    this.client is undefined in readme example

    I am trying to copy the example in the readme, with the following code, but I get the error: this.client is undefined. If I change this.client to client it works fine.

    import RailsRanger from 'rails-ranger';
    const client = new RailsRanger;
    export default {
      client,
    
      orders: {
        update(params) {
          return this.client.update('orders', params) // <-- error on this line
            .then(response => response.data)
        } 
      }
    };
    
    opened by ryanmk54 1
  • Deprecate the `transformData` option in favor of a better name

    Deprecate the `transformData` option in favor of a better name

    Documentation of the transformData parameter

    Let's face it, transformData might just be the worst name possible since transform is a very vague verb and data is almost everything in software.

    I'm considering convertKeys but suggestions are welcome 😄

    Of course the old transformData should be kept working (with a warning) until the next major version release.

    enhancement 
    opened by victor-am 0
  • Interchangeable API adapters

    Interchangeable API adapters

    Allow a set of official adapters (like ActiveModelSerializer's JSON API, Attributes, etc) and the possibility of passing a custom adapter to the Rails Ranger client.

    I thought something like that:

    import RailsRanger from 'rails-ranger'
    
    //
    // Official adapter
    //
    const api = new RailsRanger({
      adapter: 'officialAdapterName'
    })
    
    //
    // Custom adapter
    //
    const myAdapter = {
      // Some object describing the adapter
    }
    
    const api = new RailsRanger({
      adapter: myAdapter
    })
    

    But as always I'm open to suggestions 😄

    feature request 
    opened by victor-am 0
  • Support for singular resources

    Support for singular resources

    Currently we don't support singular resources through the api.action('resource') API, but it's a desirable feature.

    I've played around with some APIs for this but I didn't feel comfortable with any of them.

    Suggestions are welcome 😄

    feature request 
    opened by victor-am 0
Releases(1.2.1)
Owner
Victor Marques
Full stack web developer (Ruby/Elixir/Vue.js)
Victor Marques
Promise based HTTP client for the browser and node.js

axios Promise based HTTP client for the browser and node.js New axios docs website: click here Table of Contents Features Browser Support Installing E

axios 98k Jan 3, 2023
RESTful HTTP client for JavaScript powered web applications

Amygdala is a RESTful HTTP library for JavaScript powered web applications. Simply configure it once with your API schema, and easily do GET, POST, PU

Lincoln Loop 392 Dec 6, 2022
Grupprojekt för kurserna 'Javascript med Ramverk' och 'Agil Utveckling'

JavaScript-med-Ramverk-Laboration-3 Grupprojektet för kurserna Javascript med Ramverk och Agil Utveckling. Utvecklingsguide För information om hur utv

Svante Jonsson IT-Högskolan 3 May 18, 2022
Hemsida för personer i Sverige som kan och vill erbjuda boende till människor på flykt

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

null 4 May 3, 2022
Kurs-repo för kursen Webbserver och Databaser

Webbserver och databaser This repository is meant for CME students to access exercises and codealongs that happen throughout the course. I hope you wi

null 14 Jan 3, 2023
Use ESM with importmap to manage modern JavaScript in Rails without transpiling or bundling.

Importmap for Rails Use ESM with importmap to manage modern JavaScript in Rails without transpiling or bundling. Installation ... Usage ... License Im

Ruby on Rails 795 Jan 7, 2023
Rails plugin for Esbuild

?? esbuild-rails Rails plugin for Esbuild to help loading Stimulus controllers, ActionCable channels, and other Javascript. ⚙️ Installation Install wi

Chris Oliver 118 Dec 14, 2022
Base Rails app that includes login, social login, homepage, and basic model for serving as a scaffold app.

Rails7Base I created the Rails7Base as a scaffold application. Countless times, I had to create apps that must have the following features: Login syst

Chim Kan 14 Jul 2, 2022
Steam Game - Nimby Rails

Nimby-Rails-Mod.txt-File-Tools Steam Game - Nimby Rails zh: 这是Steam中的一个铁道游戏《Nimby Rails》的Mod配置文件编辑器。 这个配置文件编辑器用的是纯JS语法 用了HTML+CSS+JS+JQuery+一点点的Vue 本质

Mikeson Zhang 3 Jun 14, 2022
Create beautiful JavaScript charts with one line of Ruby

Chartkick Create beautiful JavaScript charts with one line of Ruby. No more fighting with charting libraries! See it in action Chartkick 4.0 was recen

Andrew Kane 6.1k Jan 8, 2023
A concise collection of classes for PHP, Python, JavaScript and Ruby to calculate great circle distance, bearing, and destination from geographic coordinates

GreatCircle A set of three functions, useful in geographical calculations of different sorts. Available for PHP, Python, Javascript and Ruby. Live dem

null 72 Sep 30, 2022
Quick One Liners in JavaScript, TypeScript, Python, Rust, Java, Ruby, C, C++

ONE LINERS This repository contains cool and simple one line utility functions to easily use common repetitive methods in JavaScript, TypeScript, Pyth

Divin Irakiza 3 Mar 2, 2022
Ruby's rstfilter extension

vscode-rstfilter README You can see the execution results by saving the file. Requirements rstfilter-lsp command in rstfilter gem is needed. $ gem ins

Koichi Sasada 17 Dec 27, 2022
Lightweight VSCode extension for Ruby.

vscode-ruby-light Lightweight VSCode extension for Ruby. Install Install via Visual Studio Marketplace: Ruby Light - Visual Studio Marketplace For Dia

Ryo Nakamura 23 Jan 2, 2023
Ajax for Node.js and browsers (JS HTTP client)

superagent Small progressive client-side HTTP request library, and Node.js module with the same API, supporting many high-level HTTP client features T

Sloth 16.2k Jan 1, 2023
Social-Feeds-APIs - REST APIs to build social media sites.

express4.17.1-in-docker EXPRESS 4.17 SPA IMPORTANT NOTES: 1. Make sure you follow the steps mentioned under "PROJECT START STEPS" and ensure that the

Patel Rohan 1 Jan 3, 2022
Examples of how to do query, style, dom, ajax, event etc like jQuery with plain javascript.

You (Might) Don't Need jQuery Frontend environments evolve rapidly nowadays and modern browsers have already implemented a great deal of DOM/BOM APIs

NEFE 20.3k Dec 24, 2022
Extends the default Bootstrap Modal class. Responsive, stackable, ajax and more.

Note: Since this plugin was created to solve a lot of the issues with BS2, it still uses the BS2 markup syntax. Currently I believe the default BS3 mo

Jordan Schroter 5k Dec 28, 2022
A JQuery plugin to create AJAX based CRUD tables.

What is jTable http://www.jtable.org jTable is a jQuery plugin used to create AJAX based CRUD tables without coding HTML or Javascript. It has several

Volosoft 1.1k Dec 21, 2022