Nuxt3 useFetch sends request only once - vue.js

<script setup lang="ts">
const loadPost = () => {
console.log('load')
const { data, pending, error, refresh } = useFetch(
'https://jsonplaceholder.typicode.com/posts',
{
method: 'POST',
body: {
title: 'foo',
body: 'bar',
userId: 1,
},
headers: {
'Content-type': 'application/json; charset=UTF-8',
},
})
console.log(data)
}
</script>
<template>
<div class="max-w-xs space-y-4 mx-auto mt-4">
<button #click.prevent="loadPost">Load post</button>
</div>
</template>
After clicking on the load button, I see every time the request is processed through the console log, but I do not see a new request in the network chrome devtools, I need to receive a response from the server every time, how can I do this?
Note: If I use a regular fetch(), then the request is sent every time, as it should be
my nuxt version - 3.0.0-rc.1

Thanks! Solved by adding param initialCache: false
useFetch('https://reqres.in/api/users?page=2?delay=1', { initialCache: false })

If you want to re-fetch the data, use the refresh() method returned from useFetch():
<script setup lang="ts">
const { data, pending, error, refresh } = await useFetch('https://reqres.in/api/users?page=2?delay=1')
const loadPost = () => {
refresh()
}
</script>
demo

Watch out! you need a key. In you don't provide one, will generate it from the URL. This might not get you the result you expect. Generate a different key if the data will change.
Here is in the docs
Options (from useAsyncData):
key: a unique key to ensure that data fetching can be properly de-duplicated across requests, if not provided, it will be generated based on the url and fetch options.

Related

How to check authentication in SvelteKit?

I want to check if the user is logged in when they visit the route /login and, if so, redirect them to /. The same happens vice versa if they are not logged in as well.
I want to put something like:
export async function load() {
const res = await fetch("[endpoint]", {
headers: {
"Authorization": `Bearer ${localStorage.jwt}`
},
credentials: "include"
});
const json = await res.json();
return {
logged_in: json.logged_in
};
}
in my +page.js (or +page.server.js?) but this doesn't work as it says localStorage is undefined.
How should I go about doing this?
LocaleStorage in Sveltekit is a bit tricky, but there are ways around it, like checking to see if your code is being executed on the client's browser, like so:
import { browser } from '$app/env'
export async function load(){
if (browser) {
// Do stuff here
...
}
...
}
A solution that's worked for me is chucking this code into the <script context="module"> of a base __layout.svelte that every layout inherits from.
For instance, you could have a __layout.svelte that consists of nothing more than this code. Building off of what Stephane pointed out, if you don't want to use cookies (e.g. a jwt token with a very limited lifespan) you could use session storage;
<script context="module">
export async function load({ fetch, session }) {
const res = await fetch("[endpoint]", {
headers: {
"Authorization": `Bearer ${session.jwt}`
},
credentials: "include"
});
const json = await res.json();
return {
logged_in: json.logged_in
};
}
</script>
<slot />
(You can read more about load() here)
And then have other layouts that inherit this layout by having a layout like __layout-auth#default.svelte. You can read more about named layouts here.

How to POST file data to a presigned S3 URI with Vue?

I'm building a Vue app with a Rails backend. I'm following some articles online that suggested a workflow in which I:
Let my Rails API generate a pre-signed S3 url
Which I get through an API request in my Vue app
I use the data from that request to POST the actual image directly to S3
The first two steps are working fine, but I'm struggling to understand how to include the filedata in the request that is of type: 'multipart/form-data'.
My Code is as follows, I use vuetify as a component library:
<template>
<v-form>
<v-file-input v-model="file" label="File input"></v-file-input>
<v-btn class="mt-2" block bottom color="primary" #click="submit">Opslaan</v-btn>
</v-form>
</template>
<script>
import { axios_request } from '../_helpers';
export default {
name:'AccountImageForm',
data: () => ({
file: {}
}),
methods: {
filesChange(event) {
this.file = event.target.files[0]
},
submit() {
axios_request('/api/v1/images/upload_url')
.then(
response => {
// this response contains the pre-signed info
var data = {
...response.url_fields,
file: this.file
}
var headers = { 'Content-Type': 'multipart/form-data' }
axios_request(response.url, 'POST', data, headers)
.then(
response => {
console.log(response)
}
)
}
)
},
}
}
</script>
This requests fails with the following error
<Error>
<Code>MalformedPOSTRequest</Code>
<Message>The body of your POST request is not well-formed multipart/form-data.</Message>
<RequestId>x</RequestId>
<HostId>xx</HostId>
</Error>
When I look at my original formData, it seems that the filedata is empty. Also the size of the request is not big enough, so my assumption is that the file is missing. Is there some additional work to serialize the file for this request?
Thanks
The issue is that you're trying to post multipart/form-data but are sending a JS object literal which Axios is probably just stringifying.
What you need to do instead is create a FormData instance.
For example
response => {
// convert your url_fields into a FormData instance
const data = Object.entries(response.url_fields).reduce((fd, [ key, val ]) =>
(fd.append(key, val), fd), new FormData())
// now add the file
data.append('file', this.file)
axios_request(response.url, 'POST', data)
.then(...)
}

Prefetching data with Nuxt to improve loading time

