double namespaced routing issues with fetch

84 views Asked by At

Hello> Im creating a Javascript SPA/Rails Api that has books and users. I using namespace in my routes,along with serializers for each model. Im not sure why im getting this error when trying to do a post fetch request for a user. Im not sure if i will have the same problem with my books. Belo you can see where in my controller at the top have Api::V1....etc for each controller. I then in my routes have my namespace routes. My rails console is running as well

Routing Error uninitialized constant Api

my controller

class Api::V1::UsersController < ApplicationController

    def index
        users=User.all
        render json: users
    end 

    def create
        if User.find_by(:name=> user_params[:name])
            user=User.find_by(:name=>user_params[:name])
            redirect_to "/api/v1/users/#{user.id}"
        else 
            user = User.create(user_params)
            user.save!
            render json: user
        end 
    end 

    def show
        user = User.find_by(:id => params[:id])
        render json: user
    end 

    private

    def user_params
        params.require(:user).permit(:name)
    end
end
class Api::V1::BooksController < ApplicationController

    def index
        books = Book.all
        render json: books
    end 

    def create
        book = Book.create(book_params)
        book.save!
        render json: book
    end 

    def destroy
        book=Book.find_by(:id => params[:id]).destroy
        render json: book
    end 

    private

    def book_params
        params.require(:book).permit(:title,:author,:review,:rating,:user_id)
    end 
end

ROUTES

Rails.application.routes.draw do






  namespace :api do
    namespace :v1 do
      resources :books
    end
  end
end
//POST fetch for creating a User
    static createUser(user) {
        let newUserForm = document.getElementById('new-user-form')
        newUserForm.addEventListener('submit', function(e){
            e.preventDefault();
            fetch('http://localhost:3000/api/v1/users', {
                method: 'POST',
                headers: {
                    'Content-Type' : 'application/json',
                    'Accept' : 'application/json'
                },
                body: JSON.stringify({
                    user: {
                        name: e.target.children[1].value
                    }
                })
            })
                .then(res => {
                    if (!res.ok) {
                        throw new Error(); // Will take you to the `catch` below
                    }
                    return res.json();
                })
                .then (user => {
                    let newUser = new User(user)
                    console.log(user)
                    newUser.displayUser();
                })
                .catch(error => {
                    console.error('User class Error', error)
                })
            })
    }
1

There are 1 answers

0
max On BEST ANSWER

Define (and reopen) namespaced classes and modules using explicit nesting. Using the scope resolution operator can lead to surprising constant lookups due to Ruby’s lexical scoping, which depends on the module nesting at the point of definition.

module Api
  module V1
    class UsersController < ApplicationController
      # ...
    end
  end 
end

While you could naively belive that class Api::V1::UsersController does the same thing it does not as it requires the Api module to be defined at the time the class is loaded and does not properly set the module nesting which will lead to suprising constant lookups. For example when you use User Ruby will not find Api::User or Api::V1::User as could be expected.

There are however many more issues with this code as its riddled with poor api design descisions - potential nil errors (through the use of find_by instead of find). It should really look like this:

# no need to repeat yourself
namespace :api do
  namespace :v1 do
    resources :users
    resources :books
  end 
end
# config/initializers/inflections.rb
ActiveSupport::Inflector.inflections(:en) do |inflect|
  inflect.acronym 'API'
end
# Acronyms should be all-caps
# https://github.com/rubocop-hq/ruby-style-guide#camelcase-classes
module API
  module V1
    class UsersController < ApplicationController
      
      # GET /api/v1/users
      def index
        users = User.all
        render json: users
      end 

   
      # POST /api/v1/users
      def create
        user = User.new(user_params)
        if user.save
          render json: user, 
                 status: :created
        else
          render json: { errors: user.errors.full_messages }, 
                 status: :unprocessable_entity
        end
      end 
      
      # GET /api/v1/users/1
      def show
        # Use .find instead of find_by as it will return a
        # 404 - Not Found if the user is not found
        user = User.find(params[:id])
        render json: user
      end 
      
      private
      
      def user_params
        params.require(:user).permit(:name)
      end
    end
  end 
end
module API
  module V1
    class BooksController < ApplicationController
      # GET /api/v1/books
      def index
        books = Book.all
        render json: books
      end

      # POST /api/v1/books
      def create
        book = Book.new(book_params)
        if book.save
          render json: book, 
                 status: :created
        else 
          render json: { errors: book.errors.full_messages }, 
                 status: :created
        end
      end

      # DELETE /api/v1/books/1
      def destroy
        book = Book.find(params[:id])
        book.destroy
        head :ok 
      end

      private

      def book_params
        params.require(:book)
              .permit(:title, :author, :review, :rating, :user_id)
      end
    end
  end
end

When creating a resource you should return a 201 CREATED response if it was successful and either include the resource in the response body or provide a location header which tells the client where they can find the resource.

If its not successful you should return a status code such as 422 Unprocessable Entity which tells the client that the server was not able to process the request with the given parameters.