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)
})
})
}
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.