vuejs routing with express server - vue.js

I've a node.js (express) application listening at localhost:4000/api
I'm working on a vuejs based client with router as follows,
const routes = [
{
path: '/customers',
name: 'customers',
component: Customers,
},
{
path: '/customers/:customer_id',
name: 'customer_details',
component: CustomerDetails,
},
]
I'm managing server path via vue.config.js file as,
const path = require('path');
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://localhost:4000'
}
}
}
}
I'm also using state mgmt via store object/functionality as follows,
const BASE_URL = 'api/'
fetchCustomers(context) {
const server_url = BASE_URL + '/customers'
axios.get(server_url)
...
}
This works fine, but
fetchCustomer(context, id) {
const server_url = BASE_URL + '/customers/' + id
axios.get(server_url)
...
}
This one fails with 404 error. When I checked on the server, I see that client is making call to,
/customers/api//customers/5c891995e5212d439459ff28
instead of
/api//customers/5c891995e5212d439459ff28
And at the client side I've a router-link as follows,
<router-link :to="{'name':'customer_details',
params: {'customer_id': cid }}></router-link>
When I click on this link, I'm seeing following in the console,
GET http://localhost:8080/customers/api//customers/5c891995e5212d439459ff28 404 (Not Found)
If I remove vue.config.js and put BASE_URL with complete path as 'http://localhost:4000/api', then I don't see this error. Must be very small mistake but I'm not seeing it. Any help is appreciated.

Related

Heroku Deploy Vite Static App Won't Connect to API with Axios Using a Proxy

I think this problem follows this one on netlify API from proxy not working after deploying on netlify
I am setting up a Vite app and making an axios api request in my app component
getSuggestionList(street, zip, city) {
this.axios.get('/1.0/address/find?country=at&zip=' + zip + '&city=' + city + '&street-address=' + street + '&street-number=&offset=1&limit=100', {
auth: {
username: '7166-631A-5394-4C03-9106-0A93-C433-2613',
password: ''
}
})
Now, for development I configured a proxy in server
in vite.config.js
import { fileURLToPath, URL } from "url";
import { defineConfig } from "vite";
import vue from "#vitejs/plugin-vue";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
"#": fileURLToPath(new URL("./src", import.meta.url)),
},
},
server: {
proxy: {
'/1.0/address': 'http://api.opendata.host/'
}
},
proxy: {
'/1.0/address': {
target: 'http://api.opendata.host/',
changeOrigin: true,
secure: false,
ws: true,
}
}
});
Following this guide
https://rubenr.dev/en/cors-vite-vue/
I understand this does not transfer to production, so I also tried the proxy part. I don't get a response, just like described in the netlify guide.
From Heroku docs
https://github.com/heroku/heroku-buildpack-static
I have the basic setup without proxies (leading to the initial, no-response behavior)
in static.json:
{
"root": "./dist",
"clean_urls": true,
"routes": {
"/**": "index.html"
},
"proxies": {
"/1.0/address": {
"origin": "http://api.opendata.host/"
}
}
}
And when I add proxies I get a 404 not found. When I change up the spelling: no response again, so it seems to be making a connection with the proxies configuration. But why not they way it works locally? Does anyone see my error here or having something for me to try?
I found it: for anyone else having trouble with this kind of deployment on Heroku -- the mountpoint "/1.0/address" in static.json seems to be replaced resulting in the 404 not found.
For production I added a prefix /api in my axios call, something like
var apiPath = '/1.0/address/find?country=at&zip=' + zip + '&city=' + city + '&street-address=' + street + '&street-number=&offset=1&limit=100'
var prodMountpoint = '/api' // /api/
if (import.meta.env.PROD) {
apiPath = prodMountpoint + apiPath
}
This results in a correct api call: "/api" is replaced with the proxied host, if I understand correctly. For development, the vite.config.js does its work as before.

Why proxy in vue.config.js 404

