I have a page where I need to fetch data with axios which contains credentials and depending on the parameter in the url make another request using the data from the previous request.
I don't know how I can approach this since I can use both getInitialProps and getStaticProps in pages, and in components I can't use them at all.
The code below works, but I don't know how to break it down so that the login is done on the servar side so that I can still get parameters from the URL.
function Result({surveyId, responseId, sessionKey}) {
return (
<>
<div>surveyId: {surveyId}</div>
<div>responseId: {responseId}</div>
<div>sessionKey: {sessionKey}</div>
</>
)
}
Result.getInitialProps = async ({ query }) => {
// Example of URL
// http://localhost:3000/result?surveyId=12345&responseId=6
const surveyId = query.surveyId
const responseId = query.responseId
const data = { method: 'get_session_key', params: ['admin', 'password'], id: 1 }
const options = {
headers: {
'connection': 'keep-alive',
'content-type': 'application/json'
}
}
const sessionKey = await axios.post(url, data, options).then(
(response) => {
return response.data.result
},
(error) => {
console.log(error)
}
)
return {
surveyId: surveyId,
responseId: responseId,
sessionKey: sessionKey,
}
}
getStaticProps, getInitialProps or getServerSideProps, they all executed only in the pages. Because client makes the request to the pages, so behind the scene, the way how next.js sets up the routing, whenever a request hits a route, next.js first checks if this page has any of those function, and if it has it runs those functions first, gets the results as props and then runs the component functions and passes the props to the component.
getsStaticProps is used for static file generation. A good example is generating of blogs. When you run npm run build,next js will run all api calls and populate the page with blogs. So actually, if you check the build folder, inside html files of pages that getStaticPath is executed, all the data will be already inside that html file. Data will be cached by the server so when user makes a request to those statically generated files, data will be served right away. So you should not run the login process in the getStaticProps since login is a dynamic process.
All those functions are used for prerendering for better SEO optimization. But if you want to load user-specific data you dont need to prerender user-specific data for seo purpose. You could just do client-side as well.
Or you could use next.js api functions, you would be writing the function inside api directory, and from getServerSideProps you would send a request to the api route. That way, if you need to run same code in a different page, instead of writing the same code for authentication, you would be making request the api function and it would handle for you.
I found another solution which uses getStaticPaths and dynamic routes. In my case /pages/survey/[...params].js.
export default function Result({ surveyId, responseId, sessionKey }) {
return (
<>
<div>surveyId: {surveyId}</div>
<div>responseId: {responseId}</div>
<div>sessionKey: {sessionKey}</div>
</>
)
}
export function getStaticPaths() {
return { paths: [], fallback: true }
}
export async function getStaticProps({ params }) {
const surveyId = params.survey[0]
const responseId = params.survey[1]
const data = { method: 'get_session_key', params: ['admin', 'password'], id: 1 }
const options = {
headers: {
'connection': 'keep-alive',
'content-type': 'application/json'
}
}
const url = 'https://example.com'
const sessionKey = await axios.post(url, data, options).then(
(response) => {
return response.data.result
},
(error) => {
console.log(error)
}
)
return {
props: { surveyId, responseId, sessionKey },
revalidate: false,
}
}
Related
I got a nestjs application. It is listening on localhost:3000. I have health check, i can ping with curl or insomnia and it is working correctly. I can use localhost/3000/api/register to register a new user without any problem. I wanted to try it with sveltekit. And i had an issue when i tried to fetch data it and i got an error:
TypeError: fetch failed
at fetch (/Users/marcelljuhasz/Development/svelte-kit-demo/node_modules/undici/index.js:105:13)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async send (/Users/marcelljuhasz/Development/svelte-kit-demo/src/lib/api.ts:16:13)
at async default (/Users/marcelljuhasz/Development/svelte-kit-demo/src/routes/register/+page.server.ts:23:15)
at async handle_action_json_request (file:///Users/marcelljuhasz/Development/svelte-kit-demo/node_modules/#sveltejs/kit/src/runtime/server/page/actions.js:51:16)
at async resolve (file:///Users/marcelljuhasz/Development/svelte-kit-demo/node_modules/#sveltejs/kit/src/runtime/server/index.js:356:17)
at async respond (file:///Users/marcelljuhasz/Development/svelte-kit-demo/node_modules/#sveltejs/kit/src/runtime/server/index.js:229:20)
at async file:///Users/marcelljuhasz/Development/svelte-kit-demo/node_modules/#sveltejs/kit/src/exports/vite/dev/index.js:444:22
I checked my server i got the cors enabled. The front end is listening to: localhost:5173.
I have this code inside:
app.enableCors({
origin: 'http://localhost:5173',
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
allowedHeaders: 'Content-Type, Accept',
preflightContinue: true,
});
I am learning sveltekit now and i checked a project inside the github repository to see how it is works but i read the documentations too:
https://github.com/sveltejs/realworld
The structure almost the same i have the lib folder with the api.ts
import { error } from '#sveltejs/kit';
const base = 'http://localhost:3000/api';
async function send({ method, path, data }) {
const opts = { method, headers: {} };
if (data) {
opts.headers['Content-Type'] = 'application/json';
opts.body = JSON.stringify(data);
}
const res = await fetch(`${base}/${path}`, opts);
if (res.ok || res.status === 422) {
const text = await res.text();
return text ? JSON.parse(text) : {};
}
console.log(res);
throw error(res.status);
}
export function get(path) {
return send({ method: 'GET', path });
}
export function del(path) {
return send({ method: 'DELETE', path,});
}
export function post(path, data) {
return send({ method: 'POST', path, data });
}
export function put(path, data) {
return send({ method: 'PUT', path, data });
}
I have a register.svelte in the routes dir. With +page.svelte and +page.server.ts is the same like in the repository, i just exclude my own fields. The data input is return in the correct format.
+page.server.ts looks like this, almost the same like in the repo:
import { fail, redirect } from '#sveltejs/kit';
import * as api from '$lib/api.js';
/** #type {import('./$types').PageServerLoad} */
export async function load({ parent }) {
const { user } = await parent();
if (user) throw redirect(307, '/');
}
/** #type {import('./$types').Actions} */
export const actions = {
default: async ({ request }) => {
const data = await request.formData();
const user = {
username: data.get('username'),
email: data.get('email'),
password: data.get('password')
};
const body = await api.post('register', { ...user });
if (body.errors) {
return fail(401, body);
}
console.log(body)
throw redirect(307, '/');
}
};
So in a nutshell i got typerror after i hit the sign uo button. On my server log tells nothing. I see this log in the sveltekit log. I tried to check cors, but it is okey and i haven't got any cors errors in dev console. I checked in my console with curl to check if is the server available. I tried to post, get with insomnia and curl. And it is worked as expected. I have no clue for this. It is wierd if i check the chrome dev tool the request. In the general tab the request url is: localhost:5173 which is the default vite.config for sveltekit server. But i passing my own server which is localhost:3000 and i dont understand what is this behavor. If anybody have experience with sveltekit i am curious what is wrong. I tried to fetch data with an own svelte file without +page.server.ts, i put this fetch method into the component file and it is worked. Wierd.
In my project I'm using NextJs and tRPC for backend calls. I wanted to fetch some data in getServerSideProps using tRPC and provide it in Page component, also using react-query state for whole application. Here's my _app withTRPC config
export default withTRPC<AppRouter>({
config({ ctx }) {
const url = `${getBaseUrl()}/api/trpc`;
return {
url,
transformer: superjson,
queryClientConfig: { defaultOptions: { queries: { staleTime: 60 } } },
};
},
ssr: false,
})(MyApp);
I used ssr: false because of 'bug' in NextJs, which will cause to return empty props for first render, if set to true.
Here's my getServerSideProps function on the page
export const getServerSideProps = async () => {
const ssg = createSSGHelpers({
router: appRouter,
ctx: await createContext(),
transformer: superjson,
});
const entryRD = await getPageBySlug(option.some("trados"))();
await ssg.prefetchQuery("contentful.getProductList");
console.log("==>props", entryRD, ssg.dehydrate());
return {
props: {
trpcState: ssg.dehydrate(),
entryRD,
},
};
};
When I log to the console on server, both values are there, entryRD and ssg.dehydrate(). The latter contains object with mutations and queries and also data with correctly fetched data. Here is my page code:
const Page = ({ entryRD, trpcState }: InferGetServerSidePropsType<typeof getServerSideProps>) => {
const { data, isLoading } = trpc.useQuery(["contentful.getProductList"]);
console.log("==>data", data, isLoading);
return isLoading ? <div>Loading...</div> : <EntryCompontn entry={entryRD} />
When I read the docs, I understand it like:
fetch data on server,
use ssg.dehydrate() to return cache to component
when you use trpc.useQueried(), it will return cached value from state
Unfortunately data is empty and isLoading is true for a brief moment, when data is fetched. Did I misunderstood something, or did I make a mistake?
I'm working with next.js, in development mode everything works fine, but in production mode I have a problem when rendering the pages dynamically.
I have the following path inside the pages folder
pages/user/[id], and this component is where I call the getServerSideProps function.
import headers from '../../headers';
export async function getServerSideProps(context) {
const URL = 'https://somewhere...';
let { id } = context.params;
const apiResponse = await fetch(
`${URL}/${id}/detail`,
{
headers: headers,
}
);
if (apiResponse.ok) {
const data = await apiResponse.json();
return {
props: data, // will be passed to the page component as props
};
} else {
return { props: {} };
}
}
My problem is the following, I need to send in headers the authentication token that I only get when I login and I get the 2FA code, so in build time, that info does not exist and I get a 401 error no authorizate when execute npm run build and when I access to /user/34 for example I get a 404 error.
I have checked these questions at stackoverflow:
NextJs: Static export with dynamic routes
https://stackoverflow.com/questions/61724368/what-is-the-difference-between-next-export-and-next-build-in-next-js#:~:text=After%20building%2C%20next%20start%20starts,can%20serve%20with%20any%20host.&text=js%20will%20hydrate%20your%20application,to%20give%20it%20full%20interactivity.
next.js getStaticPaths list every path or only those in the immediate vicinity?
I have some parts in my app that are statics and works fine, but the problem is with the dynamic paths, as next.js is not creating those paths.
EDIT: I'll include a image with other problem, if after the fetch in the if I just say :
if(apiResponse){ //without 'ok'
}
I'll recieve this errror:
return {
props: data, // will be passed to the page component as props
}
props should be object
return {
props: {data} // or props: {data:data}
}
I am using getStaticProps and getStaticPaths, I used fetch API to call an API endpoint (which is Wordpress headless CMS in my case) and set the paths for dynamic routing. When I run npm dev it works fine, the data is fetched correctly. But at build time it gives error that:
FetchError: invalid json response body at https://abr.af/wp/wp-json/wp/v2/advisors reason: Unexpected token < in JSON at position 0
My code in pages/advisor/[advisor].js
export const getStaticPaths = async () => {
const advisors = await getAdvisors()
const paths = advisors.map((each) => {
return {
params: { advisor: each.slug },
}
})
return {
paths,
fallback: false,
}
}
export const getStaticProps = async ({ params }) => {
const query = params.advisor
const advisors = await getAdvisors()
const advisor = advisors.find((advisor) => advisor.slug === query)
return {
props: {
advisor,
},
}
}
my fetch function in component/FetchData.js
export async function getAdvisors() {
const res = await fetch('https://abr.af/wp/wp-json/wp/v2/advisors', {
method: 'GET',
headers: {
// update with your user-agent
'User-Agent': '*',
Accept: 'application/json; charset=UTF-8',
},
})
const advisors = await res.json()
return advisors
}
export async function getExpertise() {
const res = await fetch('https://abr.af/wp/wp-json/wp/v2/expertise', {
method: 'GET',
headers: {
// update with your user-agent
'User-Agent': '*',
Accept: 'application/json; charset=UTF-8',
},
})
const expertise = await res.json()
return expertise
}
I googled this issue and find that I should add User-Agent header to my request but this not solve my problem.
I am new to Next.js, I don't know what is the reason any help would be appreciated.
I had the same error, and had to switch to getServerSideProps. For my case, the api I was using was a next api that was not readily available during build time (db connection + fetching).
I had to go back to NextJS documentation, to understand when you should use getStaticProps.
And this is what is stated:
The data required to render the page is available at build time ahead
of a user’s request.
The data comes from a headless CMS.
The data can
be publicly cached (not user-specific).
The page must be pre-rendered
(for SEO) and be very fast — getStaticProps generates HTML and JSON
files, both of which can be cached by a CDN for performance.
I have a page which requires making an HTTP request to an API which might take more than 10 seconds to respond, and my host limits me to 10 second executions.
Is there a way that I can load a temporary page or something and then asynchronously load the rest of the data? I'm currently doing this:
export async function getServerSideProps({ params }) {
const res = await fetch(`${process.env.API_USER}name=${params['name']}`)
const videos = await res.json()
const tag_res = await fetch(`${process.env.API_TAG}author=${params['name']}`)
const tags = await tag_res.json()
const name = params['name']
return {
props: { videos, tags, name }, // will be passed to the page component as props
}
}
Lets's move your HTTP request from getServerSideProps to client side (your components)
// Functional component
useEffect(() => {
fetch(...)
}, [])
// Class-based component
componentDidMount() {
fetch(...)
}
If you still want to stick with getServerSideProps, maybe you have to upgrade/switch your host, or implement a proxy/wrapper server for handling your HTTP request and return response as fast as it can