Skip to content

Different getStaticPaths behavior with _app.js in development vs. production #16977

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
ericclemmons opened this issue Sep 9, 2020 · 14 comments
Closed
Milestone

Comments

@ericclemmons
Copy link

ericclemmons commented Sep 9, 2020

Bug report

Describe the bug

In _app.js, I have Amplify.configure(...) that stores a configuration for internal use with other Amplify features (e.g. API):

// pages/_app.js
import { Amplify, API } from "aws-amplify"

Amplify.configure({ ... })

export default function MyApp({ Component, pageProps }) {
// pages/index.js
import { API } from "aws-amplify"

export default function Home() {
  useEffect(() => {
	// API internally references the configuration in `Amplify`
    API.graphql(...).then(console.log)
  })
...

However, when using getStaticProps for a page, I found that the instance of Amplify isn't configured when running next build. It's as if _app.js isn't imported or executed at all during the build!

To Reproduce

Simple reproduction here: ericclemmons/next.js-issues-16977@bf9f827!

Notice how my static page pages/[isConfigured].js receives a value of true in development (due to _app.js setting the configuration) while false in production (because _app.js is presumably not called prior to executing getStaticPaths).

gsp-bug

👇 This is my "real-world" code from when I discovered this behavior. It shouldn't differ from the simpler reproduction above.

// pages/_app.js
console.log('🕵️‍♀️', 'pages/_app.js', 'imported')

import '../styles/globals.css'
import { Amplify } from 'aws-amplify'
import awsExports from '../src/aws-exports'

console.log('🕵️‍♀️', 'pages/_app.js', 'Amplify.configure')
Amplify.configure({ ...awsExports, ssr: true })
console.log('🕵️‍♀️', 'pages/_app.js', 'Amplify configuration', Amplify.configure())

export default function MyApp({ Component, pageProps }) {
  console.log('🕵️‍♀️', 'pages/_app.js', 'MyApp')

  return <Component {...pageProps} />
}
// pages/posts/[id].js
console.log('🕵️‍♀️', 'pages/[id].js', 'imported')

import { Amplify, API, withSSRContext } from 'aws-amplify'
import Head from 'next/head'
import { useRouter } from 'next/router'
import styles from '../../styles/Home.module.css'
import { getPost, listPosts } from '../../src/graphql/queries'
import { deletePost } from '../../src/graphql/mutations'

console.log('🕵️‍♀️', 'pages/[id].js', 'Amplify configuration', Amplify.configure())

export async function getStaticPaths() {
  console.log('🕵️‍♀️', 'pages/[id].js', 'getStaticPaths')

  const SSR = withSSRContext()
  const response = await SSR.API.graphql({ query: listPosts })
  const paths = response.data.listPosts.items.map((post) => ({
    params: { id: post.id },
  }))

  return {
    // Statically build pages at run-time, but SSR any new ones after
    // See: https://nextjs.org/docs/basic-features/data-fetching#fallback-pages
    fallback: false,
    paths,
  }
}

// `/pages/posts/[id].js` => `params.id`
export async function getStaticProps({ params }) {
  console.log('🕵️‍♀️', 'pages/[id].js', 'getStaticProps')

  const SSR = withSSRContext()
  const response = await SSR.API.graphql({
    query: getPost,
    variables: { id: params.id },
  })

  return {
    props: {
      post: response.data.getPost,
    },
  }
}

export default function Post({ post }) {
  console.log('🕵️‍♀️', 'pages/[id].js', 'Post')

  const router = useRouter()

  if (router.isFallback) {
    return <div>Loading post...</div>
  }

  async function handleDelete() {
    await API.graphql({
      authMode: 'AMAZON_COGNITO_USER_POOLS',
      query: deletePost,
      variables: { input: { id: post.id } },
    })

    window.location.href = '/'
  }

  return (
    <div className={styles.container}>
      <Head>
        <title>{post.title} – Amplify + Next.js Blog</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main className={styles.main}>
        <h1 className={styles.title}>
          <small className={styles.description}>
            <a href="/">Home</a> &raquo;
          </small>

          {post.title}
        </h1>
        <p className={styles.description}>{post.content}</p>
      </main>

      <footer className={styles.footer}>
        <button onClick={handleDelete}>🗑 Delete Post</button>
      </footer>
    </div>
  )
}

Expected behavior

I expected that, if a page extends _app.js, any effects that may have occurred within that file to persist in other pages during the build process.

System information

  • OS: macOS
  • Version of Next.js: 9.5.3
  • Version of Node.js: 12.16.3
@ericclemmons ericclemmons changed the title Different getStaticPaths behavior with _app.js doesn't affect execution context of getStaticPaths when building Different getStaticPaths behavior with _app.js in development vs. production Sep 9, 2020
@Timer Timer added this to the iteration 9 milestone Sep 9, 2020
@Timer
Copy link
Member

Timer commented Sep 9, 2020

Thanks for the report! We'll take a look at the reproduction soon and ensure the behavior is consistent one way or the other.

@ericclemmons
Copy link
Author

@Timer Thanks for the quick response! Let me know if I can help at all. That repro should help make this approachable.

FYI, I was able to confirm that moving Amplify.configure into all the pages resolves the issue, but that seems undesirable for medium-large sites.

@ericclemmons
Copy link
Author

Shoot. I was preparing my talk for Next.js and did yarn build for a sanity check then hit this error again.

I'll be sure to call out that this bug is being addressed and to put Amplify.configure in each page just to be certain.

If y'all are busy for these sprints, if you can point me to the tests/source that needs updating I can get it a PR started 🙏

ericclemmons added a commit to ericclemmons/next.js-conf-2020 that referenced this issue Oct 20, 2020
@Timer Timer modified the milestones: iteration 10, iteration 11 Oct 27, 2020
@Timer Timer added the point: 2 label Oct 30, 2020
@Timer Timer modified the milestones: iteration 11, 10.x.x Oct 30, 2020
@MontoyaAndres
Copy link

MontoyaAndres commented Nov 17, 2020

Waiting for it! In my case when I execute Auth.signOut, sometimes the user is logout and sometimes not. This is what I'm doing:

  const signOut = () => {
    Auth.signOut().then(() => {
      router.reload();
    });
  };

When the user executes the function signOut, the page is reloaded, then the /page/MySecretPage.tsx executes this method to validate if the user has access or not:

export async function getServerSideProps(context) {
  await withAuth(context);

  return {
    props: {},
  };
}

This is the function withAuth:

import { withSSRContext } from 'aws-amplify';

export const withAuth = async context => {
  try {
    const SSR = withSSRContext(context);
    const user = await SSR.Auth.currentAuthenticatedUser();

    return user;
  } catch (error) {
    context.res.writeHead(301, { Location: '/signin' });
    context.res.end();
  }
};

Right now I have the Amplify.configure on the _app.tsx file. I haven't tried on each page as the docs explain.

@MatiasVerdier
Copy link

I'm having the same issue

OS: macOS
Version of Next.js: 10.0.7
Version of Node.js: 14.16.0

@MatiasVerdier
Copy link

This seems to be solved in version v10.0.8 but I don't know exactly how

@kylemh
Copy link
Contributor

kylemh commented Apr 28, 2021

@ericclemmons are you seeing ^ as well?

@ababushkin
Copy link

Seems to work for me as well, I'm on 11.0.1 (with webpack4) and I can see my data being resolved on the server even though my Amplify app is hoisted only in _app

@PeterEckIII
Copy link

@ericclemmons, unfortunately, the suggested workaround is not fixing the issue for me. I've included some details below:

pages/games/[id].js

import { useRouter } from "next/router";
import Amplify, { withSSRContext } from "aws-amplify";
import awsExports from '../../src/aws-exports';
import * as queries from '../../src/graphql/queries';

Amplify.configure({ ...awsExports, ssr: true });

const Game = ({ game }) => {
    const router = useRouter();

    if (router.isFallback) {
        return <div>Loading...</div>
    }

    console.log(`Game: ${JSON.stringify(game, null, 2)}`);

    return (
        <div>
            Game page!
        </div>
    );
}

export async function getStaticPaths() {
    const SSR = withSSRContext();
    
    const { data } = await SSR.API.graphql({
        query: queries.gamesByDate,
        variables: {
            type: "Game",
            gameDate: { le: "2021-09-15" },
            limit: 1000
        }
    });
    const paths = data.gamesByDate.items.map(game => ({
        params: { id: game.id }
    }))

    return {
        fallback: true,
        paths
    }
}

export async function getStaticProps(context) {
    const SSR = withSSRContext();
    const { id } = context.params;
    const { data } = await SSR.API.graphql({
        query: queries.getGame,
        variables: { id }
    });

    return {
        props: {
            game: data.getGame
        }
    }
}

export default Game;

/pages/_app.js

import Amplify from 'aws-amplify';
import config from '../src/aws-exports';
import { UserProvider } from '../src/context/UserContext/context';

import Navbar from '../src/components/UI/Navbar';

Amplify.configure({
  ...config,
  ssr: true
});

Amplify.Logger.LOG_LEVEL = 'DEBUG';

function MyApp({ Component, pageProps }) {

  return (
    <UserProvider>
      <Navbar />
      <Component {...pageProps} />
      <GlobalStyle />
    </UserProvider>
  );
}

export default MyApp

It's also worth noting I've tried the following approaches in my _app.js file to configure Amplify Auth:

{ ... }
import Amplify, { withSSRContext } from 'aws-amplify'
{ ... }

const { Auth } = withSSRContext()
Auth.configure({
    region: config.aws_project_region,
    userPoolId: config.aws_user_pools_id,
    userPoolWebClientId: config.aws_user_pools_web_client_id,
    mandatorySignIn: false
});
{ ... }
import Amplify, { Auth } from 'aws-amplify'
{ ... }

Auth.configure({
    region: config.aws_project_region,
    userPoolId: config.aws_user_pools_id,
    userPoolWebClientId: config.aws_user_pools_web_client_id,
    mandatorySignIn: false
});

When I observe the Logger in my console, despite how I configure (or don't configure) the Auth module I get the same error:

error - Error: No current user
    at GraphQLAPIClass.<anonymous> (<PROJECT_PATH>/node_modules/@aws-amplify/api-graphql/lib/GraphQLAPI.js:244:31)
    at step (<PROJECT_PATH>/node_modules/@aws-amplify/api-graphql/lib/GraphQLAPI.js:44:23)
    at Object.throw (<PROJECT_PATH>/node_modules/@aws-amplify/api-graphql/lib/GraphQLAPI.js:25:53)
    at rejected (<PROJECT_PATH>/node_modules/@aws-amplify/api-graphql/lib/GraphQLAPI.js:17:65)
    at processTicksAndRejections (internal/process/task_queues.js:94:5) {
  type: 'Error',
  page: '/games/[id]'
}

I've also listed my Logger results relating to Auth -- let me know if you need additional detail from these logs, happy to provide.
Logger Results:

[DEBUG] 29:57.995 AuthClass - configure Auth
[DEBUG] 29:57.995 Parser - parse config [
  {
    region: '<REGION>',
    userPoolId: '<REGION>XXXXX',
    userPoolWebClientId: 'XXXXX',
    mandatorySignIn: false
  },
  'to amplifyconfig',
  {
    Analytics: {},
    Auth: {},
    Storage: {
      region: '<REGION>',
      userPoolId: '<REGION>XXXX',
      userPoolWebClientId: 'XXXX',
      mandatorySignIn: false
    },
    Logging: {}
  }
]
[DEBUG] 29:57.997 Hub - Dispatching to auth with  {
  event: 'configured',
  data: null,
  message: 'The Auth category has been configured successfully'
}
[DEBUG] 29:57.997 Hub - Dispatching to auth with  {
  event: 'configured',
  data: null,
  message: 'The Auth category has been configured successfully'
}
[DEBUG] 29:57.998 AnalyticsClass - on hub capsule auth {
  event: 'configured',
  data: null,
  message: 'The Auth category has been configured successfully'
}
[DEBUG] 29:58.2 Amplify - amplify config {
  aws_project_region: '<REGION>',
  aws_cognito_region: '<REGION>',
  aws_user_pools_id: '<REGION>XXXX',
  aws_user_pools_web_client_id: 'XXXX',
  oauth: {},
  aws_cloud_logic_custom: [
    {
      name: 'AdminQueries',
      endpoint: 'https://XXXX.execute-api.<REGION>.amazonaws.com/dev',
      region: '<REGION>'
    }
  ],
  aws_appsync_graphqlEndpoint: 'https://XXXX.appsync-api.<REGION>.amazonaws.com/graphql',
  aws_appsync_region: '<REGION>',
  aws_appsync_authenticationType: 'AMAZON_COGNITO_USER_POOLS',
  aws_appsync_apiKey: 'XXXX',
  ssr: true
}

It appears that the Auth category is being successfully configured, but for whatever reason, I'm unable to persist the user session from the previous page in order to query my API. I have my user object also stored in a context, but I'm unsure how calling this server-side might be done.

The same issue was occurring on a Dashboard page that shows all games. Fortunately, I was able to move the API call to the client-side via useEffect for the time being, but it would be great for some specific examples of successfully retrieving the user and then calling an API operation.

Outside of using getStaticPaths and getStaticProps, is there a way for me to render these individual pages at build time to avoid loading issues?

Please do let me know if I'm missing something in my implementation.

Thanks!

@koenoe
Copy link

koenoe commented Oct 13, 2021

Still relevant for me, I'm on 11.1.2 with webpack 5

@413n

This comment was marked as off-topic.

@bouthouri

This comment was marked as off-topic.

@ijjk
Copy link
Member

ijjk commented Feb 24, 2022

Hi, this appears to be working consistently in the latest version of Next.js v12.1.0, please update and give it a try!

Screen.Recording.2022-02-24.at.11.42.01.AM.mp4

@ijjk ijjk closed this as completed Feb 24, 2022
@github-actions
Copy link
Contributor

This closed issue has been automatically locked because it had no new activity for a month. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 27, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests