Network Error when click <nuxt-link/> in NUXT? - vue.js

Network error when click <nuxt-link/>,
this is my error details, but when i refresh the link, everything is good ? why ?
this is my entire code ,...
<template>
<div>
<nuxt-child/>
<h1>Videos</h1>
<div v-for="video in $store.state.videos" :key="video.id">
<nuxt-link :to="`/videos/${video.id}`">{{ video.customerName }}</nuxt-link>
</div>
</div>
</template>
<script>
export default {
head:{
title: 'Customer List'
},
async fetch({$axios, store}) {
let response = await $axios.get('/customers');
let videos = response.data;
store.commit('SET_VIDEOS', videos);
},
}
</script>
<style lang="scss" scoped>
</style>````

Your API request is cross-origin as localhost:3000 differs from localhost:8000
Your API needs to respond to the request on /api/customers with a header of Access-Control-Allow-Origin: *. This enables the API to be called from any origin page. Ideally in the future you would want to replace this "wildcard" * with the actual origin but for testing this is fine.
I assume your API is an Express one, in this case adding this to your application would force all requests to utilise a wildcard CORS:
// Set middlware
app.use(function (req, res, next) {
// URL of website to allow or a wildcard
res.setHeader('Access-Control-Allow-Origin', '*');
// Continue to next layer of request/middleware
next();
});
Alternatively you could also set the origin to http://localhost:8000
As a side note, in your example code I see you are using the Vuex store and have a method to make the API call before committing to the store.
If you ever plan on repeating this API call on another component, it would be advised to turn your function into an action within the Vuex store so it is in one central, reusable place.

Related

Vue Vite + CosmicJS

