Onyx Database Relationships

One-to-Many Relationship Tutorial

Build one-to-many relationships in both the open source ORM and the cloud-native resolver experience. Toggle between versions to see how to save a Sailboat and its CrewMember roster.

Select Onyx Open Source or Onyx Cloud to tailor the instructions to your environment.

Onyx Open Source Tutorial

Switch to Onyx Open Source to review the annotation-driven implementation and sample code.

Onyx Cloud Tutorial

Model the one-to-many relationship with resolver attributes and cascade saves using graph paths so each crew member references the correct sailboat.

1

Capture resolver schema changes

Define resolver-backed attributes for crew and sailboat in your schema JSON. Each resolver performs a filtered query to retrieve related records.
1{
2  "tables": [
3    {
4      "name": "Sailboat",
5      "identifier": {
6        "name": "registrationCode",
7        "type": "String",
8        "generator": "None"
9      },
10      "partition": "",
11      "attributes": [
12        { "name": "registrationCode", "type": "String" },
13        { "name": "name", "type": "String" }
14      ],
15      "resolvers": [
16        {
17          "name": "crew",
18          "resolver": "return await db.from('CrewMember').where(eq('sailboatId', this.registrationCode)).list();"
19        }
20      ],
21      "indexes": []
22    },
23    {
24      "name": "CrewMember",
25      "identifier": {
26        "name": "id",
27        "type": "String",
28        "generator": "None"
29      },
30      "partition": "",
31      "attributes": [
32        { "name": "id", "type": "String" },
33        { "name": "firstName", "type": "String" },
34        { "name": "lastName", "type": "String" },
35        { "name": "sailboatId", "type": "String" }
36      ],
37      "resolvers": [
38        {
39          "name": "sailboat",
40          "resolver": "return await db.from('Sailboat').where(eq('registrationCode', this.sailboatId)).firstOrNull();"
41        }
42      ],
43      "indexes": []
44    }
45  ],
46  "revisionDescription": "Resolver-based one-to-many relationship"
47}
2

Shape the object graph

Create the sailboat payload and embed crew members with stable identifiers so subsequent saves update the same records.
1const sailboat = {
2  registrationCode: "NCC1701",
3  name: "Wind Passer",
4  crew: [
5    {
6      id: "crew_001",
7      firstName: "Martha",
8      lastName: "McFly"
9    },
10    {
11      id: "crew_002",
12      firstName: "Emmett",
13      lastName: "Brown"
14    }
15  ]
16};
3

Cascade the save

Use db.cascade() with crew:CrewMember(sailboatId, registrationCode) to ensure the SDK assignssailboatId for each crew member during persistence.
1await db
2  .cascade("crew:CrewMember(sailboatId, registrationCode)")
3  .save("Sailboat", sailboat);
4
5console.log("Saved " + sailboat.crew.length + " crew members with " + sailboat.name);
4

Resolve the crew

Query the sailboat and resolve the crew resolver to confirm the cascade save produced the expected child records.
1const savedSailboat = await db
2  .from("Sailboat")
3  .where(eq("registrationCode", "NCC1701"))
4  .resolve("crew")
5  .firstOrNull();
6
7console.log("Crew count: " + (savedSailboat?.crew?.length ?? 0));

Need Help?

If you have any questions or need assistance: