Vue + Express +GCloud routing breaks in App Service - express

I have a Vue3 + Express + Okta app. It works perfectly fine on local, but once I deploy it to Google Cloud's App Service I can only load the main page. Any navigation gives me 404.
I assume I am doing something dumb, but I've tried all I can think about to no avail. Help please?
Here's my router (I removed extra routes):
import { createRouter, createWebHistory } from "vue-router";
import AppHeader from "./layout/AppHeader";
import AppFooter from "./layout/AppFooter";
import Home from "./views/Home.vue";
import ListEpisodes from "./views/ListEpisodes.vue";
import { LoginCallback, navigationGuard } from '#okta/okta-vue';
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: "/",
name: "home",
components: {
default: Home,
footer: AppFooter,
header: AppHeader
}
},
{
path: "/episodes",
name: "episodes",
components: {
header: AppHeader,
default: ListEpisodes,
footer: AppFooter
}
},
{
path: '/login/callback',
component: LoginCallback
}
]
})
router.beforeEach(navigationGuard);
export default router;
To deploy to GCP, I run npm run build for the frontend, then copy dist/ to my express app's static folder, then "gcloud app deploy".
The routing worked fine in GCP before I moved from vue2 to vue3 to integrate with okta. The extra stuff was history: createWebHistory() and router.beforeEach(navigationGuard);
Any ideas what can that be?
createWebHistory() was originally createWebHistory(process.env.BASE_URL). I thought maybe there's an issue there, so I tried creating .env file with BASE_URL=https://my_domain.
Then I removed the parameter - no difference on local, still didn't work on GCP.
Tried removing nocache from the express app as well, as that was another thing I changed before redeploying from working (vue2 many changes ago) version.
UPD: here's my app.yaml
runtime: nodejs16
service: default
instance_class: F2

Ok, I still don't know what is happening, but I found another way to do this: deploying api and frontend separately and then setting up the dispatch rules to connect Vue app to the backend.
I have used this guide with a few changes: https://medium.com/#rgoyard/how-to-deploy-a-single-page-application-and-its-backend-to-google-app-engine-353ff93bd38c
in vue.config.js for the Vue app remove/comment out the proxy redirecting to your api:
devServer: {
// proxy: {
// '^/api': {
// target: 'http://localhost:3000',
// changeOrigin: true
// },
// }
}
create frontend.yaml in the frontend dir, don't set any handlers:
runtime: nodejs18
service: default
Deploy frontend: gcloud app deploy frontend.yaml
Create api.yaml in the api dir:
runtime: nodejs18
service: api
Deploy api: gcloud app deploy api.yaml
Create dispatch.yaml in the root dir (or anywhere, really):
dispatch:
- url: '*/api/*'
service: api
Deploy dispatch rules: gcloud app deploy dispatch.yaml

Related

Use express.js as middleware for nuxtjs?

I've been following this tutorial that explains how to have an express app in your nuxtjs app, but when I make a postman request to the route defined in the express app, it returns 404 error. Is this tutorial outdated?
Here's my nuxt.config.js:
import { resolve } from 'path'
export default {
ssr: true,
target: 'server',
modules: [
'#nuxtjs/axios'
],
serverMiddleware: [
{ path: '/api', handler: '~/api/index.js' },
],
}
I made an api folder with an api folder with a index.js file in it where you can find a normal express app, I configured my nuxt.config.js file but it seems like the express.js is not running when I run the nuxtjs app. What should I do?

Issue with Vue application deployed to subdirectory when route changes from publicPath

