amplify-js: Unable to query Many-to-Many Relationship in Datastore. Keep getting Invalid field modelID, model ModelName.

Before opening, please confirm:

JavaScript Framework

React

Amplify APIs

DataStore

Amplify Categories

api

Environment information

# Put output below this line
  System:
    OS: Windows 10 10.0.19042
    CPU: (4) x64 Intel(R) Core(TM) i5-4460  CPU @ 3.20GHz       
    Memory: 1.71 GB / 7.94 GB
  Binaries:
    Node: 12.18.2 - C:\Program Files\nodejs\node.EXE
    Yarn: 1.22.4 - C:\Program Files (x86)\Yarn\bin\yarn.CMD     
    npm: 6.14.5 - C:\Program Files\nodejs\npm.CMD
  Browsers:
    Chrome: 89.0.4389.128
    Edge: Spartan (44.19041.906.0), Chromium (90.0.818.42)      
    Internet Explorer: 11.0.19041.1
  npmPackages:
    @chakra-ui/react: ^1.4.1 => 1.4.1 
    @emotion/react: 11 => 11.1.5 
    @emotion/styled: 11 => 11.1.5 
    @reach/router: ^1.3.4 => 1.3.4 
    @typescript-eslint/eslint-plugin: ^4.21.0 => 4.21.0 (4.19.0)
    @typescript-eslint/parser: ^4.21.0 => 4.21.0 (4.19.0)       
    aws-amplify: ^3.3.26 => 3.3.26 
    bundle-optimisations:  1.0.0 
    dev-404-page:  1.0.0 
    eslint: ^7.23.0 => 7.23.0 (7.22.0)
    eslint-plugin-react: ^7.23.1 => 7.23.1 
    formik: ^2.2.6 => 2.2.6 
    framer-motion: 4 => 4.0.3 
    gatsby: ^3.1.2 => 3.1.2 
    gatsby-env-variables: ^2.0.0 => 2.0.0 
    gatsby-image: ^3.1.0 => 3.1.0 
    gatsby-plugin-gatsby-cloud: ^2.1.0 => 2.1.0 
    gatsby-plugin-image: ^1.1.2 => 1.1.2
    gatsby-plugin-manifest: ^3.1.0 => 3.1.0
    gatsby-plugin-offline: ^4.1.0 => 4.1.0
    gatsby-plugin-react-helmet: ^4.1.0 => 4.1.0
    gatsby-plugin-sharp: ^3.1.2 => 3.1.2
    gatsby-source-filesystem: ^3.1.0 => 3.1.0
    gatsby-transformer-sharp: ^3.1.0 => 3.1.0
    internal-data-bridge:  1.0.0
    load-babel-config:  1.0.0
    mixpanel-browser: ^2.41.0 => 2.41.0
    nanoid: ^3.1.22 => 3.1.22
    prettier: 2.2.1 => 2.2.1
    prod-404:  1.0.0
    prop-types: ^15.7.2 => 15.7.2
    react: ^17.0.1 => 17.0.1
    react-dom: ^17.0.1 => 17.0.1
    react-dropzone: ^11.3.2 => 11.3.2
    react-dropzone-uploader: ^2.11.0 => 2.11.0
    react-helmet: ^6.1.0 => 6.1.0
    react-icons: ^4.2.0 => 4.2.0
    typescript: ^4.2.4 => 4.2.4
    webpack-theme-component-shadowing:  1.0.0
  npmGlobalPackages:
    @aws-amplify/cli: 4.47.1
    @react-native-community/cli: 4.10.1
    aws-cdk: 1.95.2
    cors-anywhere: 0.4.4
    cp-cli: 2.0.0
    eslint-config-airbnb-base: 14.2.0
    eslint-plugin-import: 2.22.0
    eslint: 7.4.0
    expo-cli: 3.22.3
    gatsby-cli: 2.19.1
    lerna: 3.22.1
    rimraf: 3.0.2

Describe the bug

I have 2 model with Many-to-Many Relationship. The 2 model is Order and Product. An Order will have many Product and Product will in many Order.

So I followed this Amplify guide to group the into OrderProducts , Order and Product (Code I state in Reproduction step)

But when I query the OrderProduct model like below, in order to get a List of Products by OrderID:

import {  Product, OrderProducts } from '../models';

export const GetAllProductIdByOrderId = async (order) => {
    return await DataStore.query(OrderProducts, op => op.orderID("eq", order.id)) // this is actual orderID
}

I get this error:

Error: Invalid field for model. field: orderID, model: OrderProducts
    at Object.get (index.js:61)
    at eval (dataSource.js:103)
    at Function.ModelPredicateCreator.createFromExisting (index.js:94)
    at DataStore.eval (datastore.js:582)
    at step (datastore.js:43)
    at Object.eval [as next] (datastore.js:24)
    at fulfilled (datastore.js:15)

Expected behavior

When I query OrderProducts with orderID,

 DataStore.query(OrderProducts, op => op.orderID("eq", order.id))

I should get an array of ProductID

Reproduction steps

  1. In Schema.graphql put this 3 models
type Order @model @key(name: "byStore", fields: ["storeID"]) @auth(rules: [{allow: private, operations: [read, update, create, delete]}]) {
  id: ID!
  buyer_name: String
  order_total_amount: String
  products: [OrderProducts] @connection(keyName: "byOrder", fields: ["id"])
  created_at: AWSTimestamp
}

type OrderProducts @model @key(name: "byOrder", fields:["orderID", "productID"]) @key(name: "byProduct", fields:["productID", "orderID"]) @auth(rules: [{allow: private, operations: [read, update, create, delete]}]){
  id: ID!
  orderID: ID!
  productID: ID!
  order: Order! @connection(fields: ["orderID"])
  product: Product! @connection(fields: ["productID"])
}