I have a small front-end and back-end separated project with development environment and production environment, so I want to set the proxy to call api. vue/cli version is 4.6.5.
file structs:
src
axios
api.js
request.js
components
home
LastBlogs.vue
.env.development
.env.production
package.json
vue.config.js
.env.development:
NODE_ENV = 'development'
VUE_APP_BASE_API = '/dev-api'
VUE_APP_API_ADDRESS= 'http://localhost:8080/blog/'
.env.production:
NODE_ENV = 'production'
# base api
VUE_APP_BASE_API = '/api'
# api publicPath
VUE_APP_API_ADDRESS= 'http://localhost:8080/blog'
vue.config.js:
'use strict'
var path = require('path')
module.exports = {
configureWebpack: {
devtool: 'source-map'
},
assetsDir: 'static',
devServer: {
contentBase: path.join(__dirname, 'dist'),
compress: true,
port: 8001,
proxy: {
[process.env.VUE_APP_BASE_API]: {
target: [process.env.VUE_APP_API_ADDRESS], // api地址
changeOrigin: true,
ws: true,
pathRewrite: {
['^' + process.env.VUE_APP_BASE_API]: '/api',
}
}
}
}
}
axios:
import axios from 'axios'
import qs from 'qs'
// import {app} from '../main.js'
console.log(process.env)
/****** 创建axios实例 ******/
const request = axios.create({
baseURL: process.env.VUE_APP_API_ADDRESS,
timeout:5000
})
// some code of interceptors
export default request;
api.js:
import request from './request.js'
var api = process.env.VUE_APP_BASE_API //'/api'
export function getLastBlogs(){
return request({
url: api+'/blog/lastBlogs',
method: 'get'
})
}
I call api in vue file as this:
<script>
import {getLastBlogs} from '#/axios/blogApi.js'
export default {
name: 'LastBlogs',
data() {
return {
blogs: ["AAAA", "BBBB"]
}
},
created: async function(){
let res = await getLastBlogs();
this.blogs = res.data
}
}
</script>
I got 404 at terminal:
error: xhr.js:160 GET http://localhost:8080/blog/dev-api/blog/lastBlogs 404
and the api of back end is ok:
When I put http://localhost:8080/blog/api/blog/lastBlogs in browser, I get this:
{"code":"0","msg":"操作成功","data":[{"id":1,"blogUser":1,"blogTitle":"test1","blogDescription":"for test","blogContent":"ABABABABAB","blogCreated":"2020-09-20T10:44:01","blogStatus":0},{"id":2,"blogUser":1,"blogTitle":"test2","blogDescription":"for test","blogContent":"BABABABABA","blogCreated":"2020-08-20T10:44:01","blogStatus":0}]}
What should I do? Thanks.
So you are configuring Vue CLI dev-server (running on port 8001) to proxy all requests to /api to http://localhost:8080/blog/api (server) but at the same time configure Axios to use baseURL: process.env.VUE_APP_API_ADDRESS ...which means Axios is sending all requests directly to your server instead of proxy...
Just remove that baseURL: process.env.VUE_APP_API_ADDRESS from Axios config
I also believe your pathRewrite option in proxy config is incorrect.
You dispatch request to /dev-api/blog/lastBlogs
Request goes to Vue dev-server (localhost:8001)
Proxy translates /dev-api into http://localhost:8080/blog/dev-api = http://localhost:8080/blog/dev-api/blog/lastBlogs
pathRewrite is applied to whole path part of the URL - /blog/dev-api/blog/lastBlogs - RegEx ^/dev-api will not match
Try to change pathRewrite into [process.env.VUE_APP_BASE_API]: '/api'

Setup of vue.config.js file to imitate production setup (connect two apps)

