I want to load a specific configuration (opening hours, company address,…) based on the subdomain. Thus I want to run a single instance of NextJS for different clients (each client has a subdomain).
I try
export async function getStaticProps() {
const subdomain = /:\/\/([^\/?]+)/.exec(window.location.href)[1].split(".")[0];
const config = mysqlquery(subdomain);
return {
props: {
config,
}
}
}
and I get window is not defined.
First install absoluteUrl then you should request to your local api to find subdomain let see the code:
export async function getStaticProps() {
const req = await
fetch("http://localhost:3000/api/hello");
const dd = await req.json();
console.log("sub", dd);
return {
props: {
aboutData: [],
},
};
}
now in pages/api/hello.js
import absoluteUrl from "next-absolute-url";
export default async function
handler(req,res) {
const { origin } = absoluteUrl(req);
let subdomain = origin.match(/\w+/);
console.log("request ", subdomain);
return res.status(200).json({ subDomain:
subdomain });
}
it will send an array subdomain is the first index of array actually i am not sure about regex that i write you can check if not work write your own regex to determin the subdomain
You can access the url using context.req.headers.referer, being your code as follows:
export async function getStaticProps(context) {
const subdomain = /:\/\/([^\/?]+)/.exec(context.req.headers.referer).split(".")[0];
const config = mysqlquery(subdomain);
return {
props: {
config,
}
}
}
Related
I am using react-query in conjunction with Next JS getServerSideProps to fetch data before a page loads using the hydration method specified in the docs like this:
// Packages
import { dehydrate, QueryClient } from '#tanstack/react-query';
// Hooks
import { useGetGoogleAuthUrl, useGetMicrosoftAuthUrl } from '../hooks/auth';
import { getGoogleAuthUrl, getMicrosoftAuthUrl } from '../hooks/auth/api';
export async function getServerSideProps({ req, res }) {
const queryClient = new QueryClient();
const microsoftAuthQueryClient = new QueryClient(); // Not working
await queryClient.prefetchQuery(['getGoogleAuthUrl'], getGoogleAuthUrl);
await microsoftAuthQueryClient.prefetchQuery(['getMicrosoftAuthUrl'], getMicrosoftAuthUrl); // Not working
return {
props: {
dehydratedState: dehydrate(queryClient),
dehydratedMicrosoftAuthState: dehydrate(microsoftAuthQueryClient), // Not working
},
};
}
export default function Signin() {
const date = new Date();
const { data: googleAuthData } = useGetGoogleAuthUrl();
const { data: microsoftAuthData } = useGetMicrosoftAuthUrl();
console.log(googleAuthData); // logs actual data on mount and data is immediately available
console.log(microsoftAuthData); // logs undefined before eventually logging data after being successfully fetched with the useGetMicrosoftAuthUrl() query
return (
//Page content
);
}
How do I make it work as it is supposed to work. Is it not possible to make multiple requests in getServersideProps using react-query hydration method?
Thank you so much in advance
You would just use the same queryClient and prefetch both queries into it, then hydrate just the one:
export async function getServerSideProps({ req, res }) {
const queryClient = new QueryClient();
await queryClient.prefetchQuery(['getGoogleAuthUrl'], getGoogleAuthUrl);
await queryClient.prefetchQuery(['getMicrosoftAuthUrl'], getMicrosoftAuthUrl);
return {
props: {
dehydratedState: dehydrate(queryClient),
},
};
}
This however fetches them one after the other, so you might want to await them in Promise.all:
await Promise.all([
queryClient.prefetchQuery(['getGoogleAuthUrl'], getGoogleAuthUrl),
queryClient.prefetchQuery(['getMicrosoftAuthUrl'], getMicrosoftAuthUrl)
])
I have the following function:
export async function getServerSideProps({ req }: any) {
const user = (
await axios.get("http://localhost:4000/api/auth/status", {
withCredentials: true,
headers: { Cookie: `connect.sid=${req.cookies["connect.sid"]}` },
})
).data;
return { props: { user } };
}
Which fetches the users cookie, and then make a HTTP request using it, now I would have liked to do this in my _app.js file - however getServerSideProps() doesn't seem to be useable in there? Essentially, I was wondering how I would execute this function once and not have to include it in every single page file, and then be able to access its output (user) from each page.
Any suggestions would be greatly appreciated.
i had same problem for use getStaticProps. my problem solved with this way.
you can create a lib folder in project root. and create getServerSide.js file into lib.
export function makeServerSideProps(ns = {}) {
return async function getServerSideProps(ctx) {
return {
props: await getUserProps(ctx, ns),
};
};
}
and define function for receive user data getUserProps.
export async function getUserProps(ctx, ns = ['common']) {
const user = (
await axios.get("http://localhost:4000/api/auth/status", {
withCredentials: true,
headers: { Cookie: `connect.sid=${req.cookies["connect.sid"]}` },
})
).data;
return user;
}
and use makeServerSideProps into any pages:
import { makeServerSideProps} from 'lib/getServerSide';
import User from 'components/Authentication/User';
const UserPage = () => {
return (
<User/>
);
};
export default UserPage ;
const getServerSideProps = makeServerSideProps();
export { getServerSideProps };
Now, i'm in http://localhost:3000/, but on prod i will be in a different url, for example http://example.com/, how can i get full browser url in getServerSideProps? I have to get http://localhost:3000/ or http://example.com/, if string will not contain port, it will be ok
And in the additional answer you use in this my case :
export const getServerSideProps = async (
context: GetServerSidePropsContext
) => {
const { req } = context;
let url = req.headers.referer;
let arr = url.split('/');
url = `${arr[0]}//${arr[2]}`;
This URL gives you for example http://localhost:3000 enjoy!
'next/router' provide a couple of methods too, which you can use. For example:
import { useRouter } from 'next/router';
const RouterObject = () => {
const { asPath, pathname, req, query, res } = useRouter();
console.log(asPath);
console.log(pathname);
console.log('host: ', req.headers.host);
}
If you have the host you can check if contains localhost. Then you know you are in your local environment.
In SingleBlogPost.jsx i have:
export async function getStaticPaths() {
const res = await fetch("http://localhost:1337/api/posts");
let { data } = await res.json();
const paths = data.map((data) => ({
params: { slug: data.attributes.slug },
}));
return {
paths,
fallback: "blocking",
};
}
where I generate blog pages by their slug.
But then in getStaticProps I need to fetch single post by slug but I want to do it by id.
export async function getStaticProps(context) {
console.log("context", context);
const { slug } = context.params;
console.log("slug is:", slug);
const res = await fetch("http://localhost:1337/api/posts");
const { data } = await res.json();
return {
props: {
data,
},
revalidate: 10, // In seconds
};
}
And I want to keep url like /blog/:slug , I dont want to include id. in url .When I already fetch all posts in getStaticPaths how I can access post id in getStaticProps to avoid fetching by slug?
You can filter your API response by your slug to get the same result
const res = await fetch(`http://localhost:1337/api/posts?filters[slug][$eq]${slug}`);
This will generate your desired result
It looks like recently released a workaround using a file system cache.
The crux of the solution is that they save the body object in memory, using something like this:
this.cache = Object.create(null)
and creating methods to update and fetch data from the cache.
Discussion here: https://github.com/vercel/next.js/discussions/11272#discussioncomment-2257876
Example code:
https://github.com/vercel/examples/blob/main/build-output-api/serverless-functions/.vercel/output/functions/index.func/node_modules/y18n/index.js#L139:10
I found a concise work around that uses the object-hash package. I basically create a hash of the params object and use that to create the tmp filename both on set and get. The tmp file contains a json with the data I want to pass between the two infamous static callbacks.
The gist of it:
function setParamsData({params, data}) {
const hash = objectHash(params)
const tmpFile = `/tmp/${hash}.json`
fs.writeFileSync(tmpFile, JSON.stringify(data))
}
function getParamsData (context) {
const hash = objectHash(context.params)
const tmpFile = `/tmp/${hash}.json`
context.data = JSON.parse(fs.readFileSync(tmpFile))
return context
}
We can then use these helpers in the getStaticPaths and getStaticProps callbacks to pass data between them.
export function getStaticPaths(context) {
setParamsData({...context, data: {some: 'extra data'})
return {
paths: [],
fallback: false,
}
}
export function getStaticProps(context) {
context = getParamsData(context)
context.data // => {some: 'extra data'}
}
I'm sure someone can think of a nicer API then re-assigning a argument variable.
The tmp file creation is likely not OS independent enough and could use some improvement.
I'm calling a page withRouter(Page) and expect the variable for the page (the page is called [category].js) to be present on initial page load. Query itself is there, the key is there, but the value is 'undefined.' There seem to be a few calls to getInitialProps on the server side with 2/3 being undefined.
The react component has a constructor, etc. it's not a functional component.
This is my current getInitialProps:
Category.getInitialProps = async ({ req, query }) => {
let authUser = req && req.session && req.session.authUser
let categoryData = {}
let categoryItemData = {}
let category = query.category
if(category){
let res = await fetch(url1,
{
method: 'POST',
credentials: 'include',
})
categoryData = await res.json();
let categoryItemsRes = await fetch(url2,
{
method: 'POST',
credentials: 'include',
})
categoryItemData = await categoryItemsRes.json();
}
return { query, authUser, categoryData, categoryItemData }
}
This might be redundant at this point, but I ran into this as well and found the docs explain this here
During prerendering, the router's query object will be empty since we do not have query information to provide during this phase. After hydration, Next.js will trigger an update to your application to provide the route parameters in the query object.
You might try this instead:
export async function getServerSideProps(ctx) {
const { id } = ctx.query;
return {
props: {
id,
},
};
}
This way it gets the query params when rendering server side, so they're instantly available.
For others who use express custom server, to fix the undefined params, we have to set the dynamic route at server.js as follow:
# server.js
...
app.prepare().then(() => {
const server = express();
....
server.get('/product/:category', (req, res) => {
const { category } = req.params;
return app.render(req, res, `/product/${category}`, req.query)
})
...
}
And then, as Valentijn answers, we can get the category params.
# pages/product/[category].js
....
export async function getServerSideProps(ctx) {
const {category} = ctx.params;
return {
props: {
category
},
};
};
...
The key is dynamic path /product/${category}. Don't use /product/:category