This question already has an answer here:
Fetch error when building Next.js static website in production
(1 answer)
Closed last year.
I created an API in next JS (in the pages/api folder) and I used it on a page in the pages folder.
When I run on the localhost (development stage), the API can be called correctly. But when I deploy to Vercel there is an error during build.
This is my code when i call the API which is in the pages/api folder
export const getStaticProps = async () => {
const baseUrlDribble = 'https://api.dribbble.com/v2';
const baseUrl = process.env.NODE_ENV === 'production' ?
'https://jovanka-samudra.vercel.app/api' : 'http://localhost:3000/api';
const resShots = await fetch(`${baseUrlDribble}/user/shots?access_token=${process.env.TOKEN_DRIBBLE}&page=1&per_page=9`);
const shots = await resShots.json();
const resResult = await fetch(`${baseUrl}/projects`);
const result = await resResult.json();
const projects = result.data.projects;
return {
props: {
shots,
projects,
},
revalidate: 1,
}
}
This is the API code to retrieve data from database (pages/api/projects folder)
import ProjectService from "#services/ProjectService";
import connectDB from "#utils/connectDB";
import projectValidator from "#validators/project";
import ClientError from '#exceptions/ClientError';
const handler = async (req, res) => {
const projectService = new ProjectService();
if (req.method === 'GET') {
try {
const projects = await projectService.getProjects();
return res.status(200).json({
success: true,
length: projects.length,
data: {
projects
}
});
} catch (error) {
return res.status(500).json({
success: false,
message: error.message,
});
}
} else if (req.method === 'POST') {
...
}
return res.status(404).json({
success: false,
message: 'Method not alowed'
});
}
export default connectDB(handler);
services/ProjectService folder
import InvariantError from '#exceptions/InvariantError';
import NotFoundError from '#exceptions/NotFoundError';
import Project from '#models/Project';
class ProjectService {
async getProjects() {
const projects = await Project.find().sort({ 'createdAt': -1 });
return projects;
}
...
}
export default ProjectService;
You should not fetch an internal API route from getStaticProps — instead, you can write the fetch code in API route directly in getStaticProps.
https://nextjs.org/docs/basic-features/data-fetching/get-static-props#write-server-side-code-directly
Related
I'm trying to return data fetched from a private API and display it on a page. My frontend use React JS and my backend use node with Express and Axion. My code work up to the point of returning the data. I get my APi Key and fetch my data but the data is not transferred to my page (Quotes.js).
Backend
app.js
import express from "express";
import { getCase } from "./getCase.js";
const app = express();
app.use(function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept"
);
next();
});
app.get("/", function (req, res) {
console.log("app.js call getCase");
res.send(getCase());
//console.log(req);
});
//console.log(Quote.getQuote());
let port = process.env.PORT;
if (port == null || port == "") {
port = 5000;
}
app.listen(port, function () {
console.log(`Server started on port ${port}...`);
});
Backend getCase
import { getToken } from "./nsApiToken.js";
import axios from "axios";
let getData = "";
console.log("begin of getCase");
const getCase = async () => {
let tokenRes = await getToken();
const url =
"https://5156735-sb1.app.netsuite.com/app/site/hosting/restlet.nl?script=860&deploy=1&recordtype=supportcase&id=717986";
try {
const res = await axios.get(url, {
headers: {
Authorization: `Bearer ${tokenRes.data.access_token}`,
},
});
return res;
} catch (error) {
return error;
}
};
export { getCase };
Frontend App.js
import logo from "./logo.svg";
import "./App.css";
import Quotes from "./Quotes.js";
function App() {
return (
<div className="App">
<header className="App-header">
<Quotes />
</header>
</div>
);
}
export default App;
Frontend Quotes.js
import React, { useState, useEffect } from "react";
import axios from "axios";
const Quotes = async () => {
const [text, setText] = useState([]);
const [author, setAuthor] = useState("");
const getQuote = await axios
.get("http://localhost:5000", {
crossdomain: true,
})
.then((res) => res.data)
.then((data) => {
setText({
data: data,
});
console.log("res: ", text);
});
return (
<div>
<button onClick={getQuote}>Generate Quote</button>
<h1>{text}</h1>
<h3>{author}</h3>
</div>
);
};
export default Quotes;
Process:
When I run my process the front execute and call Quotes.js in the axios get process.
app.js then route to home ('/') and call getCase via the app.get.
The getCase process execute get the API token and add it in the headers Authorization. The process initiate the call and fetch the data (if I console.log(res.data.fields.phone) or console.log(res.data.id) I see the correct data.
In my Quotes.js I want to display the data but res.data is empty, yet I get back status 200.
I've been trying to understand why it is not passing the data from the backend to the frontend.
There are several problems and some improvements to be made.
Backend
Problem - You are sending the entire AxiosResponse in the response from your Express app
Just send the data
const getCase = async () =>
(
await axios.get(
"https://5156735-sb1.app.netsuite.com/app/site/hosting/restlet.nl",
{
params: {
script: 860,
deploy: 1,
recordtype: "supportcase",
id: 717986,
},
headers: {
Authorization: `Bearer ${(await getToken()).data.access_token}`,
},
}
)
).data; // Return the data, not the whole response
Problem - getCase() is async
You need to await the result
app.get("/", async (req, res, next) => {
try {
res.json(await getCase());
} catch (err) {
next(err); // send the error to the Express error handler
}
});
Improvement - Creating your own CORS middleware is a waste of time
By the time you create a comprehensive CORS middleware, it will look exactly the same as the standard one so just use that
import express from "express";
import cors from "cors";
const app = express();
express.use(cors());
Frontend
Problem - React function components cannot be async
Function components must return a valid JSX node. Remove async from Quotes
Problem - getQuote should be a function
In order to trigger getQuote by button click, it needs to be a function
// if text is an object, initialise it as one
const [text, setText] = useState({});
const getQuotes = async () => {
try {
// there is no "crossdomain" Axios option
const { data } = await axios.get("http://localhost:5000");
setText({ data });
} catch (err) {
console.error(err.toJSON());
}
};
Problem - the text state is an object
JSX cannot render plain objects, you instead need to reference properties that can be rendered.
<h1>{text.data?.some?.property}</h1>
No idea what your response object looks like so this is just generic advice
The reason why this is not working is for two reasons. Firstly, res.data is not an asynchronous function. And since you are doing await, you can just get data. Secondly, you need to make your API calls and setState in the useEffect hook or else it would just end up in an infinite rerender situation. You just have to do the following and it should work:
useEffect(() => {
const fetchData = async () => {
const {data} = await axios
.get('http://localhost:5000', {
crossdomain: true
})
setText(data)
}
fetchData()
}, [])
Here is my code
import { Appwrite, Query } from "appwrite";
export default defineNuxtPlugin((nuxtApp) => {
return {
provide: {
api: () => {
const api = new Appwrite();
api
.setEndpoint(useRuntimeConfig().app.endpoint) // Your API Endpoint
.setProject(useRuntimeConfig().app.project) // Your API project ID
;
return api
}
}
}
})
//path: /plugins/demo.js
I was looking for a way around using the runtime config in the /composables directory and the **error: Nuxt instance unavailable** kept popping up, so I thought why not make that function a plugin, since it's used almost everywhere.
But the plugin appears to always return null when used with useAsyncData as shown below
//path: /pages/collections/[slug].vue
const { $api } = useNuxtApp();
const { data, pending, error, refresh } = await useAsyncData(`collectionsById-${route.params.slug}`, async () => {
const products = await $api().database.listDocuments(PRODUCTS_COLLECTION_ID)
const info = await $api().database.getDocument("62750820bc5ef93a8152", route.params.slug)
return {
info: info, /* Uncaught (in promise) TypeError: Cannot read properties of null (reading 'info') at setup ([slug].vue:20:1) */
data: products
}
})
The reason for all this is using the runtimeConfig as it is not available in the /composables. Is there a reason for the error info is throwing?
And here is my code simplified
//path: /pages/collections/[slug].vue
const { $api } = useNuxtApp();
const { data, pending, error, refresh } = await useAsyncData(`collectionsById-${route.params.slug}`, async () => {
const products = await $api().database.listDocuments(PRODUCTS_COLLECTION_ID)
// const info = await $api().database.getDocument("62750820bc5ef93a8152", route.params.slug)
return products
})
In this code the data return null
I am implementing firebase authentication to Nuxt js application and I am so close. The problem is I want to commit a vuext mutation inside firebase's default function onAuthStateChanged(). But when ever I load the page it shows the following error:
"Uncaught (in promise) TypeError: Cannot read properties of undefined (reading '$store')"
Can you guys please help me out with this problem.
Thanks.
import firebase from '#/plugins/firebase'
import {
getAuth,
signInWithEmailAndPassword,
onAuthStateChanged
} from "firebase/auth"
export const state = () => ({
user: null,
authIsReady: false
})
export const mutations = {
updateUser(state, payload) {
state.user = payload
console.log('user is updated', state.user)
},
setAuthIsReady(state, payload) {
state.authIsReady = payload
console.log(state.authIsReady)
}
}
export const actions = {
async signIn(context, {
email,
password
}) {
console.log('sign in action')
const res = await signInWithEmailAndPassword(getAuth(), email, password)
if (res) {
context.commit('updateUser', res.user)
} else {
throw new Error('could not complete sign in')
}
}
}
// this function is causing the problem
const unsub = onAuthStateChanged(getAuth(), (user) => {
this.$store.commit('updateUser', user)
unsub()
})
The firebase.js file that I'm importing "auth" from below, is just all the regular setting up Firebase in Nuxt stuff... and the important lines are:
const auth = getAuth()
export { auth }
Try the code below ... I have mine in a file named "fireauth.js" in the plugins folder (don't forget to import the "fireauth.js" file in your nuxt.config.js)
import {
auth
} from "~/plugins/firebase.js";
export default (context) => {
const {
store
} = context
return new Promise((resolve, reject) => {
auth.onAuthStateChanged((user) => {
if (user) {
return resolve(store.dispatch('onAuthStateChangedAction', user))
}
return resolve()
})
})
}
In your store/index.js file add the following async function in your actions setting:
async onAuthStateChangedAction(vuexContext, authUser) {
if (!authUser) { //in my case I'm just forcing user back to sign in page, only authorized users allowed//redirect from here this.$router.push({
path: '/signin',
})
}else {
//call your commits or do whatever you want to do
vuexContext.commit("setUser", authUser.email);
}
},
The first part of the code ensures that when the auth state changes in Firestore, this change is communicated to the action that you just created in the store. The second part of the code, the async function in the store accomplishes whatever you want it to do within the store.
Having trouble using the npm google trends api in NEXTJS
Not sure if this is the right way to go about it.
I made a new API route.
http://localhost:3000/api/trends
http://localhost:3000/api/trends
import googleTrendsApi from "google-trends-api";
const handler = async (res, req) => {
const data = await googleTrendsApi
.interestOverTime({ keyword: ["Women's march", "Trump Inauguration"] })
.then(function (results) {
console.log("These results are awesome", results);
});
res.status(200).json({ data: data });
};
I get an error saying
TypeError: res.status is not a function
Try this. Here I am using TypeScript, if you are using Javascript, please edit code as needed.
import type { NextApiRequest, NextApiResponse } from 'next'
import googleTrendsApi from "google-trends-api";
type Data = {
name: string
}
export default function handler(
req: NextApiRequest,
res: NextApiResponse<Data>
) {
const data = googleTrendsApi
.interestOverTime({ keyword: ["Women's march", "Trump Inauguration"] })
.then(function (results: any) {
res.status(200).json(results)
});
}
I am trying to launch a post Axios request from my front to my back using React framework. When I use my localhost server its works but I use Heroku address, I have an error message (400 bad request) in my console.
I tried to launch other get Axios requests with heroku and it works. So I am wondering that the problem I have is related to Post Axios requests.
I will appreciate your comments
please find below my code (front react):
import React from "react";
import axios from "axios";
const Questions = () => {
const fetchData = async () => {
const response = await axios.post(
"https://formnest-back-certification.herokuapp.com/form/create",
{
title: "nouveau formulaire",
}
);
console.log(response.data);
};
fetchData();
return (
<>
<div>Hello form</div>
</>
);
};
export default Questions;
Here is my code in the back React:
router.post("/form/create", async (req, res) => {
try {
/* const titleForm = await Form.findOne({ title: req.fields.title });
console.log(titleForm);
if (titleForm) {
return res.status(400).json({ error: "title already used" });
} else { */
if (req.fields.title) {
const newForm = new Form({
title: req.fields.title,
/* questions: req.fields.questions,
answers: req.fields.answers, */
});
// Sauvegarde du formulaire
await newForm.save();
return res.json(newForm);
} else {
return res.status(400).json({ error: "Missing parameters" });
}
} catch (e) {
return res.status(400).json({ error: e.message });
}
/* console.log(req.fields); */
/*
res.json({ message: "form created" }); */
});
If you are using express framework for the POST request, you need to using body-parser module then using req.body to retrieve the request data.
req.fields is when you are using express-formidable module.