Reputation: 357
I am developping an application with Ruby on Rails for the backend and Vuejs for the frontend. Rails is being consumed as an api. Vite is the frontend server. When I launch my application,
rails s (in one tab of my console)
vite dev (in the other)
I can only see the json provided by my controller but I can't see any frontend being rendered. It looks like the backend and the frontend are not connected properly. I have provided what I think the most importants files are to solve my issue. I don't really know how to solve this problem.
My_Application
|-app
|-controllers
|-application_controller
|-models
.
.
.
|-frontend
|-components
|-entrypoints
|-application.js
|-router.js
.
.
.
|-index.html
|-config
|-routes.rb
|-application.rb
|-vite.json
.
.
.
This is application_controller.rb
class ApplicationController < ActionController::API
before_action :configure_permitted_parameters, if: :devise_controller?
before_action :authenticate_user!
# skip_before_action :verify_authenticity_token if Rails.env.development?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up) do |u|
u.permit(:first_name, :last_name, :name, :email, :password)
end
devise_parameter_sanitizer.permit(:account_update) do |u|
u.permit(:first_name, :last_name, :name, :email, :password, :password_confirmation, :current_password)
end
end
end
This is index.html
<!DOCTYPE html>
<html>
<head>
<title>Rails7VueVite</title>
<meta charset="UTF-8">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Kode+Mono:[email protected]&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Barriecito&family=Kode+Mono:[email protected]&display=swap" rel="stylesheet">
</head>
<body>
<div class="flex flex-col h-screen">
<main class="flex flex-col">
<div id="app" class="font-sans"><div>
</main>
</div>
<script type="module" src="/entrypoints/application.js"></script>
</body>
</html>
This is application.js
// application.js
import { createApp } from 'vue'
import { createPinia } from "pinia"
import App from '../components/pages/App.vue'
import router from './router.js'
import useSessionStore from "../stores/SessionStore.js"
import '@mapbox/mapbox-gl-geocoder/dist/mapbox-gl-geocoder.css'
import './main.scss';
const app = createApp(App)
const pinia = createPinia()
// Load JWT from local storage on refresh
const loadAuthToken = async () => {
const authToken = localStorage.getItem("authToken")
const authTokenExists = authToken !== "undefined" && authToken !== null
if (authTokenExists) {
await useSessionStore(pinia).loginUserWithToken(authToken)
}
}
loadAuthToken().then(() => {
app
.use(router)
.use(pinia)
.mount("#app")
})
app.config.errorHandler = (err, instance, info) => {
console.log('wrong !!! err =>', err)
console.log('wrong !!! instance =>', instance)
console.log('wrong !!! info =>', info)
}
if (import.meta.env.DEV) {
// Enable Vue Devtools in development environment
app.config.devtools = true;
}
This is router.js
// app/javascript/packs/router.js
import { createRouter, createWebHistory } from 'vue-router'
import useSessionStore from "../stores/SessionStore"
import Home from '../components/pages/Home.vue'
import About from '../components/pages/About.vue'
import Track from '../components/pages/Track.vue'
import NewProject from '../components/pages/NewProject.vue'
import MyTracks from '../components/pages/MyTracks.vue'
import NewResultTrack from '../components/pages/NewResultTrack.vue'
import TrackConversation from '../components/conversations/TrackConversation.vue'
import Messaging from '../components/pages/Messaging.vue'
import UserForm from '../components/UserForm.vue'
const authGuard = (to, next) => {
const isLoggedIn = useSessionStore().isLoggedIn
const requiresAuth = to.meta.requiresAuth
if (isLoggedIn && !requiresAuth) return next({ name: "Home" })
if (!isLoggedIn && requiresAuth) return next({ name: "Login" })
return next()
}
const routes = [
{ path: '/', name: 'Home', component: Home },
{ path: '/aboutkkk', component: About },
{ path: '/track/:zeTrackId', name: 'track', component: Track },
{ path: '/new_project', component: NewProject },
{ path: '/my_own_tracks', component: MyTracks },
{ path: '/upload_track/:zeTrackId', name: 'result_track', component: NewResultTrack },
{ path: '/conversation/:conversationId', name: 'conversation', component: TrackConversation },
{ path: '/my_messages', component: Messaging },
{ path: '/login-signin', component: UserForm },
{ path: "/users/sign_in", name: "Login", component: UserForm, meta: { requiresAuth: false }},
{ path: "/users/sign_up",name: "Signup", component: UserForm, meta: { requiresAuth: false }},
{ path: "/users/logout",name: "Logout",meta: { requiresAuth: true },
beforeEnter: async (_to, _from, next) => {
await useSessionStore().logout()
next({ name: "Login" })
}
}
];
const router = createRouter({
history: createWebHistory(),
routes
})
router.beforeEach((to, _from, next) => {
authGuard(to, next)
})
export default router
This is routes.rb
Rails.application.routes.draw do
devise_for :users,
controllers: {
sessions: "users/sessions",
registrations: "users/registrations"
}
namespace :api do
namespace :v1 do
resources :chatrooms, only: %i[index] do
resources :messages, only: %i[index create]
end
end
end
resources :tracks
resources :instruments, only: [:index, :new, :create]
resources :genres, only: [:index, :new, :create]
root 'tracks#index'
get '/index_results/:id', to: 'tracks#index_results', as: 'index_results'
get '/my_tracks', to: 'tracks#myTracks', as: 'myTracks'
get "/member-data", to: "members#show"
get "*path", to: "static#index", constraints: proc { |request| !request.xhr? && request.format.html? }
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end
This is application.rb
require_relative "boot"
require "rails/all"
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
module Collaborasound
class Application < Rails::Application
config.application_name = Rails.application.class.module_parent_name
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 6.1
config.session_store :cookie_store, key: "_interslice_session"
# Required for all session management (regarless of session_store)
config.middleware.use ActionDispatch::Cookies
config.middleware.use config.session_store, config.session_options
# Configuration for the application, engines, and railties goes here.
#
# These settings can be overridden in specific environments using the files
# in config/environments, which are processed later.
#
# config.time_zone = "Central Time (US & Canada)"
# config.eager_load_paths << Rails.root.join("extras")
# Only loads a smaller set of middleware suitable for API only apps.
# Middleware like session, flash, cookies can be added back manually.
# Skip views, helpers and assets when generating a new resource.
config.api_only = true
end
end
This is vite.json
{
"all": {
"sourceCodeDir": "app/frontend",
"watchAdditionalPaths": []
},
"development": {
"autoBuild": true,
"publicOutputDir": "vite-dev",
"port": 3036
},
"test": {
"autoBuild": true,
"publicOutputDir": "vite-test",
"port": 3037
}
}
This is vite.config.ts
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import * as path from "path";
import FullReload from "vite-plugin-full-reload";
import RubyPlugin from "vite-plugin-ruby"
// import path from "path";
export default defineConfig({
plugins: [
vue(),
RubyPlugin(),
FullReload(["config/routes.rb", "app/views/**/*"], { delay: 250 })
],
resolve: {
alias: [
{
find: "@/lib",
replacement: path.resolve(__dirname, "./app/frontend/components/lib/")
},
{
find: "@/components",
replacement: path.resolve(__dirname, "./app/frontend/components/")
},
{
find: '@/entrypoints',
replacement: path.resolve(__dirname, "./app/frontend/entrypoints")
}
]
}
})
Upvotes: 0
Views: 80
Reputation: 307
Of the code you posted, I don't see anything that's actually communicating with your Rails API.
Keep in mind that your Vue router.js
and your Rails routes.rb
do not automatically talk to each other. Vue is a single-page application framework, and Vue routes are what steer your user to your different Vue components. It will not talk to the Rails API for you.
I see that you're using Pinia for your store. This is where you'd define your API calls. I recommend you review the Pinia docs, which includes some examples on how to define actions that communicate with an API. A basic example might look like:
export const useTracks = defineStore('tracks', {
state: () => ({
tracks: null
}),
actions: {
async fetchTodos() {
try {
// localhost:3000 is the default Rails port
const response = await fetch('https://localhost:3000/tracks');
if (!response.ok) {
throw new Error('Network error, failed to fetch tracks');
}
const data = await response.json();
this.tracks = data;
} catch (error) {
console.error('Error fetching tracks:', error);
}
},
},
})
Upvotes: 0