Cannot stringify a function when using axios get within nuxtServerInit - javascript

i am trying Udemy Nuxt.js course connect my app to the backend,
when trying the following code i got GET http://localhost:3000/ 500 (Internal Server Error) on the client side,
import Vuex from 'vuex';
import axios from 'axios';
const createStore = () => {
return new Vuex.Store({
state: {
loadedPosts: [],
},
mutations: {
setPosts(state, posts) {
state.loadedPosts = posts;
},
},
actions: {
nuxtServerInit(vuexContext, context) {
return axios
.get('https://nuxt-blog-7a712.firebaseio.com/posts.json')
.then((res) => {
const postArray = [];
for (const key in res.data) {
postArray.push({ ...res.data[key], id: key });
}
vuexContext.commit('setPost', postArray);
})
.catch((e) => context.error(e));
},
WARN Cannot stringify a function transformRequest
WARN Cannot stringify a function transformResponse
WARN Cannot stringify a function httpAdapter
WARN Cannot stringify a function validateStatus
WARN Cannot stringify arbitrary non-POJOs Writable
WARN Cannot stringify a function
i have google it to find some answers but couldn't find a fix, some posts talked about devalue package but do not know about this package usage

maybe late, but I had trouble with this as well. The warnings are not what are breaking your code, they are just warnings.
What is breaking your code has to be something else, but looking at your code I can't find it..
try changing
.catch((e) => context.error(e));
to
.catch((e) => console.log(e); context.error(e));
to get a better idea of what is going wrong!

Related

How to use Peer.js in Next.js with TypeScript?

Next.js runs on server side also, so Peer.js raise error when using Next.js. Here one says: https://stackoverflow.com/a/66292100/239219
this is probably because peer js is performing some side effect during import.
He propose this:
useEffect(() => {
import('peerjs').then(({ default: Peer }) => {
// Do your stuff here
});
}, [])
But I need DataConnection as using Typescript, and also asign it to a useState. would you show an example how?
This is what I put togeter, but TypesScript raise errors:
useEffect(() => {
import('peerjs').then(({ default: Peer, DataConnection }) => {
const peer = new Peer(localStorage.token)
peer.on('connection', (conn: DataConnection) => {
console.log('Connected to peer:', conn)
conn.on('data', (data) => {
console.log('Received data:', data)
})
})
return () => {
peer.destroy()
}
})
}, [])
like: 'DataConnection' refers to a value, but is being used as a type here. Did you mean 'typeof DataConnection'?
You can use a type-only import (introduced in version 3.8) at the top of the file:
import type { DataConnection } from "peerjs";
This import will be erased in the output, so rest assured that this will not import it "early".
Or if that doesn't fancy you, you can also "inline" the import:
peer.on('connection', (conn: import("peerjs").DataConnection) => {
Looks weird, but when import(...) is used as a type, it resolves to the namespace that importing the module would give you.

Logout user when jwt expires in react/redux

I am using localstorage to store a jwt in my react/redux app for authentication. I am trying to have the user get logged out if their token is expired. One way I have gotten this to work would be to use my authMiddleware.js on the backend to send the error.message and set the payload equal to an error variable, and then in a useEffect if the error is jwt expired I run my logout function (which just clears the localstorage) and reset the error to null. Like the following:
authMiddleware.js:
const jwt = require("jsonwebtoken");
const User = require("../models/user");
const protect = async (req, res, next) => {
let token = req.body.token;
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = await User.findById(decoded.id).select("-password");
next();
} catch (error) {
res.status(400).json({ message: error.message });
}
};
module.exports = { protect };
Portfolio.slice:
export const getCurrentHoldings = createAsyncThunk(
"/portfolio/getCurrentHoldings",
async (value, thunkAPI) => {
try {
const token = thunkAPI.getState().auth.user.token;
const userID = thunkAPI.getState().auth.user._id;
const newObj = {
token: token,
userID: userID,
};
let url = `http://localhost:3001/api/portfolio/getCurrentHoldings`;
const response = await axios.post(url, newObj);
console.log("New request ran in getCurrentHoldings");
return response.data;
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
return thunkAPI.rejectWithValue(message);
}
}
);
const initialState = {
error: null,
status: "idle",
holdings: null,
jwtError: null,
};
export const portfolioSlice = createSlice({
name: "portfolio",
initialState,
reducers: {
reset: (state) => initialState,
},
extraReducers(builder) {
builder
.addCase(getCurrentHoldings.pending, (state, action) => {
state.status = "loading";
})
.addCase(getCurrentHoldings.fulfilled, (state, action) => {
state.status = "success";
state.holdings = action.payload;
console.log(state.holdings);
})
.addCase(getCurrentHoldings.rejected, (state, action) => {
state.status = "failed";
state.error = action.error.message;
state.jwtError = action.payload;
})
},
});
Portfolio.js:
useEffect(() => {
if (jwtError == "jwt expired") {
dispatch(logout());
dispatch(reset());
}
}, [jwtError]);
The problem with this solution is I have multiple slices that I would need to add a similar variable for each and the useEffect would grow and start looking like:
useEffect(() => {
if (jwtError == "jwt expired") {
dispatch(logout());
dispatch(reset());
}
if (jwtError1 == "jwt expired") {
dispatch(logout());
dispatch(reset1());
}
if (jwtError2 == "jwt expired") {
dispatch(logout());
dispatch(reset2());
}
}, [jwtError, jwtError1, jwtError2]);
Thus this solution is not scalable, one way I thought to fix this was having some of the slices access data from another slice so at least the useEffect would be reduced to the original size and be scalable but I found that reducers only have access to the state they own Thus looking into this problem more I found a couple of posts related to this and I got suggestions to either 1. use cookies instead of localstate 2. Use middleware and 3. use instance.interceptors
Now one question I had for all of the above solutions is if this issue should be solved on the frontend, backend, or both? Since the middleware and instance.interceptors solution looks like its solved on the frontend. I would like to know if this is a security risk and if you should also use a backend middleware aswell.
I also would like to know if using cookies instead of useState is just a best practice, but either way I would like to implement this with localstorage also.
And finally I would like a best practices for how this should be done with redux in react and what the code might look like with my setup.
Update:
The solution I am trying currently is redux middleware and I am unable to decode the token on the frontend, installing jsonwebtoken in the react project results in a an error: Module not found: Error: Can't resolve 'crypto' in myfile. As far as I know I will need this library on the frontend if I am to decode it as suggested in middleware link.
Thus looking into this problem more I found a couple of posts related to this and I got suggestions to either 1. use cookies instead of localstate 2. Use middleware and 3. use instance.interceptors
The suggestions you got are great, I would definitely use an http-only cookie to store the token (safer because separated from the JS runtime, no malicious js code can ever see it) and a redux middleware and an axios interceptor.
The solution I am trying currently is redux middleware and I am unable to decode the token on the frontend, installing jsonwebtoken in the react project results in a an error: Module not found: Error: Can't resolve 'crypto' in myfile. As far as I know I will need this library on the frontend if I am to decode it as suggested in middleware link.
If you're using https://www.npmjs.com/package/jsonwebtoken, this seems to be a Node.js-only implementation, not meant for the browser. Looking at JWT Verify client-side? suggests that https://github.com/auth0/jwt-decode should be sufficient for you in the browser.
If you don't go with a http-only cookie based solution, there is a more elegant solution: You can decode the JWT, read the expiration time, and then schedule a function to run a few seconds before the expiration time (via setInterval) that refreshes the token. If this fails, the function can dispatch an action that logs the user out and resets the redux state to what you need it to be. This is a more proactive solution, as you don't need to wait until a request to the backend fails because of an expired token - after all you know when it will expire.

Mock Service Worker / Node isn't working and I can't see why

If anyone can spot whatever's wrong with this code, I'd really appreciate. Not seeing an issue myself, but it's failing.
import React from "react"
import {setupServer} from "msw/node"
import {rest} from "msw"
describe("mocking apis", () => {
const testCall = jest.fn()
const server = setupServer(
...[
rest.get("/test", (req, res, ctx) => {
console.log('This line is never run')
testCall()
return res(ctx.json({message: "success"}))
}),
]
)
test("test", async () => {
server.listen()
fetch("/test", {method: "GET"})
expect(testCall).toHaveBeenCalled()
server.close();
})
})
I also had this problem. After a while I realized the cause. In my src/setupTests.js file I had:
import { enableFetchMocks } from 'jest-fetch-mock';
...
enableFetchMocks();
So, fetch() was not being called at all.
I made 3 changes to the posted code to get it to work:
Import and call disableFetchMocks().
Add await before fetch(....
Change the URL to http://localhost/test, to address a test error that said I needed to use an absolute URL.
Here is the working code (cleaned up to AirB&B style by PyCharm):
import { setupServer } from 'msw/node';
import { rest } from 'msw';
import { disableFetchMocks } from 'jest-fetch-mock';
describe('mocking apis', () => {
const testCall = jest.fn();
const server = setupServer(
...[
rest.get('http://localhost/test', (req, res, ctx) => {
console.log('This line is run');
testCall();
return res(ctx.json({ message: 'success' }));
}),
],
);
test('test', async () => {
disableFetchMocks();
server.listen();
await fetch('http://localhost/test', { method: 'GET' });
expect(testCall).toHaveBeenCalled();
server.close();
});
});
When you run your tests these run in a node environment, in this fetch function does not exist (it means: global.fetch) for that reason you need to make a polyfill.
I recommend installing the npm package 'whatwg-fetch'
npm install whatwg-fetch
and use it like this:
import 'whatwg-fetch';
This video could help

Unable to use external API on Botpress (axios)

When trying to use axis to query an external Weather API, I get this error
ReferenceError: axios is not defined
at getTropicalCyclones (vm.js:16:9)
Here is my action for getTropicalCyclones {}
(of course I have to hide my client ID and secret)
const getTropicalCyclones = async () => {
const BASE_WEATHER_API = `https://api.aerisapi.com/tropicalcyclones/`
const CLIENT_ID_SECRET = `SECRET`
const BASIN = `currentbasin=wp`
const PLACE = `p=25,115,5,135` // rough coords for PH area of responsibility
const ACTION = `within` // within, closest, search, affects or ''
try {
let text = ''
let response = {}
await axios.get(
`${BASE_WEATHER_API}${ACTION}?${CLIENT_ID_SECRET}&${BASIN}&${PLACE}`
)
.then((resp) => {]
response = resp
text = 'Success retrieving weather!'
})
.catch((error) => {
console.log('!! error', error)
})
const payload = await bp.cms.renderElement(
'builtin_text',
{
text,
},
event.channel
)
await bp.events.replyToEvent(event, payload)
} catch (e) {
// Failed to fetch, this is where ReferenceError: axios is not defined comes from
console.log('!! Error while trying to fetch weather info', e)
const payload = await bp.cms.renderElement(
'builtin_text',
{
text: 'Error while trying to fetch weather info.',
},
event.channel
)
await bp.events.replyToEvent(event, payload)
}
}
return getTropicalCyclones()
So my question is, how do I import axios? I've tried
const axios = require('axios')
or
import axios from 'axios';
but this causes a different error:
Error processing "getTropicalCyclones {}"
Err: An error occurred while executing the action "getTropicalCyclones"
Looking at the package.json on GitHub, it looks like axios is already installed
https://github.com/botpress/botpress/blob/master/package.json
However, I cannot locate this package.json on my bot directory...
Secondly, based on an old version doc it looks like this example code just used axios straight
https://botpress.io/docs/10.31/recipes/apis/
How do I use axios on Botpress?
Any leads would be appreciated
Botpress: v11.0.0
Simply use ES6 import.
include this line at the top of your code.
import axios from 'axios';
Note: I'm expecting that the axios is already installed

ReferenceError: fetch is not defined

I have this error when I compile my code in node.js, how can I fix it?
RefernceError: fetch is not defined
This is the function I am doing, it is responsible for recovering information from a specific movie database.
function getMovieTitles(substr){
pageNumber=1;
let url = 'https://jsonmock.hackerrank.com/api/movies/search/?Title=' + substr + "&page=" + pageNumber;
fetch(url).then((resp) => resp.json()).then(function(data) {
let movies = data.data;
let totPages = data.total_pages;
let sortArray = [];
for(let i=0; i<movies.length;i++){
sortArray.push(data.data[i].Title);
}
for(let i=2; i<=totPages; i++){
let newPage = i;
let url1 = 'https://jsonmock.hackerrank.com/api/movies/search/?Title=' + substr + "&page=" + newPage;
fetch(url1).then(function(response) {
var contentType = response.headers.get("content-type");
if(contentType && contentType.indexOf("application/json") !== -1) {
return response.json().then(function(json) {
//console.log(json); //uncomment this console.log to see the JSON data.
for(let i=0; i<json.data.length;i++){
sortArray.push(json.data[i].Title);
}
if(i==totPages)console.log(sortArray.sort());
});
} else {
console.log("Oops, we haven't got JSON!");
}
});
}
})
.catch(function(error) {
console.log(error);
});
}
If you're using a version of Node prior to 18, the fetch API is not implemented out-of-the-box and you'll need to use an external module for that, like node-fetch.
Install it in your Node application like this
npm install node-fetch
then put the line below at the top of the files where you are using the fetch API:
import fetch from "node-fetch";
This is a quick dirty fix, please try to eliminate this usage in production code.
If fetch has to be accessible with a global scope
import fetch from 'node-fetch'
globalThis.fetch = fetch
You can use cross-fetch from #lquixada
Platform agnostic: browsers, node or react native
Install
npm install --save cross-fetch
Usage
With promises:
import fetch from 'cross-fetch';
// Or just: import 'cross-fetch/polyfill';
fetch('//api.github.com/users/lquixada')
.then(res => {
if (res.status >= 400) {
throw new Error("Bad response from server");
}
return res.json();
})
.then(user => {
console.log(user);
})
.catch(err => {
console.error(err);
});
With async/await:
import fetch from 'cross-fetch';
// Or just: import 'cross-fetch/polyfill';
(async () => {
try {
const res = await fetch('//api.github.com/users/lquixada');
if (res.status >= 400) {
throw new Error("Bad response from server");
}
const user = await res.json();
console.log(user);
} catch (err) {
console.error(err);
}
})();
If you want to avoid npm install and not running in browser, you can also use nodejs https module;
const https = require('https')
const url = "https://jsonmock.hackerrank.com/api/movies";
https.get(url, res => {
let data = '';
res.on('data', chunk => {
data += chunk;
});
res.on('end', () => {
data = JSON.parse(data);
console.log(data);
})
}).on('error', err => {
console.log(err.message);
})
fetch came to Node v17 under experimental flag --experimental-fetch
It will be available in Node v18 without the flag.
https://github.com/nodejs/node/pull/41749#issue-1118239565
You no longer need any additional package to be installed
EDITED - New Solution
To use the latest version (3.0.0) you must do the import like this:
const fetch = (url) => import('node-fetch').then(({default: fetch}) => fetch(url));
Old Anwser:
This may not be the best solution, but if you install this version :
npm install node-fetch#1.7.3
you can now use the line below without error's.
const fetch = require("node-fetch");
Node.js hasn't implemented the fetch() method, but you can use one of the external modules of this fantastic execution environment for JavaScript.
In one of the other answers, "node-fetch" is cited and that's a good choice.
In your project folder (the directory where you have the .js scripts) install that module with the command:
npm i node-fetch --save
Then use it as a constant in the script you want to execute with Node.js, something like this:
const fetch = require("node-fetch");
You should add this import in your file:
import * as fetch from 'node-fetch';
And then, run this code to add the node-fetch:
$ yarn add node-fetch
If you're working with typescript, then install node-fetch types:
$ yarn add #types/node-fetch
Best one is Axios library for fetching.
use npm i --save axios for installng and use it like fetch, just write axios instead of fetch and then get response in then().
You have to use the isomorphic-fetch module to your Node project because Node does not contain Fetch API yet. For fixing this problem run below command:
npm install --save isomorphic-fetch es6-promise
After installation use below code in your project:
import "isomorphic-fetch"
For those also using typescript on node-js and are getting a ReferenceError: fetch is not defined error
npm install these packages:
"amazon-cognito-identity-js": "3.0.11"
"node-fetch": "^2.3.0"
Then include:
import Global = NodeJS.Global;
export interface GlobalWithCognitoFix extends Global {
fetch: any
}
declare const global: GlobalWithCognitoFix;
global.fetch = require('node-fetch');
It seems fetch support URL scheme with "http" or "https" for CORS request.
Install node fetch library npm install node-fetch, read the file and parse to json.
const fs = require('fs')
const readJson = filename => {
return new Promise((resolve, reject) => {
if (filename.toLowerCase().endsWith(".json")) {
fs.readFile(filename, (err, data) => {
if (err) {
reject(err)
return
}
resolve(JSON.parse(data))
})
}
else {
reject(new Error("Invalid filetype, <*.json> required."))
return
}
})
}
// usage
const filename = "../data.json"
readJson(filename).then(data => console.log(data)).catch(err => console.log(err.message))
In node.js you can use : node-fetch package
npm i node-fetch
then :
import fetch from 'node-fetch';
here is a full sample in (nodejs) :
import fetch from "node-fetch";
const fetchData = async () => {
const res = await fetch("https://restcountries.eu/rest/v2/alpha/col"); // fetch() returns a promise, so we need to wait for it
const country = await res.json(); // res is now only an HTTP response, so we need to call res.json()
console.log(country); // Columbia's data will be logged to the dev console
};
fetchData();
In HackerRank, some libraries are installed by default and some are not.
Because it is running Node.js, the fetch API is not installed by default.
The best thing for you to do is to check whether the libraries are or not installed.
on the top of the exercise, there is the following:
const https = require('https');
Please try to add this to the top as well:
const axios = require('axios');
and then run the code.
If there is a compilation error, then it's not available, otherwise you can use axios, which is a good alternative to fetch
To use it with then, you can:
function getMovieTitles(substr){
axios.get(url)
.then(function(response){
console.log(response.data);
})
}
or taking advantage of the async/await
async function getMovieTitles(substr){
let response = await axios.get(url)
console.log(response.data);
}
This is the related github issue
This bug is related to the 2.0.0 version, you can solve it by simply upgrading to version 2.1.0.
You can run
npm i graphql-request#2.1.0-next.1
The following works for me in Node.js 12.x:
npm i node-fetch;
to initialize the Dropbox instance:
var Dropbox = require("dropbox").Dropbox;
var dbx = new Dropbox({
accessToken: <your access token>,
fetch: require("node-fetch")
});
to e.g. upload a content (an asynchronous method used in this case):
await dbx.filesUpload({
contents: <your content>,
path: <file path>
});
This worked for me:
const nodeFetch = require('node-fetch') as typeof fetch;
For me these are looking more simple.
npm install node-fetch
import fetch from "node-fetch";
There are actually a lot of different libraries for making fetch available in the browser.
The main ones I'm aware of are:
node-fetch
cross-fetch
whatwg-fetch
isomorphic-fetch
I currently use node-fetch, and it has worked fine, but I don't really know which one is "the best". (though the openbase.com pages I linked to provide some metadata on usage [eg. Github stars, npm downloads], which can help)
npm i node-fetch
Once installed, in your JavaScript file:
import fetch from "node-fetch";
Lastly make this change package.json file:
"type": "module"
Just make your app.js file Extension as app.mjs and the problem will be solved!!!:)
Solution without installations
Method 1
import { PLATFORM_ID } from '#angular/core';
import { isPlatformBrowser, isPlatformServer } from '#angular/common';
constructor(#Inject(PLATFORM_ID) private platformId: Object) {
// constructor code
}
ngOnInit() {
if (isPlatformBrowser(this.platformId)) {
// Client only code. Any javascript base functions
}
if (isPlatformServer(this.platformId)) {
// Server only code. Any javascript base functions
}
}
Method 2
import { PLATFORM_ID} from '#angular/core';
import { isPlatformBrowser } from '#angular/common';
#Component({
selector: 'app-navigation',
templateUrl: './navigation.component.html',
styleUrls: ['./navigation.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NavigationComponent implements OnInit {
private isBrowser: boolean = false;
constructor(
#Inject(PLATFORM_ID) private platformId: Object
) {
this.isBrowser = isPlatformBrowser(platformId);
}
ngOnInit(): void {
if (this.isBrowser) {
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then((response) => response.json())
.then((json) => console.log(json));
}
}
}
DEMO - JSFIDDLE - Open console to view the fetch api service working
This answer does not directly answer this question. Instead it suggests for an alternative.
Why? Because the using 'node-fetch' is getting complicated since you cannot import the updated versions using const fetch = require('node-fetch') . You will have to do more things to just make it work.
Try using axios package:
Simple installation npm i axios
code for fetching goes like
const response = await axios.get(url).then(res => res.data)
Might sound silly but I simply called npm i node-fetch --save in the wrong project. Make sure you are in the correct directory.
If need install:
npm install --save global-fetch
then
var fetch = require("node-fetch");

Categories

Resources