I want to eagerly load a related aws-amplify gen 2 model and pass it on, but it seems to cause a typescript error no matter what I try. How do I do this without causing a typescript error? For background, I've done JavaScript, Python and dabbled in others but I'm new to TypeScript and Amplify. I'm using the web/ChatGPT to figure it out but neither seemed to help with this question.
The Error
Types of property 'dataType' are incompatible.
Type '{ name: string; isComplex: boolean; dataCategories: LazyLoader<{ name: string; addDefault: boolean; dataEntries: LazyLoader<{ category: LazyLoader<... | null, false>; dataCategoryId: string; ... 7 more ...; readonly updatedAt: string; } | null, true>; ... 9 more ...; readonly updatedAt: string; } | null, true>; note...' is not assignable to type 'LazyLoader<{ name: string; isComplex: boolean; dataCategories: LazyLoader<{ name: string; addDefault: boolean; dataEntries: LazyLoader<{ category: LazyLoader<... | null, false>; dataCategoryId: string; ... 7 more ...; readonly updatedAt: string; } | null, true>; ... 9 more ...; readonly updatedAt: string; } | null, ...'.
Type 'undefined' is not assignable to type 'LazyLoader<{ name: string; isComplex: boolean; dataCategories: LazyLoader<{ name: string; addDefault: boolean; dataEntries: LazyLoader<{ category: LazyLoader<... | null, false>; dataCategoryId: string; ... 7 more ...; readonly updatedAt: string; } | null, true>; ... 9 more ...; readonly updatedAt: string; } | null, ...'.
The Set Up
api.ts
/**
* Subscribe to real-time updates for data categories, including their data types.
* @param {Function} callback - Function to update state with new data.
* @returns {Function} Unsubscribe function.
*/
export function subscribeToDataCategories(
callback: (items: Schema["DataCategory"]["type"][]) => void
): () => void {
const sub = client.models.DataCategory.observeQuery().subscribe({
next: async (result: { items?: Schema["DataCategory"]["type"][] }) => {
console.log("Updating DataCategories:", result.items);
if (!result.items) {
callback([]);
return;
}
const enrichedItems = await Promise.all(
result.items.map(async (item) => {
try {
let dataType: Schema["DataType"]["type"] | undefined;
if (item.dataType && typeof item.dataType === "function") {
// Resolve LazyLoader
const resolved = await item.dataType();
dataType = resolved?.data ?? undefined;
}
return { ...item, dataType };
} catch (error) {
console.error(
`Failed to fetch DataType for ID ${item.dataTypeId}:`,
error
);
return { ...item };
}
})
);
console.log("Enriched Categories:", enrichedItems);
callback(enrichedItems);
},
error: (error: unknown) => {
console.error("Subscription error:", error);
},
});
return () => sub.unsubscribe(); // Cleanup function
}
resources.ts
Here is my resources.ts file
import { type ClientSchema, a, defineData } from "@aws-amplify/backend";
import { postConfirmation } from "../auth/post-confirmation/resource";
const schema = a
.schema({
UserProfile: a
.model({
email: a.string().required(),
profileOwner: a.string(),
})
.secondaryIndexes((index) => [index("email")])
.authorization((allow) => [
allow.owner(),
allow.ownerDefinedIn("profileOwner"),
allow.groups(["Admins"]).to(["read"]),
]),
DataType: a
.model({
name: a.string().required(),
note: a.string(),
isComplex: a.boolean().required(),
dataCategories: a.hasMany("DataCategory", "dataTypeId"),
})
.secondaryIndexes((index) => [index("name")])
.authorization((allow) => [allow.authenticated(), allow.publicApiKey()]),
DataCategory: a
.model({
name: a.string().required(),
note: a.string(),
addDefault: a.boolean().required(),
defaultValue: a.string(),
options: a.string().array(), // For future use with options of values
dataEntries: a.hasMany("DataEntry", "dataCategoryId"),
dataTypeId: a.id().required(), // ✅ Explicitly define the reference field
dataType: a.belongsTo("DataType", "dataTypeId"),
entryCount: a.integer().default(0),
})
.secondaryIndexes((index) => [index("name")])
.authorization((allow) => [
allow.owner(),
allow.groups(["Admins"]).to(["read"]),
allow.publicApiKey(), // TODO: Remove. FOR TESTING
]),
DataEntry: a
.model({
note: a.string(),
category: a.belongsTo("DataCategory", "dataCategoryId"),
dataCategoryId: a.id().required(),
date: a.date().required(),
value: a.string().required(),
dummy: a.integer().default(0),
})
.secondaryIndexes((index) => [
index("dataCategoryId")
.name("categoryEntriesByDate")
.queryField("listCategoryEntries")
.sortKeys(["date"]),
index("dummy")
.name("entriesByDate")
.queryField("listByDate")
.sortKeys(["date"]),
])
// client.models.DataEntry.listDataentryByDataCategoryId({dataCategoryId: "ID"})
.authorization((allow) => [
allow.owner(),
allow.groups(["Admins"]).to(["read"]),
allow.publicApiKey(), // TODO: Remove. FOR TESTING
]),
})
.authorization((allow) => [allow.resource(postConfirmation)]);
export type Schema = ClientSchema<typeof schema>;
// export const schema = schema;
export { schema };
export const data = defineData({
schema,
authorizationModes: {
defaultAuthorizationMode: "userPool", // Changed from public api key. /react/build-a-backend/data/customize-authz/
apiKeyAuthorizationMode: {
expiresInDays: 30,
},
},
});
Attempts
Attempt 1
This time I tried partially custom types, still errored.
type ResolvedDataType = Omit<Schema["DataType"]["type"], "dataCategories"> & {
dataCategories?: Schema["DataCategory"]["type"][];
};
type EnrichedDataCategory = Omit<Schema["DataCategory"]["type"], "dataType"> & {
dataType?: ResolvedDataType;
};
export function subscribeToDataCategories(
callback: (items: EnrichedDataCategory[]) => void
): () => void {
Attempt 2
Here I tried just adding DataType to the standard DataCategory type but it also errored.
export function subscribeToDataCategories(
callback: (
items: (Schema["DataCategory"]["type"] & {
dataType?: Schema["DataType"]["type"];
})[]
) => void
): () => void {