We have a Vue application that we're deploying to a subdirectory: /deploypath/
Right now, we have vue.config.js as:
const { defineConfig } = require('#vue/cli-service')
module.exports = defineConfig({
publicPath: process.env.NODE_ENV === 'production'
? '/deploypath/'
: '/',
transpileDependencies: [
'vuetify'
]
})
Here's what's happening: In index.js (router) I have multiple paths configured to return multiple views and components. When a user is logged in, they can access additional pages. When they're not logged in, they're redirected to a (landing page).
I have multiple routes defined:
const routes = [
{
path: '/deploypath',
name: 'feature1',
component: FeatureOneView,
meta: {
title: 'Feature One',
}
},
{
path: '/deploypath/notloggedin',
name: 'notloggedin',
component: NotLoggedInView,
meta: {
title: 'Landing',
}
}
]
const router = new VueRouter({
mode: 'history',
routes: routes
});
Now, the issue I'm running into is that (after deploying a production build) when I visit /deploypath it works, however any other path (e. g. /deploypath/notloggedin) doesn't work. We have an Ubuntu instance running with nginx.
Are we doing something wrong with the Vue config or is there an issue on the nginx side, or other?
In case it helps anyone, a good buddy of mine helped find a solution:
cd /etc/nginx/sites-available
then:
sudo vim <insert your site's conf file here>
then press "i" to edit and within the top-level server { ... } section paste in (replace "dirname" with the name of the subdirectory you're hosting your Vue application in):
location ^~ /dirname {
try_files $uri /dirname/index.html =400;
}
then press escape (esc) on keyboard, then type ":wq" and press enter to save..
Then run:
sudo service nginx restart
then refresh your browser window and hopefully you see your Vue app!

How to set up Express proxy DevServer on a Vue3 Vite App

I was following a tutorial on a Full-Stack MEVN app with different folders for the client and server, and when it came time to join both, I notice my from-end is trying to make requests to the server it is running on instead of the express server.
This is my vite.config.js file as followed by the tutorial
import { defineConfig } from 'vite'
import vue from '#vitejs/plugin-vue'
const path = require('path')
// https://vitejs.dev/config/
export default defineConfig({
alias: {
'#': path.resolve(__dirname, './src'),
},
plugins: [vue()],
devServer: {
proxy: "http://localhost:5001"
}
})
But when I try to use the GET method (that works fine on its own) I get a 404 error on my DevTools because I guess it is trying to perform tasks on the same server instead of my already developed express server.

VueJS app as subdomain throws 404 when a path is added

I have a portfolio site portfolio.com and a subdomain which points to a VueJS frontend hosted on Netlify vuejsapp.portfolio.com
Users upload files to the app and it generates a download link URL, say vuejsapp.portfolio.com/download/048677a. When I navigate to the link within the VueJS app (by clicking a button to redirect after it's uploaded) it redirects to the Download component without issue. But if I copy and paste that link directly in my browser it throws a 404 error. Why is this?
I know it has to do with a Vue Router configuration but I can't seem to find much information about it or perhaps I'm looking in the wrong place. Could someone tell me what I'm missing or point me to some relevant documentation please?
My router.js file:
Vue.use(Router);
export default new Router({
mode: "history",
base: process.env.BASE_URL,
routes: [
{
path: "/",
name: "home",
component: Home,
},
{
path: "/about",
name: "about",
component: About
},
{
path: "/download/:id",
name: "download",
component: Download,
props: route => ({ id: route.params.id }),
}
]
});
Since the code/setup is running properly on your local environment and only breaking on Netlify its pretty clear that you're running into a wrong server configuration issue.
Your Netlify environment has to know that it should always route any requests to / and leave the routing to your Vue App. You can read more about how to resolve that in the Netlify docs.

Webpack Cannot Resolve node_modules lazy assets

I'm working on a vuejs project and we're trying to use external vue cli applications as libraries. In these libraries we want to be able to export a router config, which lazy loads the components within one of these modules. However when we compile this using the vue-cli-service into a library and it's got lazy chunk assets we cannot resolve them with webpack.
I have a feeling its something to do with the public path, or some simple configuration but i'm just stuck and banging my head against a wall at this stage with it.
https://github.com/EvanBurbidge/mono-repo-tester
Here's a simple overview of what we're doing
App1 -> main app, installs App2, imports { router } from 'app2'
App2 -> library, compiles to common js lib exports router config
The console output from app1
The router configuration from app2
The router importing app2 from app1
/* config.module.rule('js') */
{
test: /\.jsx?$/,
exclude: [
function () { /* omitted long function */ }
],
use: [
/* config.module.rule('js').use('cache-loader') */
{
loader: 'cache-loader',
options: {
cacheDirectory: '/Users/evan/test/node_modules/.cache/babel-loader',
cacheIdentifier: '39e7e586'
}
},
/* config.module.rule('js').use('babel-loader') */
{
loader: 'babel-loader'
}
]
},
I think I know what the problem is.
It appears that you're suffering from the fact that the default config of webpack bundled with VueJS does not support what you are trying to accomplish. In fact, it may very well be that Webpack does not support what you are trying to accomplish #2471 #6818 #7843.
When you compile app2 into UMD and try to use it within app1 by importing the UMD, the dynamic imports of app2 are not being resolved and are thus not copied over to the publicPath of app1. Since it is a dynamic import, compilation succeeds to the point where you can deploy the app. When you try to load the app however, it starts complaining that chunks are missing.
Here's one way to solve this problem:
app2/package.json
{
"name": "app2",
...
"main": "src/router.js"
}
app2/src/router.js
const Hey = () => import(/*webpackChunkName: 'app2.Hello.vue' */ './components/HelloWorld.vue')
export default [
{
path: '/app2',
component: Hey,
name: 'app2.hey'
}
]
app1/router.js
import app2Router from 'app2'
import Home from './views/Home.vue'
export default new Router([
mode: 'history',
...
routes: [
{
path: '/',
name: 'home',
component: Home
},
...app2Router
]
])
By marking the main or module of app2/package.json as router.js instead of the the UMD bundle, you are forcing app1 to build the whole dependency graph and include any dynamic import that is detected. This in turn causes the dependencies to be copied over properly.
You could also achieve the exact same results by using
import app2Router from 'app2/src/router'
Supposedly, this issue is now fixed in Webpack 5 https://github.com/webpack/webpack/issues/11127