I am trying to build a simple web-app with Vue and a FaunaDB. When trying to fetch data from the DB i get the following error:
localhost/:1 Uncaught (in promise) SyntaxError: Unexpected token < in JSON at position 0
When i print the response from the Netlify function server this is what i get:
Here is the code from the vue-page that tries to get the data:
created() {
EventService.readAll()
.then(response => {
this.events = response.data
})
}
This is the EventService Modul:
const readAllDates = () => {
console.log("hey")
return fetch('/.netlify/functions/read-all-dates').then((response) => {
console.log(response)
return response.json()
})
}
export default {
readAll: readAllDates
}
and this is my read-all-dates.js:
import faunadb from 'faunadb'
const q = faunadb.query
const client = new faunadb.Client({
secret: process.env.FAUNADB_SECRET
})
exports.handler = (event, context, callback) => {
console.log("Function `read-all-dates` invoked")
return client.query(q.Paginate(q.Match(q.Ref("indexes/all_dates"))))
.then((response) => {
const dateRefs = response.data
console.log("Todo refs", dateRefs)
console.log(`${dateRefs.length} todos found`)
const getAllDateDataQuery = dateRefs.map((ref) => {
return q.Get(ref)
})
// then query the refs
return client.query(getAllDateDataQuery).then((ret) => {
return callback(null, {
statusCode: 200,
body: JSON.stringify(ret)
})
})
}).catch((error) => {
console.log("error", error)
return callback(null, {
statusCode: 400,
body: JSON.stringify(error)
})
})
}
What am i doing wrong?
Turns out it was vue-router which stopped the netlify proxy from directing the request to the right endoint. There seems no good way around this in dev:
https://forum.vuejs.org/t/devserver-proxy-not-working-when-using-router-on-history-mode/54720
The error you posted is one that it's worth getting familiar with!
localhost/:1 Uncaught (in promise) SyntaxError: Unexpected token < in JSON at position 0
Effectively this is caused when you try to parse something as JSON, but it's not actually JSON. The < is a tell-tale sign that the request response was probably HTML instead of JSON. The next step in debugging is to look at the XHR request itself in the browser debugging "Network" panel.
In my experience, one of the most common reasons for this error is a routing problem, which is triggering a 404 response route serving HTML instead of your expected function handler.
Related
I got a serverless Netlify function like this:
exports.handler = async function(event, context) {
return {
statusCode: 200,
body: JSON.stringify({message: "Hello World"})
};
}
When called by this url <site-name>/.netlify/functions/helloworld
I do get the message {"message":"Hello World"}
I also got a pages/api/mailingList.js Nextjs API endpoint:
const axios = require('axios');
export default async function handler(req, res) {
//console.log(req.query.mail);
if (req.method === "PUT") {
axios
.put(
"https://api.sendgrid.com/v3/marketing/contacts",
{
contacts: [{ email: `${req.query.mail}` }],
list_ids: [process.env.SENDGRID_MAILING_LIST_ID],
},
{
headers: {
"content-type": "application/json",
Authorization: `Bearer ${process.env.SENDGRID_API_KEY}`,
},
}
)
.then((result) => {
res.status(200).send({
message:
"Your email has been successfully added to the mailing list. Welcome 👋",
});
})
.catch((err) => {
res.status(500).send({
message:
"Oups, there was a problem with your subscription, please try again or contact us",
});
console.error(err);
});
}
}
This mailing list API endpoint, do work when using curl from the terminal with PUT as the method:
curl -X PUT -d mail=helloworld#gmail.com https://netlify.app/api/mailingList
The API endpoint also work from the URL (/api/mailingList?mail=helloworld#gmail.com) when removing the if (req.method === "PUT") { part from the mailingList.js
However, I am NOT able to get the API endpoint to be called from within the Netlify function.
(Preferably the mailingList API should be possible to call multiple times with different mailing list IDs from the Netlify function helloworld.js based on different logic /api/mailingList?mail=helloworld#gmail.com&listid=xxx)
To get the API endpoint to be called at all, from the function, I have tried adding a axios call from the helloworld.js to mailingList.js like this
const axios = require('axios');
exports.handler = async function(event, context) {
const mail = "helloworld#gmail.com";
// add to mailinglist
axios
.put("/api/mailingList?mail="+mail)
.then((result) => {
if (result.status === 200) {
toast.success(result.data.message);
}
})
.catch((err) => {
console.log(err);
});
}
This result in the following error from the browser: error decoding lambda response: invalid status code returned from lambda: 0
(I do not get any error msg from the Netlify log, either helloworld.js or mailingList.js)
Clearly, there is something wrong with how I call the mailigList.js from helloworld.js. Would greatly appreciate if some one could give me some advice and show me what I am doing wrong.
How can I call the API endpoint (mailigList.js) from within the Netlify function helloworld.js? (Preferably multiple times with different mailing list IDs: /api/mailingList?mail=helloworld#gmail.com&listid=xxx)
Found the solution in this article: https://travishorn.com/netlify-lambda-functions-from-scratch-1186f61c659e
const axios = require('axios');
const mail = "helloworld#gmail.com";
exports.handler = (event, context, callback) => {
axios.put("https://<domain>.netlify.app/api/mailingList?mail="+mail)
.then((res) => {
callback(null, {
statusCode: 200,
body: res.data.title,
});
})
.catch((err) => {
callback(err);
});
};
I'm building a blog with Nuxt to and Prismic as CMS.
my nuxt.config.js looks like this:
mode: 'universal',
modules: ['#nuxtjs/prismic'],
target: 'static',
generate: {
fallback: '404.html',
},
Project is deployed on Netlify with build command "npm run generate"
In pages directory I have dynamic links ( _uid.vue ) where I use the new fetch to get the post according to route.
async fetch() {
const post = await this.$prismic.api.getByUID('blog-post', this.$route.params.uid)
this.post = post
},
This all works! However I want to handle fetch errors and display correspond error page. For example when the post we try to fetch does not exist or now is deleted. I tried as they show from the link I provide above about fetch, but I get error that post is undefined.
async fetch() {
const post = await await this.$prismic.api.getByUID('blog-post', this.$route.params.uid)
if (post.id === this.$route.params.id) {
this.post = post
} else {
// set status code on server and
if (process.server) {
this.$nuxt.context.res.statusCode = 404
}
// use throw new Error()
throw new Error('Post not found')
}
}
My project on GitHub
Also I'm not sure using the fetch hook inside a page is considered a best practice, I think you should prefer asyncData with the following pattern (or async/await one):
export default {
asyncData({ params, error }) {
return axios
.get(`https://my-api/posts/${params.id}`)
.then(res => {
return { title: res.data.title }
})
.catch(e => {
error({ statusCode: 404, message: 'Post not found' })
})
}
}
From Nuxt documentation~
Could you not just catch any exceptions like this:
try {
const post = await this.$prismic.api.getByUID('blog-post', this.$route.params.uid);
if (post.id === this.$route.params.id) {
this.post = post;
}
}Â catch ((error) => {
// set status code on server and
if (process.server) {
this.$nuxt.context.res.statusCode = 404;
}
// use throw new Error()
throw new Error('Post not found');
});
Of course you would have to actually check the kind of exception occurred.
I'm trying to send some data back from my express app using the following in my app.js this data comes from a 3rd party api and I want to send it back to my front end application. I am able to see the content in my express api but it doesn't seem to deliver a result to my front end?? anyone have any ideas as to why I'm getting log below in the console.
I suspect it has to do with some async or timeout issue with the express app but I haven't been able to fix the issue myself.
function getFish(){
axios.get('https://www.fishwatch.gov/api/species')
.then(function (response) {
console.log(response)
return response
})
.catch(function (error) {
console.log(error);
})
}
app.get('/getFish', async(req,res) =>{
let fish = await getFish()
res.json({fishes: fish})
})
when I try to log the data in my app, I only get an empty object
{}
here is my vue code to log the value's returned by my express api.
created:function(){
this.fetchFish()
},
methods: {
fetchFish(){
fetch('http://localhost:3000/getFish')
.then((response) => {
return response.json();
})
.then((data) => {
console.log(data);
});
}
}
You need to return the axios promise in your server's getFish function or the /getFish route won't be awaiting anything:
function getFish(){
// New `return` statement
return axios.get('https://www.fishwatch.gov/api/species')
.then(function (response) {
console.log(response)
return response
})
.catch(function (error) {
console.log(error);
})
}
This is part 2 of me debugging my application in production
In part 1, I managed to at least see what was causing my problem and managed to solve that.
When I send a request to my API which is hosted on Heroku using axios interceptor, every single request object looks like this in the API
{ 'object Object': '' }
Before sending out data to the API, I console.log() the transformRequest in axios and I can see that the data I am sending is actually there.
Note: I have tested this process simply using
axios.<HTTP_METHOD>('my/path', myData)
// ACTUAL EXAMPLE
await axios.post(
`${process.env.VUE_APP_BASE_URL}/auth/login`,
userToLogin
);
and everything works and I get data back from the server.
While that is great and all, I would like to abstract my request implementation into a separate class like I did below.
Does anyone know why the interceptor is causing this issue? Am I misusing it?
request.ts
import axios from "axios";
import { Message } from "element-ui";
import logger from "#/plugins/logger";
import { UsersModule } from "#/store/modules/users";
const DEBUG = process.env.NODE_ENV === "development";
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_URL,
timeout: 5000,
transformRequest: [function (data) {
console.log('data', data)
return data;
}],
});
service.interceptors.request.use(
config => {
if (DEBUG) {
logger.request({
method: config.method,
url: config.url
});
}
return config;
},
error => {
return Promise.reject(error);
}
);
service.interceptors.response.use(
response => {
console.log('axios interception response', response)
return response.data;
},
error => {
const { response } = error;
console.error('axios interception error', error)
if (DEBUG) {
logger.error(response.data.message, response);
}
Message({
message: `Error: ${response.data.message}`,
type: "error",
duration: 5 * 1000
});
return Promise.reject({ ...error });
}
);
export default service;
Login.vue
/**
* Sign user in
*/
async onClickLogin() {
const userToLogin = {
username: this.loginForm.username,
password: this.loginForm.password
};
try {
const res = await UsersModule.LOGIN_USER(userToLogin);
console.log("res", res);
this.onClickLoginSuccess();
} catch (error) {
throw new Error(error);
}
}
UsersModule (VUEX Store)
#Action({ rawError: true })
async [LOGIN_USER](params: UserSubmitLogin) {
const response: any = await login(params);
console.log('response in VUEX', response)
if (typeof response !== "undefined") {
const { accessToken, username, name, uid } = response;
setToken(accessToken);
this.SET_UID(uid);
this.SET_TOKEN(accessToken);
this.SET_USERNAME(username);
this.SET_NAME(name);
}
}
users api class
export const login = async (data: UserSubmitLogin) => {
return await request({
url: "/auth/login",
method: "post",
data
});
};
I'm not sure what you're trying to do with transformRequest but that probably isn't what you want.
A quote from the documentation, https://github.com/axios/axios#request-config:
The last function in the array must return a string or an instance of Buffer, ArrayBuffer, FormData or Stream
If you just return a normal JavaScript object instead it will be mangled in the way you've observed.
transformRequest is responsible for taking the data value and converting it into something that can actually be sent over the wire. The default implementation does quite a lot of work manipulating the data and setting relevant headers, in particular Content-Type. See:
https://github.com/axios/axios/blob/885ada6d9b87801a57fe1d19f57304c315703079/lib/defaults.js#L31
If you specify your own transformRequest then you are replacing that default, so none of that stuff will happen automatically.
Without knowing what you're trying to do it's difficult to advise further but you should probably use a request interceptor rather than transformRequest for whatever it is you're trying to do.
When my axios call fails, the code in catch() correctly displays the error message contained in err.response.data.message, which is defined.
But when the axios call succeeds, I get this error in console:
Uncaught (in promise) TypeError: Cannot read property 'data' of undefined
How to fix that?
Here's my axios call code:
mounted () {
this.$axios.$get(`http://example.com/wp-json/project/v1/post/${this.$route.params.id}`)
.then((res) => {
this.title = res.post_title
this.content = res.post_content
})
.catch((err) => {
this.$toast.error(err.response.data.message)
})
Try res.data.post_title instead of res.post_title
...
.then((res) => {
this.title = res.data.post_title
this.content = res.data.post_content
})
...
Axios return an object containing the following information
{
data: {}, // the response that was provided by the server
status: 200, // the HTTP status code from the server response
statusText: 'OK',// the HTTP status message from the server response
headers: {}, // the headers that the server responded with
config: {}, // the config that was provided to `axios` for the request
request: {} // the request that generated this response
}
If you steal get the same error, then it's probably a server side mal-formatted data, try to console.log the response and see if there any problem serve side.
...
.then((response) => {
console.log(response.data);
console.log(response.status);
console.log(response.statusText);
console.log(response.headers);
console.log(response.config);
});
...