aws-amplify / amplify-js

A declarative JavaScript library for application development using cloud services.
https://docs.amplify.aws/lib/q/platform/js
Apache License 2.0
9.41k stars 2.11k forks source link

Nextjs AWS amplify gql returns null for nextToken even there is data in the DB table #10453

Closed Ajmaljalal closed 11 months ago

Ajmaljalal commented 1 year ago

Before opening, please confirm:

JavaScript Framework

Next.js

Amplify APIs

GraphQL API

Amplify Categories

api

Environment information

``` # Put output below this line System: OS: macOS 12.6 CPU: (10) arm64 Apple M1 Max Memory: 2.04 GB / 32.00 GB Shell: 5.8.1 - /bin/zsh Binaries: Node: 18.0.0 - /opt/homebrew/bin/node npm: 8.15.1 - ~/programing-exercises/assistian/node_modules/.bin/npm Browsers: Chrome: 105.0.5195.125 Safari: 16.0 npmPackages: @ampproject/toolbox-optimizer: undefined () @babel/core: ^7.18.2 => undefined (7.18.9, ) @babel/runtime: 7.15.4 @chakra-ui/react: ^2.2.3 => 2.2.4 @dnd-kit/core: ^6.0.5 => 6.0.5 @edge-runtime/primitives: 1.1.0-beta.31 @emotion/react: ^11.9.0 => 11.9.3 @emotion/styled: ^11.8.1 => 11.9.3 @hapi/accept: undefined () @napi-rs/triples: undefined () @next/react-dev-overlay: undefined () @reduxjs/toolkit: ^1.8.2 => 1.8.3 @reduxjs/toolkit-query: 1.0.0 @reduxjs/toolkit-query-react: 1.0.0 @segment/ajv-human-errors: undefined () @svgr/webpack: ^6.2.1 => 6.3.1 @types/draft-js: ^0.11.9 => 0.11.9 @types/node: ^17.0.30 => 17.0.45 @types/react: ^18.0.8 => 18.0.15 @types/react-draft-wysiwyg: ^1.13.4 => 1.13.4 @vercel/nft: undefined () acorn: undefined () amphtml-validator: undefined () arg: undefined () assert: undefined () async-retry: undefined () async-sema: undefined () aws-amplify: ^4.3.29 => 4.3.29 babel-loader: ^8.2.5 => 8.2.5 babel-packages: undefined () browserify-zlib: undefined () browserslist: undefined () buffer: undefined () bytes: undefined () chalk: undefined () ci-info: undefined () cli-select: undefined () comment-json: undefined () compression: undefined () conf: undefined () constants-browserify: undefined () content-disposition: undefined () content-type: undefined () cookie: undefined () cross-spawn: undefined () crypto-browserify: undefined () cssnano-simple: undefined () debug: undefined () devalue: undefined () domain-browser: undefined () edge-runtime: undefined () eslint: 8.14.0 => 8.14.0 eslint-config-next: 12.1.5 => 12.1.5 eslint-config-prettier: ^8.5.0 => 8.5.0 eslint-plugin-storybook: ^0.5.12 => 0.5.13 events: undefined () find-cache-dir: undefined () find-up: undefined () framer-motion: ^6.3.11 => 6.5.1 fresh: undefined () get-orientation: undefined () glob: undefined () gzip-size: undefined () http-proxy: undefined () https-browserify: undefined () i: ^0.3.7 => 0.3.7 icss-utils: undefined () ignore-loader: undefined () image-size: undefined () immutable: ^4.1.0 => 4.1.0 (3.7.6) is-animated: undefined () is-docker: undefined () is-wsl: undefined () jest-worker: undefined () json5: undefined () jsonwebtoken: undefined () loader-utils: undefined () lodash.curry: undefined () lru-cache: undefined () micromatch: undefined () mini-css-extract-plugin: undefined () nanoid: undefined () native-url: undefined () neo-async: undefined () next: ^12.3.0 => 12.3.0 node-fetch: undefined () node-html-parser: undefined () npm: ^8.12.1 => 8.15.1 nprogress: ^0.2.0 => 0.2.0 ora: undefined () os-browserify: undefined () p-limit: undefined () path-browserify: undefined () postcss-flexbugs-fixes: undefined () postcss-modules-extract-imports: undefined () postcss-modules-local-by-default: undefined () postcss-modules-scope: undefined () postcss-modules-values: undefined () postcss-preset-env: undefined () postcss-safe-parser: undefined () postcss-scss: undefined () postcss-value-parser: undefined () process: undefined () punycode: undefined () querystring-es3: undefined () raw-body: undefined () react: 18.1.0 => 18.1.0 (18.0.0) react-dom: 18.1.0 => 18.1.0 react-is: 17.0.2 react-redux: ^8.0.2 => 8.0.2 react-refresh: 0.12.0 react-server-dom-webpack: undefined () regenerator-runtime: 0.13.4 sass-loader: undefined () schema-utils: undefined () semver: undefined () send: undefined () setimmediate: undefined () source-map: undefined () stream-browserify: undefined () stream-http: undefined () string-hash: undefined () string_decoder: undefined () strip-ansi: undefined () tar: undefined () terser: undefined () text-table: undefined () timers-browserify: undefined () tty-browserify: undefined () typescript: ^4.7.2 => 4.7.4 ua-parser-js: undefined () unistore: undefined () util: undefined () vm-browserify: undefined () watchpack: undefined () web-vitals: undefined () webpack: undefined () webpack-sources: undefined () ws: undefined () npmGlobalPackages: git-change-date: 1.0.2 npm: 8.8.0 ```

