@clemens_s hi!
I believe what you’re looking for are the code components for fetching the data. We recently updated our Supabase example, which might help you understand how the components are built. You can check it out here:
The token is populated into the Supabase SDK via cookies when you log in or when the session is refreshed. This file might interest you the most – the global context.
In this file, we pass a cookie down to the SDK so that requests include the user auth token. In the case of an editor, we use the staticToken
property, which we copy from the network tab and set in the Studio under Project Settings > Context Settings. Just be sure to clear it before deploying, as it might otherwise be leaked to production.
To recap, here’s what you need to achieve your goal:
- Next.js server with API routes:
You need a Next.js server with API routes that return the data you need (I assume you already have that).
- Next-auth API routes:
These should already be set up.
- Global Context:
Learn more about global contexts here. The global context will:
- Populate your user data across both the app and Studio.
- Set the token for your data-fetching components.
- Refresh the token when needed.
- Provide a
staticToken
prop, which you can modify within Studio to access user data while building components.
- Data Fetching Component:
See this example.
Additionally, when working with next-auth and Plasmic, you might consider these optional steps:
- Middleware:
You might want to add middleware to set up redirects for users who are not logged in. Read more here.
- User Data in Token and Session:
Set up your user data in both a token and session so you can access it via useSession()
hooks.
- Token Refresh Logic:
Ensure that token refresh logic and expiration times are properly set up.
Finally, here’s a small example of how you can set up the GlobalContext and GlobalActionsProvider for next-auth, as discussed in step 4 as an optional path:
import React, { useEffect, useMemo, useState } from "react";
import { DataProvider, GlobalActionsProvider, usePlasmicCanvasContext } from "@plasmicapp/loader-nextjs";
import { api } from '@/pg';
import { signIn, signOut, useSession } from 'next-auth/react';
import { mutate } from 'swr';
const clearCache = () => mutate(
() => true,
undefined,
{ revalidate: false }
);
// These are functions your app should implement.
type User = {
id: string;
email: string;
name: string;
};
// Users will be able to set these props in Studio.
interface AuthGlobalContextProps {
// You might use this to override the auth token to a test token while developing. Make sure to clear before deploy.
staticToken?: string;
}
const login = async (email: string, password: string) => {
let user;
try {
user = await api.login(email, password);
} catch (error) {
return { error }
}
const response = await signIn('credentials', {
accessToken: user.access_token,
refreshToken: user.refresh_token,
expires: user.expires,
redirect: false,
});
if (!response?.error) {
clearCache();
await api.setToken(user.access_token);
// you might want to do a soft redirect instead, using router.push
// But sometimes different SDK's might struggle to apply the token.
window.location.pathname = '/';
} else {
return response;
}
};
const signup = async (userData: any) => {
try {
const response = await fetch('/api/auth/signup', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ userData }),
});
const result = await response.json();
if (response.ok) {
await login(userData.email, userData.password);
return result;
} else {
throw new Error(result.error);
}
} catch (error) {
console.error('Error during signup API call:', error);
return { error };
}
}
export const AuthGlobalContext = ({ children, staticToken }: React.PropsWithChildren<AuthGlobalContextProps>) => {
const [currentUser, setCurrentUser] = useState<User | null>(null);
const session = useSession();
const sessionData = session?.data;
const inStudio = !!usePlasmicCanvasContext();
const logout = () => signOut().then(() => {
setCurrentUser(null);
clearCache();
// same thing with hard refresh as in the comments for login.
window.location.pathname = '/login';
});
// Get current user on mount
useEffect(() => {
let newToken = sessionData?.accessToken;
if (inStudio) {
newToken = staticToken;
}
if (!newToken) {
if (sessionData?.error === 'RefreshAccessTokenError') {
logout();
}
return;
}
api
.setToken(newToken)
.then(() => {
return api.me();
})
.then(user => {
if (user?.error) {
console.log(user?.error)
return logout();
}
setCurrentUser(user)
})
.catch(error => {
if (error === 'Token expired.') {
session.update();
} else {
console.error(error);
}
});
}, [staticToken, sessionData?.accessToken, sessionData?.error]);
const refreshTokenOnFail = (call: any) => async (...args: any) => {
const resp = await call(...args);
clearCache();
if (resp?.error === 'Token expired.') {
await session.update();
}
return resp;
}
const actions = useMemo(() => ({
signup,
login,
logout: logout,
update: refreshTokenOnFail(api.update),
delete: refreshTokenOnFail(api.delete),
create: refreshTokenOnFail(api.create),
}), []);
return (
<GlobalActionsProvider contextName="AuthGlobalContext" actions={actions}>
<DataProvider name="auth" data={currentUser || {}}>
{children}
</DataProvider>
</GlobalActionsProvider>
);
}