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 };
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 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,
}
}
}
I'm trying to get some data from my server depending on whose currently logged in. I'm using Next-Auth and normally I can just call:
const { data: session } = useSession();
At the top of the functional component, but you cannot do this in getServerSideProps().
I need to make a get request like this:
export async function getServerSideProps() {
const res = await fetch(
`http://localhost:5000/api/users/${session.id}/following`
);
const isFollowing = res.json();
return {
props: { props: isFollowing },
};
}
that has the current users session ID dynamically put in.
How do I access my session ID inside getServerSideProps?
Since useSession is react-hook - it can be used only inside Component. For server-side usage there another method from Next-Auth package - getSession.
https://next-auth.js.org/v3/getting-started/client#getsession
Server-Side Example
import { getSession } from "next-auth/client"
export default async (req, res) => {
const session = await getSession({ req })
/* ... */
res.end()
}
Note: When calling getSession() server side, you need to pass {req} or context object.
you should to re-assign the headers from the getServerSideProps request to inner fetch, because that fetch has no headers, cookies or tokens
export async function getServerSideProps(ctx) {
const headers=ctx.req.headers //where cookies, jwt or anything
const res = await fetch(
`http://localhost:5000/api/users/${session.id}/following`,
{headers}
);
const isFollowing = res.json();
return {
props: { props: isFollowing },
};
}
I am using magic login in javascript and next js to add users to my app, it works just fine, but the problem I am having is. When a user lands back on the page I have to manually refresh the page to get my data. I have tried checking for the url param, and reloading if it exists then changing the changing the url to not have the url param so it doesn't loop.
But the router isn't even found after clicking the login button from the email sent from magic login.
import Head from "next/head";
import Image from "next/image";
import styles from "../styles/Home.module.css";
import { gql, useQuery } from "#apollo/client";
import useSWR from "swr";
import { useEffect } from "react";
import Layout from "../components/layout";
import { useRouter } from "next/router";
export const GET_DAD_HAT = gql`
query FindUserByID($id: ID!) {
findUserByID(id: $id) {
_id
hats {
data {
name
}
}
}
}
`;
const fetcher = (url) => fetch(url).then((r) => r.json());
function useAuth() {
const { data: user, error, mutate } = useSWR("/api/user", fetcher);
const loading = user?.token === false || user === undefined;
return {
user,
loading,
error,
};
}
export default function Profile() {
const { user, loading } = useAuth();
const router = useRouter();
useEffect(() => {
console.log("window", window);
console.log(Object.keys(router.query)[0]);
if (Object.keys(router.query)[0] === "magic_credentials") {
router.reload(window.location.pathname);
window.history.pushState({}, document.title, "/" + "profile");
}
if (loading) {
}
}, []);
return (
<Layout>
<main>{loading ? "Loading..." : <Data user={user} />}</main>
</Layout>
);
}
const Data = ({ user }) => {
const { loading, error, data } = useQuery(GET_DAD_HAT, {
variables: { id: user.id },
});
if (loading) return <h1>Loading...</h1>;
if (error) return <h1>{error.message}</h1>;
return <pre>{JSON.stringify(data, null, 2)}</pre>;
};
What happens is the data is just stuck on Loading until I manually hit refresh, after being redirected to the app from the email login flow.
UPDATE: I made a reproducible sandbox. https://omg5u.sse.codesandbox.io/login-magic
Enter your email, click the login link sent to email.
Feel free to use disposable email service like https://getnada.com/
if it's not blocked
When you arrive on profile page see that it is just loading, until you hit refresh then it should show your user id, and an empty array for data.
UPDATE: It looks like when I first land on the page the cookie lookup for fauna_client in the fetch to the user api route returns undefined. However after refreshing it returns the cookie. If I inspect the cookie is there before hitting refresh, but if I look in the terminal for next, the cookie is created after it is looked for. I think it has something to do with Magic redirect in the login api route creating the cookie after magic is logged in.
Still quite confused.
Had to do the following on a auth-verify page and a login-verify api route
useEffect(() => {
finishEmailRedirectLogin();
}, [router.query]);
const finishEmailRedirectLogin = () => {
if (router.query.magic_credential)
magicClient.auth
.loginWithCredential()
.then((didToken) => authenticateWithServer(didToken));
};
// Send token to server to validate
const authenticateWithServer = async (didToken) => {
let res = await fetch("/api/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer " + didToken,
},
});
if (res.status === 200) {
// Set the UserContext to the now logged in user
let userMetadata = await magicClient.user.getMetadata();
// await setUser(userMetadata);
router.push("/profile");
}
};
import { Magic } from "#magic-sdk/admin";
// Initiating Magic instance for server-side methods
const magic = new Magic(process.env.MAGIC_SECRET_KEY);
export default async function login(req, res) {
try {
const didToken = req.headers.authorization.substr(7);
await magic.token.validate(didToken);
res.status(200).json({ authenticated: true });
} catch (error) {
res.status(500).json({ error: error.message });
}
}
I'm attempting to add an Axios plugin to Nuxt as described here, but it doesn't seem to work.
This is my plugins/axios.js file...
export default function({ $axios }) {
console.log('Im in the axios plugin')
$axios.defaults.baseURL = `https://localhost:5001/api`
$axios.defaults.headers = {
Accept: 'application/json',
'Content-Type': 'application/json'
}
$axios.onRequest((config) => {
console.log('Making request to ' + config.url)
})
}
This is my nuxt.config.js
plugins: ['~/plugins/axios'],
modules: ['#nuxtjs/axios']
And this is where I use Axios in a file called services/BookService.js:
import axios from 'axios'
export default {
getBooks() {
return axios.get('/Home')
},
getBooksFiltered(payload) {
return axios.post('/Home/Filters', payload)
}
}
I get the console.log('Im in the axios plugin') from within my plugin, but nothing else. $axios.onRequest doesn't appear to run, and the baseURL doesn't appear to be set correctly when getBooksFiltered is triggered. I get a 404 when it tried to hit the address http://localhost:3000/Home/Filters. As described in my plugin, the address should be https://localhost:5001/api/Home/Filters
I've also tried the following in my nuxt.config.js, but it doesn't work:
axios: {
baseURL: 'https://localhost:5001/api'
}
Any ideas?
Edit
I've modified my services/BookService.js based on the suggestion below to the following...
export default {
getBooks(axios) {
console.log('Im in getBooks')
return axios.get('/Home')
}
}
My action request that makes my api call is the following....
import BookService from '~/services/BookService.js'
export const fetchBooks = (context) => {
console.log('Im in fetchBooks action')
return BookService.getBooks(this.$axios)
.then((response) => {
context.commit('SET_BOOKS', response.data.booksList)
})
.catch((error) => {
console.log(error)
})
}
And my method in my component that calls the actions...
async fetch({ store, error }) {
try {
console.log('Im in index -> fetch')
await store.dispatch('fetchBooks')
} catch (e) {
error({
statusCode: 503,
message: 'Unable to fetch books at this time'
})
}
}
I'm aware that I may be mixing async/await with promises incorrectly but I don't believe it's the cause of this issue.
Console returns the following...
My network tab contains a single request to http://localhost:3000/ which seems incorrect. It should be https://localhost:5001/api/Home based on the plugin and the address specified in the action. It is also never entering $axios.onRequest
The axios-module sets up an Axios instance on the Nuxt app instance. When you import Axios from axios, and use it directly, you're not using the previously setup Axios instance.
To fix the issue, you could either reference the preconfigured Axios instance from window.$nuxt.$axios (only in the browser), or setup your service to take an Axios instance as a parameter:
// services/BookService.js
export default axios => ({
getBooks() {
return axios.get('/Home')
},
getBooksFiltered(payload) {
return axios.post('/Home/Filters', payload)
}
})
// store.js
import BookService from '~/services/BookService.js'
export default {
actions: {
async getBooks({ commit }) {
const books = await new BookService(this.$axios).getBooks()
commit('SET_BOOKS', books)
}
}
}
Another solution from nuxt-community/axios-module #28:
~/plugins/axios-port.js
import { setClient } from '~/services/apiClient'
export default ({ app, store }) => {
setClient(app.$axios)
}
~/services/apiClient.js
let client
export function setClient (newclient) {
client = newclient
}
// Request helpers
const reqMethods = [
'request', 'delete', 'get', 'head', 'options', // url, config
'post', 'put', 'patch' // url, data, config
]
let service = {}
reqMethods.forEach((method) => {
service[method] = function () {
if (!client) throw new Error('apiClient not installed')
return client[method].apply(null, arguments)
}
})
export default service
Use:
import apiClient from '~/services/apiClient'
export default {
async current () {
return apiClient.get('...')
}
}
In my case I exported a customized axios instance as the doc suggested in my axios.js
export default function ({ $axios }, inject) {
const api = $axios.create({
baseURL:'/api'
})
// Inject to context as $api
inject('api', api)
}
Then use this.$api.get or this.$api.post in your getBook service
The above one works for me
As I have just tested, in each request we should use $axios.
Example: this.$axios.get('....'), or in another context this.$nuxt.$axios.get('...');
Because axios extension use with the app context instance, if we import, it will create a new instance which plugin cannot extend.
I have put test code on stackblitz: here
It seems you need to yarn add #nuxtjs/axios or npm install #nuxtjs/axios like the setup instruction here before it can work: https://axios.nuxtjs.org/setup
I haven't experienced with nuxt yet but I don't think by adding some line of code into some js file without actually installing will make the package available into your repo.