I'm building a blog SPA website as a hobby, and trying to figure out what would be the best way to get the smallest latency to acquire posts from a database.
So far I've tried Wordpress, but its API at least at the very first initial request every time, with API cache enabled, takes about a second (800-1100ms) to load only a handful posts - 6 to be precise each with a picture and about 2-300 words, and this is only for testing.
So I'm looking around for other possible solutions to make the request faster but stay free of charge and came across Cosmic JS.
I installed the cosmicjs module, but getting all sorts of errors as I try to initiate the requests, based on their documentation which looks like the following:
<script>
const Cosmic = require('cosmicjs')
const api = Cosmic()
// Set these values, found in Bucket > Settings after logging in at https://app.cosmicjs.com/login
const bucket = api.bucket({
slug: "YOUR_BUCKET_SLUG",
read_key: "YOUR_BUCKET_READ_KEY"
})
</script>
First, you can't use require in Vite, so I've changed
this
const Cosmic = require('cosmicjs')
to this
import Cosmic from "cosmicjs"
But I'm still getting error:
ReferenceError: process is not defined
at node_modules/cosmicjs/dist/helpers/constants.js (cosmicjs.js?v=2a84de6d:1367:19)
at __require2 (chunk-NKHIPFFU.js?v=2a84de6d:15:50)
at node_modules/cosmicjs/dist/main.js (cosmicjs.js?v=2a84de6d:1387:21)
at __require2 (chunk-NKHIPFFU.js?v=2a84de6d:15:50)
at node_modules/cosmicjs/dist/index.js (cosmicjs.js?v=2a84de6d:3359:23)
at __require2 (chunk-NKHIPFFU.js?v=2a84de6d:15:50)
at cosmicjs.js?v=2a84de6d:3371:16ű
Can't figure out what to do next to even make this work, currently my code looks like this for the cosmic request part:
import Cosmic from "cosmicjs"
const api = Cosmic();
const bucket = api.bucket({
slug: "NOT-GOING-TO-SHOW-SORRY-AND-THX",
read_key: "NOT-GOING-TO-SHOW-SORRY-AND-THX",
});
const data = await bucket.objects
.find({
type: "posts", // Object Type slug
})
.props("title,slug,metadata") // response properties
.limit(10); // number of Objects to be returned
console.log(data)
Any idea might be a good help, thanks in advance
Figured it out:
So anyone trying to use in Vite ANY node module that has process as a function in any part in that module, should do the following
In your vite.config.ts or vite.config.js add the following
export default defineConfig({
// ...
define: {
'process.env': process.env
}
})
And instead of require
const Cosmic = require('cosmicjs')
always use import
import Cosmic from "cosmicjs"
Besides that, everything works the same as in other API requests, so eg. in my case, I'm API requesting posts from my cosmic js Bucket
<script setup lang="ts">
import { ref } from "vue";
import { onMounted } from "vue";
import moment from "moment";
import Cosmic from "cosmicjs"
const api = Cosmic();
const posts = ref([] as any);
const isLoading = ref(false);
const bucket = api.bucket({
slug: "c27229f0-9018-11ed-b853-65fa50acc7e7",
read_key: "G71yMNlvizQCtrvVyp9Z1AecQp8a4RTr5dl9uEGi6nst9FNQIW",
});
async function fetchData() {
isLoading.value = true
const data = await bucket.objects.find({
type: 'posts'
}).props('slug,title,content,metadata') // Limit the API response data by props
posts.value = data.objects
isLoading.value = false
console.log(posts)
}
onMounted(async () => {
fetchData();
});
</script>
and the iterate through them in my template
<template>
<ul v-if="!isLoading" class="blog-posts-ul" v-for="post in posts" :key="post.slug">
<div class="posts-card">
<a
><router-link
:to="/blog/ + post.slug"
key="post.id"
class="posts-permalink"
>
</router-link
></a>
<img
v-if="post.metadata.image != null"
class="post.metadata.hero"
:src="post.metadata.image.imgix_url"
:alt="post.title"
/>
<img v-else src="#/assets/logos/favicon-big.png" />
<div class="posts-date">
<p>
{{ moment(post.date).fromNow() + " " + "ago" }}
</p>
</div>
<div class="posts-text">
<h1 class="posts-title">{{ post.title }}</h1>
<p v-html="post.excerpt" class="posts-excerpt"></p>
</div>
</div>
</ul>
</template>
As a last sidenote, this works flawlessly compared to Wordpress API requests, I was using Wordpress for my backend CMS and even with API cache plugin enabled, it took around 800-1100ms to load the posts, now that time shrank to about 30ms for the API request of the text based data, and extra 30-40ms for each image (thumbnails for the posts).
As a request from a commenter, I'm including some description as to why Vite needs the change of vite.config.js
Basically, other vue instances do include by default the process.env in their configurations, Vite doesn't cause its generally a slower method for acquiring data - based on what I read, haven't tested this performance difference myself.
Vite is used instead of simply using Vue CLI, because its supposed to be even a bit faster then Vue CLI.
But, this is a small change.
"Why it is needed at all? I don't even see in your code above
calling any function named process"
Because it is this cosmicjs that is using it. I'm using a service that has a free tier, named Cosmic, and in their documentation it's advised to use their own node_module named comsicjs to fetch your data (posts, thumbnails etc etc).
This node module is what is using process, so when you are
importing it as I am as you can see above in my code:
import Cosmic from "cosmicjs"
And this is why you need to change your Vite's configuration file, so it can use process.
TLDR:
If you get an error in your Vite project with "process is not defined", that could be because a module you installed is using process, so simply add the following code to your vite.config.ts or vite.config.js
export default defineConfig({
....,
define: {
'process.env': process.env
}
})

How to manually generate pages in Nuxt router with a 404 page fallback for .htaccess