Describe the bug

I am trying to get paginated data, the first time api call returns nextToken string but for consequent request, the nextToken is null even I have data in the DB table.

Expected behavior

it should return nextToken till there is no data remaining

Reproduction steps

  1. add the schemas
  2. push to amplify
  3. query works from the aws console and returns nextToken all the time
  4. from nextjs app it does not return the nextToken after the first call.

Code Snippet

// Put your code below this line.

type FeedPost @model @auth(rules: [
    { allow: public, operations: [read]},
    { allow: owner, ownerField:"ownerId",  operations: [create, update, delete, read]}
  ])
{
  id: ID
  ownerId: ID! @auth(rules: [
    { allow: public, operations: [read]},
    { allow: owner, ownerField:"ownerId", operations: [create, update, read, delete] }
    ])
  title: String!
  text: String!
  likes: [Like] @hasMany(indexName: "byPost", fields:["id"])
  comments: [FeedPostComment] @hasMany(indexName: "byPost", fields: ["id"])
  user: User @hasOne(fields: ["ownerId"])
  type: String! @index(name: "feedPostsByDate", queryField: "feedPostsByDate", sortKeyFields: ["createdAt"])
  createdAt: String
}

type FeedPostComment @model @auth(rules: [
    { allow: public, operations: [read]},
    { allow: owner,  ownerField:"ownerId", operations: [create, update, delete, read]}
  ])
{
  ownerId: ID! @auth(rules: [
    { allow: public, operations: [read]},
    { allow: owner, ownerField:"ownerId", operations: [create, update, read, delete] }
    ])
  text: String!
  user: User @hasOne(fields: ["ownerId"])
  postId: ID! @index(name: "byPost")
}

type Like @model @auth(rules: [
    { allow: public, operations: [read]},
    { allow: owner,  ownerField:"ownerId", operations: [create, update, delete, read]}
  ])
{
  ownerId: ID! @auth(rules: [
    { allow: public, operations: [read]},
    { allow: owner, ownerField:"ownerId", operations: [create, update, read, delete] }
    ])
  ownerName: String!
  postId: ID! @index(name: "byPost")
}

export const feedPostsByDate = /* GraphQL */ `
  query FeedPostsByDate(
    $type: String!
    $createdAt: ModelStringKeyConditionInput
    $sortDirection: ModelSortDirection
    $filter: ModelFeedPostFilterInput
    $limit: Int
    $nextToken: String
  ) {
    feedPostsByDate(
      type: $type
      createdAt: $createdAt
      sortDirection: $sortDirection
      filter: $filter
      limit: $limit
      nextToken: $nextToken
    ) {
      items {
        id
        ownerId
        title
        text
      }
      nextToken
    }
  }
`;