I run an R Shiny app on port 3000 which serves my vue.js App like this:
library(shiny)
server <- function(input, output, session) {
histogramData <- reactive({
mtcars
})
observe({
session$sendCustomMessage("histogramData", histogramData())
})
}
ui <- function() {
htmlTemplate("dist/index.html")
}
# Serve the bundle at js/main.js
if (dir.exists("dist/js")) {
addResourcePath("js", "dist/js")
}
# Serve the bundle at js/main.js
if (dir.exists("dist/css")) {
addResourcePath("css", "dist/css")
}
# Serve the bundle at js/main.js
if (dir.exists("dist/img")) {
addResourcePath("img", "dist/img")
}
shinyApp(ui, server)
For development, I would change it like this:
ui <- function() {
htmlTemplate("public/index.html")
}
However, I can not always run the build process just to connect the apps, I want to use the dev server to connect the apps and send data back and forth.
I have setup a vue.config.js with the following configuration to create a connection between the two apps.
const path = require('path')
module.exports = {
publicPath: ".",
devServer: {
port: 4000,
contentBase: path.resolve(__dirname, 'public'),
proxy: {
'/': {
target: 'http://localhost:3000'
},
'/websocket': {
target: 'ws://localhost:3000',
ws: true
}
}
},
transpileDependencies: [
"vuetify"
]
}
This was taken from a github repository, I am acutally quite clueless how to archieve this connection. My idea was to connect go on localhost:4000 and receive the data from localhost:3000, but nothing gets passed:
TypeError: Cannot read property 'addCustomMessageHandler' of undefined at VueComponent.mounted (HelloWorld.vue?140d:42)
This is based on the following method in my vue component (which works perfectly after the build process):
mounted: function () {
window.Shiny.addCustomMessageHandler('histogramData', histogramData =>
this.data.histogramData = histogramData
)
Can anyone tell me what´s wrong and help me to setup the connection correctly?

Page refresh or direct load shows blank screen

I've read a number of solutions to this same problem, but none have worked for me. So here it goes.
I have a Vue 2 app using Express that runs on AWS Amplify. When I run my app locally in 'dev' mode (npm run serve) and 'start' mode (npm run build && node server.js), everything works fine.
The problem shows up when I deploy to Amplify. I can click nav buttons, go back, and go forward, all of which send me to the correct URL. However, the moment I refresh the page or manually enter a valid URL in the browser, the screen goes blank and the browser URL shows https://dontblowup.co/index.html.
Below is my server.js file:
const express = require('express')
const path = require('path')
const history = require('connect-history-api-fallback')
const app = express()
const port = process.env.PORT || 8080
const buildLocation = 'dist'
const staticFileMiddleware = express.static(path.resolve(__dirname, buildLocation))
app.use(staticFileMiddleware)
app.use(history())
app.use(staticFileMiddleware)
app.get('*', function (req, res) {
res.sendFile(path.resolve(__dirname, buildLocation, 'index.html'))
})
app.listen(port, () => {
console.log(`App listening to port ${port}...`)
console.log('Press Ctrl+C to quit.')
})
The solutions I found included using the history package in the server.js file and using the staticFileMiddleware before and after using history(). I also have 'history' mode set in the Vue app's router.js file (see below).
import Vue from "vue";
import VueRouter from "vue-router";
import Index from "./views/Index.vue";
import MainFooter from "./layout/MainFooter.vue";
import DashboardLayout from "#/layout/DashboardLayout.vue";
import Analytics from "#/views/Analytics.vue";
import TradeSheet from "#/views/TradeSheet.vue";
import KellyCriterionCalculator from "#/views/KellyCriterionCalculator.vue";
import PositionBuilder from "#/views/PositionBuilder.vue";
Vue.use(VueRouter);
export default new VueRouter({
mode: 'history',
routes: [
{
path: "/",
name: "Index",
components: { default: Index, footer: MainFooter },
props: {
footer: { backgroundColor: "black" }
},
meta: { requiresAuth: false }
},
{
path: "/dashboard",
redirect: "/dashboard/analytics",
name: "Dashboard",
component: DashboardLayout,
meta: { requiresAuth: true },
children: [
{
path: "/dashboard/analytics",
name: "Analytics",
component: Analytics
},
{
path: "/dashboard/trade-sheet",
name: "Trade Sheet",
component: TradeSheet
},
{
path: "/dashboard/risk-budget-calculator",
name: "Risk Budget Calculator",
component: KellyCriterionCalculator
},
{
path: "/dashboard/trade-analyzer",
name: "Trade Analyzer",
component: PositionBuilder
}
]
}
],
scrollBehavior: to => {
if (to.hash) {
return { selector: to.hash };
} else {
return { x: 0, y: 0 };
}
}
});
At this point I'm convinced there's something wrong with Amplify or Namecheap (where my DNS is configured). Unfortunately I haven't found anyone with the same issues using the same tech, so hopefully someone here can help.
Cheers!
You need to set up it on the Amplify Console
Navigate to the Amplify Console
On the left menu click on "Rewrites and redirects"
Click on Edit
Add the rule:
Source: </^[^.]+$|\.(?!(css|gif|ico|jpg|js|png|txt|svg|woff|ttf|map|json)$)([^.]+$)/>
Target: /
Type: 200
You can read more about it here
Go to section: Redirects for Single Page Web Apps (SPA)
Most SPA frameworks support HTML5 history.pushState() to change browser location without triggering a server request. This works for users who begin their journey from the root (or /index.html), but fails for users who navigate directly to any other page. Using regular expressions, the following example sets up a 200 rewrite for all files to index.html except for the specific file extensions specified in the regular expression.
the better way to handle SPA call to index.html will be
app.get(/.*/, (req, res) => res.sendFile(__dirname + '/dist/index.html'))
in frontend vue router you need to add a redirect route like this
{
path: "*",
redirect: "/"
}

how to make angular2 routes work with express in production mode

I have 2 folder: server and src. Following: angular2-express-starter
Here is my REPO
server/app.ts:
import { routerUser } from "./routes/user";
const app: express.Application = express();
/* some stuff */
// api routes
// Set our api routes
app.use("/api/user", routerUser);
if (app.get("env") === "production") {
// in production mode run application from dist folder
console.log("PRODUCTION MODE");
app.use(express.static(path.join(__dirname, "/../client")));
}
/* some stuff */
src/app/app.router.ts
import { Routes } from '#angular/router';
import { PostsComponent } from './posts/posts.component';
export const routes: Routes = [
{ path: '', pathMatch: 'full', redirectTo: 'user/profile'},
{ path: 'user/profile', component: PostsComponent }
];
And following here
I able to run server and client in separated process with proxy config:
{
"/api/*": {
"target": "http://localhost:4300",
"secure": false
}
}
Like this:
tsc -p ./server/debug
node dist-debug/server/bin/www.js
ng server --host 0.0.0.0 --proxy-config proxy.conf.json
But issue come in production mode: build both server and client into dist folder.
When I access localhost:4300, it redirect to localhost:4300/user/profile automatically, it's fine.
But if I access direct to localhost:4300/user/profile
The page response: {"error":{},"message":"Not Found"}