I'm trying to create an SSG site with Nuxt.js.
When I access a route that isn't set in the generate property of nuxt.config.js,
I want to display the contents of a 404 page without changing the URL.(using htaccess)
The following is the site under construction
http://we-are-sober.d3v-svr.com/xxxx
This is working as expected.
http://we-are-sober.d3v-svr.com/user/xxxx
This does not work as expected.
The contents of page 404 are displayed for a moment, but soon after that, the process based on the dynamic route of "user/_id.vue" is executed.
The point of the problem is that the non-existent route behaves as if it exists.
Does anyone know how to solve this problem?
Here is the source code.
https://github.com/yhirochick/rewrite_test
404.vue
https://github.com/yhirochick/rewrite_test/blob/master/pages/404.vue
user/_id.vue
https://github.com/yhirochick/rewrite_test/blob/master/pages/user/_id.vue
nuxt.config.js
https://github.com/yhirochick/rewrite_test/blob/master/nuxt.config.js#L43-L45
.htaccess
https://github.com/yhirochick/rewrite_test/blob/master/static/.htaccess
I am Japanese. The above text is based on Google Translate.
It may be difficult to understand, but thank you.
My way of handling this kind of issue while minimizing the API calls required are following those steps:
generate a brand new Nuxt project
install axios: yarn add -D axios
add this to the nuxt.config.js file
import axios from 'axios'
export default {
...
generate: {
routes: async () => {
const users = await axios.get('https://jsonplaceholder.typicode.com/users')
return users.data.map((user) => ({
route: `/users/${user.id}`,
payload: user,
}))
},
fallback: 'no-user.html', // this one is not needed anymore if you ditch the redirect!
},
}
This will generate all the needed routes, while keeping the calls to a minimum thanks to payload that will be passed later on to the pages. More info can be found in the docs.
then, creating the /pages/users/_id.vue page does the trick
<template>
<div>
<div v-if="user">User name: {{ user.name }}</div>
<div v-else-if="error">{{ error }}</div>
</div>
</template>
<script>
export default {
asyncData({ payload }) {
if (payload && Object.entries(payload).length) return { user: payload }
else return { error: 'This user does not exist' } // this will also catch users going to `/users/`
},
}
</script>
create some no-user.vue page, error.vue layout and you should be gucci
At the end, we have 10 users from the mocked API. So those are the following cases:
if we go to /users/5, the user is already static so we do have it's info without any extra API call
if we go to /users/11, the user was not present at the time of build, hence he is not here and we are displaying an error
if we go to /users, we will still be sent to the /pages/users/_id page, but since the :id will be optional there, it will error and still display the error, an index.vue can of course handle this case
My github repo for this one can be found here: https://github.com/kissu/so-nuxt-generate-placeholder-users
This approach is called full static in Nuxt, as explained here: https://nuxtjs.org/announcements/going-full-static/
It's a tricky way, but I've found the code that works as I want.
https://fes.d3v-svr.com/users/xxxxxx
It's works that I expect.
User xxxxxx doesn't exist
Display 404 page
The URL /users/xxxxxx as it is
First, simply set .htaccess to rewrite non-exist page to 404 page
ErrorDocument 404 /no-user/index.html
Only above, Nuxt execute base on URL /users/xxxxxx/ and re-render the page as "UserXXXXXX" even he is not exist.
To avoid this, users/_id.vue is like bellow.
template
<template>
<div v-if="ssr">User name: {{ user.name }}</div>
</template>
script
<script>
export default {
asyncData({ payload }) {
return { user: payload, ssr:true }
},
}
</script>
It seems to be if a template is empty, nuxt will not execute the script depends on the URL.
It's very tricky, so I'll continue to how it is works.

VueJs - wait for OPTION response before page reload

I'm trying to make a put request to backend when a user closes or refreshes the page. I've added the event listeners and I can run my functions when those events happen. But my problem is when those functions send a put request, they send an option request first, then send the put request. And this is a problem for me because when I check the backend logs I see that option request is received by the server (and gets 200 status code) but put request is not.
Here is my code:
<template>
<div id="app">
App
</div>
</template>
<script>
import axios from "axios"
export default {
name: "App",
mounted() {
window.addEventListener("beforeunload", this.reload)
},
methods: {
reload() {
this.sendRequest()
},
sendRequest() {
axios.put("http://xxxx:pp")
}
}
}
</script>
<style scoped>
</style>
One simple workaround is to use post instead of put and avoid the options request altogether. From MDN:
Cross-site requests are preflighted like this... if:
It uses methods other than GET or POST
If you don't want to do that, you'll need to handle the options request at the backend server by supplying CORS headers.

How to prevent visitor from seeing app before redirection?

I am building a SPA using Vue-CLI with a client-side OAuth 2.0 javascript library called JSO. It uses HTML 5.0 localStorage to cache Access Tokens.
In my full app, I have everything functioning properly with the exception of the following issue:
When the user arrives at my app for the first time, he catches a quick glimpse of the app and then automatically is redirected to a third party authentication login screen. I don't want that "quick glimpse" to happen -- I need to have the user immediately redirected to the third party login page BEFORE he sees any part of my app.
So, I thought I'd set up Global Before Guards using Vue-Router like so:
From: Main.js
const routes = [
{
path: '/',
name: 'home',
component: Home,
meta: {
requiresAuth: true
}
},
...more routes...and they all require auth...
]
router.beforeEach((to, from, next) => {
const token = window.localStorage.getItem('my-token-example')
if (to.matched.some(record => record.meta.requiresAuth)) {
// this route requires auth, check if logged in
if (token == null) {
client.getToken()
next(false)
}
} else {
next()
}
})
new Vue({
created: function() {
//check for response parameters if user has an auth token (uses JSO plugin)
//if token received, then it is stashed into localStorage
client.callback()
},
render: h => h(App),
router: router
}).$mount('#app')
Example App.vue:
<template>
<div id="app">
<Header />
<routerView />
</div>
</template>
Info on client.callback():
I catch the headers response when user is returning to app
Info on client.getToken():
I get the token payload
Notes: The JSO auth docs state:
"You may also ensure that a token is available early in your application, to force all user interaction and redirection to happen before your application is fully loaded. To do that make a call to getToken, and wait for the callback before you continue.
REMEMBER to ALWAYS call the callback() function to process the response from the OAuth server, before you use getToken(), if not you will end up in an redirect_loop"
Does anyone have any suggestions on how I can prevent the user from seeing my app before he is redirected to the auth login site? Update: I think I see the problem...is the global guards only affecting the section of the app within the <RouterView /> component? Hence, we see the header and banner of my app before redirection?
I solved this. I had to simply do a v-if on my app.vue file like: <div v-if="token !== null>
That hides the app template until token is received.

res.render for routes on page reload?

(Using MEAN with UI Router)
The following code sends a json response for the route defined. This works fine when the template is rendered with UI Router; however, if I reload the page, because the response only contains json, I am left with an empty page rendering no html, only the json response.
router.get('/posts/:post', function(req, res, next) {
req.post.populate('comments', function(err, post) {
if (err) { return next(err); }
res.json(post);
});
});
Assuming this is a standard issue, how can I best allow this page to res.render('index') when the page is reloaded and respond with the json response? Should I,
Create a separate route for the json response which is called as a post promise with UI Router
Have the /posts/:post route simply respond with res.render('index')?
Thank you for any responses, not sure what the usual practise is for such issues!
It took me a while to find a working solution to this due to many of the examples online having different directory structures. I placed a catch all at the end of my routes so that url requests to any UI Router states would not be met with a 404, but instead always return the index.html file.
app.all('/*', function(req, res, next) {
// Just send the index.html for other files to support HTML5Mode
res.sendFile('index.html', { root: __dirname });
});
Then I added prefixes to my express routes, e.g. /api/posts/:post etc. Apparently express routes should not clash with any of the angular defined routes. Thanks to NormySan on reddit for informing me about this.