export const getAllFeedPosts = async (req?: any, nextToken?: string): Promise<any> => {
  const Api = req ? withSSRContext({ req }).API : API
  let variablesWithToken = {
    limit: 2,
    type: "FeedPost",
    sortDirection: "DESC",
    nextToken: nextToken
  }

// this is for the first time request
  let variablesWithoutToken = {
    limit: 4,
    type: "FeedPost",
    sortDirection: "DESC",
  }
  const vars = nextToken ? variablesWithToken : variablesWithoutToken
  try {
    const res: any = await Api.graphql({
      query: feedPostsByDate,
      variables: vars
    })
    return {
      posts: res.data?.feedPostsByDate.items,
      nextToken: res.data?.feedPostsByDate.nextToken
    }
  } catch (error) {
    return error
  }
}

  const fetchData = async () => {
    if (token) {
      const res = await getAllFeedPosts(null, token)
      const newPosts = [...allPosts, ...res.posts]
      const newToken = res.nextToken
      setToken(newToken)
      setPosts(newPosts)
    }
  }

Log output

``` // Put your logs below this line ```

aws-exports.js

const awsmobile = { "aws_project_region": "us-west-2", "aws_cognito_identity_pool_id": "us-west-2:6d33d615-44df-4678-a56f-146e1deef0aa", "aws_cognito_region": "us-west-2", "aws_user_pools_id": "us-west-2_YT3xMoe9b", "aws_user_pools_web_client_id": "3ek3fqe40m4qhufugdj7fo6f7n", "oauth": {}, "aws_cognito_username_attributes": [ "EMAIL" ], "aws_cognito_social_providers": [], "aws_cognito_signup_attributes": [ "EMAIL" ], "aws_cognito_mfa_configuration": "OFF", "aws_cognito_mfa_types": [ "SMS" ], "aws_cognito_password_protection_settings": { "passwordPolicyMinLength": 8, "passwordPolicyCharacters": [] }, "aws_cognito_verification_mechanisms": [ "EMAIL" ], "aws_appsync_graphqlEndpoint": "https://pu4w6grmsndcpcgorqzwarnx3m.appsync-api.us-west-2.amazonaws.com/graphql", "aws_appsync_region": "us-west-2", "aws_appsync_authenticationType": "API_KEY", "aws_appsync_apiKey": "da2-fkepuqnoajenplhkj4dhqfup6e" };

Manual configuration

No response

Additional configuration

No response

Mobile Device

No response

Mobile Operating System

No response

Mobile Browser

No response

Mobile Browser Version

No response

Additional information and screenshots

No response

chrisbonifacio commented 1 year ago

Hi @Ajmaljalal 👋 thanks for raising this issue. Can you share the outgoing request from the Next.js app (from the Network activity tab) and the one from the AppSync console for us to compare?

Is there any difference between calling APi.graphql on the client side vs the server?

Ajmaljalal commented 1 year ago

@chrisbonifacio here are the outgoing request: from Next.js app network tab

query FeedPostsByDate($type: String!, $createdAt: ModelStringKeyConditionInput, $sortDirection: ModelSortDirection, $filter: ModelFeedPostFilterInput, $limit: Int, $nextToken: String)
variables: {limit: 2, type: "FeedPost", sortDirection: "DESC",}
limit: 2
nextToken: "eyJ2ZXJzaW9..."
sortDirection: "DESC"
type: "FeedPost"

and here is the query from aws console appsync query MyQuery { feedPostsByDate(type: "FeedPost", limit: 2, sortDirection: DESC nextToken: "eyJ2ZXJzaW9...") { nextToken items { title } } }

and there is no difference between calling Api.graphql on the client side vs the server side. However the first call is happening on the server via getStaticProps and the nextToken from there is returned as a prop and then used from the client for future request.

svidgen commented 1 year ago

I want to make sure I understand the flow and issue:

  1. SSR request returns data and nextToken.
  2. data and nextToken are passed to client
  3. Client queries with nextToken
  4. Client receives data (populated with data) and nextToken (null, even though more data is expected)

Is that correct?

When you've run this in console, did you use the same auth mode and user that the client is using?

cwomack commented 1 year ago

@Ajmaljalal, are you still experiencing this issue? If so, can you respond to the questions above regarding your flow and auth mode?

cwomack commented 11 months ago

Closing this issue as we have not heard back from you. If you are still experiencing this, please feel free to reply back and provide any information previously requested and we'd be happy to re-open the issue.

Thank you!