Code is ran on Build
Unanswered
Largehead hairtail posted this in #help-forum
Largehead hairtailOP
I'm using NextJS and NextAuthJS in a monorepo to authenticate users for a dashboard style application. As the NextAuthJS example suggests I have an auth.ts file defining the options for the AuthHandler. For NextAuth, i have to import the AuthOptions into all of my routes which i want to be serverside authenticated.
The Problem is that, said code, which defines the AuthOptions seems to be executed during build. I know this because the code relies on environment variables. The logic I implemented to make sure my enviorment variables are present throws an error during build since the environment variables are, by design, not present during build.
I'm unsure why NextJS is trying to statically build the pages, since all code that uses these authoptions is in getServerSideProps.
See the relevant page: (frontend code shortend as it seems irrelevant)
The Problem is that, said code, which defines the AuthOptions seems to be executed during build. I know this because the code relies on environment variables. The logic I implemented to make sure my enviorment variables are present throws an error during build since the environment variables are, by design, not present during build.
I'm unsure why NextJS is trying to statically build the pages, since all code that uses these authoptions is in getServerSideProps.
See the relevant page: (frontend code shortend as it seems irrelevant)
import { getDBData } from '../db';
import { auth } from '../auth';
import { GetServerSidePropsContext } from 'next';
export const getServerSideProps = (async (context: GetServerSidePropsContext) => {
const session = await auth(context.req, context.res);
if (session) {
const dbData = await getAliveBots();
return {
props: {
serverSession: session,
dbData,
},
};
}
return {
props: {
serverSession: null,
dbData: [],
},
};
})
export default function Index({ serverSession, dbData }) {
if (serverSession) {
return (
{dbData.map((data) => (
// render a few components with data here
))
)
}
return (
<>
</>
);
}7 Replies
Largehead hairtailOP
more code is coming
auth.ts: (permission checking logic removed)
import { AuthOptions, NextAuthOptions } from 'next-auth';
import DiscordProvider from 'next-auth/providers/discord';
import { getConfig } from './utils/config';
import {Client} from 'undici';
import {APIGuild, APIGuildMember} from 'discord-api-types/v10';
import dbConnect from './db/db';
import type { GetServerSidePropsContext, NextApiRequest, NextApiResponse } from "next"
const scopes = ['identify', 'email','guilds','guilds.members.read']
const DISCORD_API_BASE_URL = 'https://discord.com';
const BASE_PATH = '/api/v10/';
async function queryDB(db) {
return await db.models.Manager_Config.findOne({managerID: getConfig().managerID}).lean();
}
let managerConfig: ReturnType<typeof queryDB>;
async function getManagerConfig() {
if (!managerConfig) {
const db = await dbConnect();
const config = await queryDB(db);
managerConfig = config;
return config;
}
return managerConfig;
}
export const authOptions: AuthOptions = {
providers: [
DiscordProvider({
clientId: getConfig().clientId,
clientSecret: getConfig().clientSecret,
authorization: {params: {scope: scopes.join(' ')}},
}),
],
secret: getConfig().jwtSecret,
callbacks: {
async session({ session, token, user }) {
session.user.id = token.id;
session.accessToken = token.accessToken;
return session;
},
async jwt({ token, user, account }) {
if (user) {
token.id = user.id;
}
if (account) {
token.accessToken = account.access_token;
}
return token;
},
signIn: async ({user, account, profile}) => {
if (!account) return false;
if (account.provider !== 'discord' && account.type !== 'oauth') return false;
const discordAccount = account as unknown as {access_token: string, refresh_token: string, expires_at: number, scope: string, token_type: string, providerAccountId: string};
const {access_token, refresh_token, expires_at, scope, token_type, providerAccountId} = discordAccount;
if (!scopes.every(scope => scope.split(' ').some(s => s === scope))) {
return false;
}
return logic(providerAccountId, (await getManagerConfig()).managingGuild, (await getManagerConfig()).managingRole, access_token);
}
}
} satisfies NextAuthOptions;
async function logic(userId: string, guildId: string, requiredRole: string, userToken: string) {
//check if user should be aloud to login
};
export function auth(...args: [GetServerSidePropsContext["req"], GetServerSidePropsContext["res"]] | [NextApiRequest, NextApiResponse] | []) {
return getServerSession(...args, config)
}finally utils/config.ts:
If I build the project now, during
export default function getEnvironmentVariable<T extends string = string>(
key: keyof NodeJS.ProcessEnv,
defaultValue?: T,
isSecret = false): T {
if (isSecret && process.env.NODE_ENV === "production")
const keyFile = key + "_FILE";
const filePath = process.env[keyFile];
if (!filePath) {
throw new Error(`Environment variable ${keyFile} is not set.`);
}
return readFileSync(filePath,{ encoding: "utf8" }).trim() as T;
} else {
const value = process.env[key] || defaultValue;
if (!value) {
throw new Error(`Environment variable ${key} is not set.`);
}
return value as T;
}
let config: {
[key: string]: string ;
};
export function getConfig() {
if (!config) {
config = {
cookieName: "token",
clientId: getEnvironmentVariable("CLIENT_ID", undefined, true),
clientSecret: getEnvironmentVariable("CLIENT_SECRET", undefined, true),
appUri: getEnvironmentVariable("APP_URL", "http://127.0.0.1:3000"),
jwtSecret: getEnvironmentVariable(
"JWT_SECRET",
"verySecretSecret",
),
databaseURL: getEnvironmentVariable("DATABASE_URL", "mongodb://db:27017/db", true),
managerID: getEnvironmentVariable("MANAGER_ID", "local"),
};
}
return config;
};If I build the project now, during
next build I get an error thrown by my code that env variable CLIENT_ID_FILE is not set. Which i do not understand since that page should definetly not be static. And if i remove that File from the project, everything compiles again (everything else is just one api route). Any idea what could be causing nextjs to build this statically?I tried setting dynamic to force-dynamic with no effect. I believe that is since that only happens in the app dir
next info:
Operating System:
Platform: linux
Arch: x64
Version: #1 SMP PREEMPT_DYNAMIC Debian 6.1.27-1 (2023-05-08)
Binaries:
Node: 18.13.0
npm: 9.2.0
Yarn: 1.22.19
pnpm: 8.8.0
Relevant Packages:
next: 13.5.3
eslint-config-next: 13.5.4
react: 18.2.0
react-dom: 18.2.0
typescript: 5.2.2
Next.js Config:
output: N/Amodule.exports = {
// output: 'standalone',
reactStrictMode: true,
transpilePackages: ["database"]
}Largehead hairtailOP
this kinda feels like a bug ngl