type Product @model @key(name: "byStore", fields: ["storeID"]) @auth(rules: [{allow: owner, operations: [create, update, delete]}, {allow: public, provider: iam, operations: [read]}]){
  id: ID!
  product_name: String!
  product_price: String!
  created_at: AWSTimestamp
  orders: [OrderProducts] @connection(keyName: "byProduct", fields:["id"])
}
  1. amplify push in CLI
  2. Save the Order and OrderProducts relationship like this:
export const CreateOrder = async (buyerPhoneNumber, buyerName, totalAmount, ) => {
    return await DataStore.save(
        new Order({
            "buyer_name": buyerName,
            "order_total_amount": totalAmount,
            "created_at": getCurrentTimestamp(),
            "products": []
        })
    );
}

export const CreateOrderProductsRelationship = async (orderId, productId, order, product) => {
    return await DataStore.save(
        new OrderProducts({
            "orderID": orderId,
            "productID": productId,
            "order": order,
            "product": product
        })
    );
}

// save a new Order by using this
/*1st function above */
 CreateOrder(buyerPhoneNumber, buyerName, totalAmount)
                    .then(orderModel => {
                        console.log(orderModel )

                       // here save the OrderProducts relationship when successfully make an Order
                        createProductOrderRelationShip(orderModel , productModel)
                    }).catch(error => { console.log(error); })

// then save a Order and Product relationship 
/*second function above*/
const createProductOrderRelationShip = (product, order) => {
 
        CreateOrderProductsRelationship(order.id, product.id, order, product)
            .then(res => {
                console.log(res)
         
            }).catch(error => {
                console.log(error)
                setOrderResult(false)
            })
    }

  1. Then I want to get all the Product with an OrderId, so I query like this:
export const GetAllProductIdByOrderId = async (order) => {
    return await DataStore.query(OrderProducts, op => op.orderID("eq", order.id))
}
  1. But I get this error
Error: Invalid field for model. field: orderID, model: OrderProducts

OrderProducts model have a field or orderID, I triple checked this in DynamoDB. But I can query the by orderID.

Code Snippet

// Put your code below this line.

Log output

// Put your logs below this line

Error: Invalid field for model. field: orderID, model: OrderProducts
    at Object.get (index.js:61)
    at eval (dataSource.js:103)
    at Function.ModelPredicateCreator.createFromExisting (index.js:94)
    at DataStore.eval (datastore.js:582)
    at step (datastore.js:43)
    at Object.eval [as next] (datastore.js:24)
    at fulfilled (datastore.js:15)

aws-exports.js

const awsmobile = {
    "aws_project_region": "ap-southeast-1",
    "aws_cognito_identity_pool_id": "ap-southeast-1:f729257a-e9c1-42dd-81e2-8e48ad2de450",
    "aws_cognito_region": "ap-southeast-1",
    "aws_user_pools_id": "ap-southeast-1_2dHHzRmON",
    "aws_user_pools_web_client_id": "c9j7hv6huv6vm7debfbtulfhq",
    "oauth": {},
    "aws_user_files_s3_bucket": "coollinksbucket164006-dev",
    "aws_user_files_s3_bucket_region": "ap-southeast-1",
    "aws_appsync_graphqlEndpoint": "https://ohobeyi2ffc77im3xegzj5w3ny.appsync-api.ap-southeast-1.amazonaws.com/graphql",
    "aws_appsync_region": "ap-southeast-1",
    "aws_appsync_authenticationType": "AMAZON_COGNITO_USER_POOLS"
};


export default awsmobile;

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

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 3
  • Comments: 18 (4 by maintainers)

Most upvoted comments

@iartemiev yup… you ok to closing this…

@iartemiev Your code WORKS!!! FINALLY IT WORKS!!! THANK YOU VERY VERY MUCH

I’d need to see the app code for the components that get rendered in your /item/product-id view to get a better idea.

If this is only happening the first time a user is navigating to this view, most likely DataStore hasn’t finished syncing data to the local store at the time you’re calling DataStore.query. All DataStore operations (e.g., save, query, observe, etc.) are performed against the local store, so upon initial visit, the store may be empty, and would therefore return [].

You can use the following pattern to eagerly start DataStore and then perform your initial query after the sync has completed:

import Amplify, {DataStore, Hub} from 'aws-amplify';


function App() {
  const [products, setProducts] = useState([]);

  useEffect(() => {
    DataStore.start();

    const listener = Hub.listen('datastore', async ({ payload: { event } }) => {
      if (event === 'ready') {
        getAllProducts();
        // initiate subscription with DataStore.observe here, if needed
      }
    });

    return () => {
      listener();
    };
  }, []);

  async function getAllProducts() {
    const records = await DataStore.query(Product);
    setProducts(records);
  }
}

Regarding auth, using multiple different authorization types (e.g., Cognito with IAM) was added in aws-amplify@3.4.0, so I’d make sure you’re using that or a later version. You’ll want to refer to this section of the docs: https://docs.amplify.aws/lib/datastore/setup-auth-rules/q/platform/js#configure-multiple-authorization-types

The “No current user” error could be happening because you’re attempting to use DataStore before you’ve signed in / gotten credentials for Cognito or IAM.

@raybotha end up you need to move the enum to the last card of your schema, like this

https://github.com/aws-amplify/amplify-adminui/issues/188#issuecomment-832110950 Not sure in local schema.graphql, but I tried in Admin UI, it worked

And I definitely agreed what you say, a lot of simple thing is still not working and no way to see why it didnt work, end up when it happen, it consume a lot of time for the simple thing