#✅ - Gen 2 data client object doesn't properly load

12 messages · Page 1 of 1 (latest)

hollow gorge
#

I'm following the documentation to read data from the gen 2 amplify resource file, and it works fine locally, but when it's actually deployed I run into seemingly random problems where the client object doesn't have any models loaded.

I've included example code:

let client = generateClient<Schema>();

const fetchUser = async ({
  email,
  serverClient,
}: FetchUserProps): Promise<User | null> => {
  try {
    client = serverClient || client;

    const usersData = await client.models.User.list({
      filter: { email: { eq: email } },
      selectionSet: [
        "id",
        "email",
        ...

Typescript confirms all my types are correct, and it works fine in a dev environment, but when hosted by Amplify I get an error when calling this list function. Some debugging indicates that the client object simply has no models within it at the time this is called.

I don't understand this system deeply enough to figure out the problem, could someone help me with some additional context?

restive badger
#

Hi @hollow gorge can you share a little more about where you're calling generateClient<Schema>()?

Are you following a pattern from our documentation? For example, we recommend generating the client and exporting it from a separate utility file

#

Also, if you have a sample repo that I can test locally and try to deploy to Amplify Hosting or maybe another hosting provider you're using, please share!

hollow gorge
# restive badger Hi <@376916386120990741> can you share a little more about where you're calling ...

Thanks for getting back to me, I have seen an alternative method with the util file, I put an example of what I'm trying below. However, when I try to import this cookieBasedClient and use it in the fetchUser function, I get this error:

"You're importing a component that needs next/headers. That only works in a Server Component which is not supported in the pages/ directory. Read more: https://nextjs.org/docs/getting-started/"

I'm not even using the pages directory, I'm using nextJS 14 with the app router. The fetchUser function is not marked with "use client".

Are there any examples you know of with a fullstack nextJS 14 app using amplify gen2? That would be super helpful to see the patterns in use for a beginner like me.

(./utils/amplifyUtils.ts)

import { createServerRunner } from "@aws-amplify/adapter-nextjs";
import config from "@/amplifyconfiguration.json";
import { cookies } from "next/headers";
import { getCurrentUser, fetchUserAttributes } from "aws-amplify/auth/server";
import { generateServerClientUsingCookies } from "@aws-amplify/adapter-nextjs/api";
import { Schema } from "@/amplify/data/resource";

export const cookieBasedClient = generateServerClientUsingCookies<Schema>({
  config,
  cookies,
});

restive badger
#

where did you create the utils folder?

#

is it in the root directory of the project?

#

cookies can only be accessed on the server side. So, if you are importing and/or using the cookieBasedClient in any component on the client-side, perhaps in a useEffect or component that is nested within a client component, that might explain the error from Next.js.

hollow gorge
#

Thanks, and yes the utils is in the root directory.

The data fetch functions are being called from within client components, so I guess that's a no-go. Is that a bad design pattern? Or can I simply define the generateClient<Schema> client once in the utils file and reuse it?

As an example, the following function is within a react context on the client side that holds user information. It gets called when the amplify hub sees a newly signed-in user, or when anyone tried to enter the page to see if they're already authenticated. Is using the fetchUser function here inappropriate?

  async function getCurrentUser(): Promise<User> {
    try {
      const userAttributes = await fetchUserAttributes();

      const existingUser = await fetchUser({ email: userAttributes.email! });

      if (existingUser) {
        log("User exists");
        return existingUser;
      } else {
        const newUser = await createUser({
          email: userAttributes.email!,
          firstName: userAttributes.given_name!,
          lastName: userAttributes.family_name!,
        });

        log(`New user created`);

        return newUser;
      }
    } catch (err) {
      logError(err);

      handleSignOut();
      throw new Error("Failed to fetch or create user");
    }
  }
restive badger
#

The data fetch functions are being called from within client components, so I guess that's a no-go. Is that a bad design pattern? Or can I simply define the generateClient<Schema> client once in the utils file and reuse it?

Yeah, you need different clients for the client and server. You can use the cookieBasedClient in server components and you can create and use a regular client (ex. const client = generateClient<Schema>()) in client components.

#

As an example, the following function is within a react context on the client side that holds user information. It gets called when the amplify hub sees a newly signed-in user, or when anyone tried to enter the page to see if they're already authenticated. Is using the fetchUser function here inappropriate?

I think this is okay. I see fetchUser would use the regular client since serverClient is not being passed in the argument so this should work on the client side

full riverBOT
#

✅ - Gen 2 data client object doesn't properly load