As far as I understand people are using server side rendering (ssr) to improve user experience and SEO with fast and content ready pages.
Recently I've started project with vue+nuxt in ssr mode.
What I've noticed is that when I try to prefetch data in nuxtServerInit action I have to wait for the async call to finish before page could be served to me.
As far as I know it will only hurt SEO and user experience.
export const actions = {
async nuxtServerInit ({ commit }) {
const response = await this.$axios.$get('games')
commit('setGameList', response.data)
}
}
Is there a way to actually prefetch data once and cache it for some period of time so that users would not be forced to wait?
Also what is the good usecase for nuxtServerInit? Cant understand the purpose of it..
Use The fetch Method
<template>
<h1>Stars: {{ $store.state.stars }}</h1>
</template>
<script>
export default {
async fetch ({ store, params }) {
let { data } = await axios.get('http://my-api/stars')
store.commit('setStars', data)
}
}
</script>
Remember to use Vuex to work well!
UPDATE: How to share the fetch function
1 - Create a file with the function:
// defaultFetch.js
module.exports = async function defaultFetch({ store, params }){
// Put some developer magic here to make the code works for you
let { data } = await axios.get('http://my-api/stars');
store.commit('setStars', data);
}
2 - Import and use in other components
// amazingComoponent1.vue
<template>
<h1>Stars: {{ $store.state.stars }}</h1>
</template>
<script>
import defaultFetch from "../utils/defaultFetch";
export default {
fetch: defaultFetch
}
</script>
// amazingComoponent2.vue
<template>
<h1>Stars: {{ $store.state.stars }}</h1>
</template>
<script>
import fetch from "../utils/defaultFetch";
export default {
fetch,
}
</script>
UPDATE 2: How to use and configure axios intance
Very easy, update the defaultFetch.js:
// defaultFetch.js
const instance = axios.create({
baseURL: 'https://some-domain.com/api/',
timeout: 1000,
headers: {'X-Custom-Header': 'foobar'}
});
module.exports = async function defaultFetch({ store, params }){
// Put some developer magic here to make the code works for you
let { data } = await instance.get('http://my-api/stars');
store.commit('setStars', data);
}
Hope helps :)

Axios-Make multiple request at once (vue.js)

How to make multiple requests in parallel using axios and vue ?
Since axios can be used by React and Vue it is pretty much the same code.
Make sure to read axios docs, you can understand it from there.
Anyway, I am going to show you an example:
<template>
<div>
<button #click="make_requests_handler">Make multiple request</button>
{{message}} - {{first_request}} - {{second_request}}
</div>
</template>
And the script:
import axios from 'axios'
export default {
data: () => ({
message: 'no message',
first_request: 'no request',
second_request: 'no request'
}),
methods: {
make_requests_handler() {
this.message = 'Requests in progress'
axios.all([
this.request_1(), //or direct the axios request
this.request_2()
])
.then(axios.spread((first_response, second_response) => {
this.message = 'Request finished'
this.first_request = 'The response of first request is' + first_response.data.message
this.second_request = 'The response of second request is' + second_response.data.message
}))
},
request_1() {
this.first_request: 'first request began'
return axios.get('you_URL_here')
},
request_2() {
this.second_request: 'second request began'
return axios.get('another_URL', { params: 'example' })
}
}
}
You can pass your asynchronous calls to Promise.all.
As long as each of them returns a promise they will execute at the same time.
I'm using store.dispatch but you could equally use axios calls or fetch.
In this example i'm making the calls when the vue component gets created:
...
async created() {
const templates = this.$store.dispatch(TEMPLATES_LOAD);
const userTemplates = this.$store.dispatch(USER_TEMPLATES_LOAD);
const players = this.$store.dispatch(OTHER_PLAYERS_LOAD);
return await Promise.all([templates, userTemplates, players])
.then(() => {
console.log('Loaded data for form elements');
});
}

Axios and vue-resource put method doesn't work

I have on front vuejs and on backend java. In one component I bring the users from the database.
Users.vue
getUsers() {
this.$http.get("/user")
.then((response) => {
this.users = response.data.users;
})
}
Still here I use v-for to bring all users in another component User.vue.
<app-user v-for="user in users" :user="user" :key="user.index"></app-user>
In user component I have a router link that takes me to another page where I can edit the username.
User.vue
<p class="user-name">{{user.firstName}}</p>
<router-link :to="'/users/edit-user/'+user.id">
<a ><i class="ti-pencil-alt" aria-hidden="true"></i></a>
</router-link>
EditUser.vue
<template>
<input type="text" :value="user.firstName" v-model="userInfo.firstName">
</template>
<script>
export default {
data() {
return {
user: {},
userInfo: {
firstName: '',
}
}
},
created() {
this.getUsers();
},
methods: {
getUsers() {
this.$http.get("/user/" + this.$route.params.id)
.then((response) => {
this.user = response.data;
})
},
updateUser() {
axios.put('/user', this.userInfo,
{'headers':{'X-AUTH-TOKEN':localStorage.token}},
{'headers':{'Content-Type': 'application/json'}})
.then((response) => {
console.log("Success! You edited the user");
})
.catch((response) => {
console.log('Error in edit');
})
}
},
}
</script>
I started learning vuejs a month ago and still have to learn :).
In the input I use :value="user.firstName" to bring the value for firstName that already exists. I try to use v-model="userInfo.firstName" to get new value for userName, but when I put this, the value, that existed already, disappears from the input.
To save data with post works fine but only with axios. I don't know why post doesn't work with vue-resource. So I tried put with axios too, but what I edit when I press save button, on EditUser.vue, my request doesn't go to server.
I say this because I saw that in the backend I don't get any error, nothing, but if I use post or get I can get or save users.
What do I do wrong in my code that I don't edit the user?
There seems to be nothing wrong with your axios implementation. Seems to me that you are updating to the general /users/ route. This works for post, since no user id exists yet, so a new one will be created. However, if you use PUT, a user id does exist. So I think you should modify your endpoint to be /user/:userid instead of just /user. Hopes this helps.
From restapitutorial dot com/lessons/httpmethods.html:
Examples:
POST http://www.example.com/customers
PUT http://www.example.com/customers/12345