How Can I Fix Fauna's Projection Error When Handling Nullable Fields?

Fauna is stating that the type User don’t comply with my projection:

Fauna says:

Type `User` is not a subtype of `.{ { id: A, ttl: B, ts: C, firstName: D, lastName: E, primaryEmail: F, emails: G, emailVerification: H, avatar: I, activeOrganization: .{ { id: J, coll: K, ts: L, name: M, slug: N, members: O, plan: P, ... } => { id: J, coll: K, ts: L, name: M, slug: N, members: O, plan: P } } => Q, organizations: .{ { id: R, coll: S, ts: T, name: U, slug: V, members: W, plan: X, ... } => { id: R, coll: S, ts: T, name: U, slug: V, members: W, plan: X } } => Y, accounts: Z, ... } => { id: A, ttl: B, ts: C, firstName: D, lastName: E, primaryEmail: F, emails: G, emailVerification: H, avatar: I, activeOrganization: Q, organizations: Y, accounts: Z } } => Any`

I have the following Schema:

collection User {
    firstName: String
    lastName: String
    primaryEmail: String
    emails: Array<String>
    compute emailVerification: String? = doc => { getVerificationEmail(doc.id)}
    avatar: String?
    activeOrganization: Ref<Organization>?
    organizations: Array<Ref<Organization>>?
    accounts: Array<Ref<Account>>
}
collection Organization {
    name: String
    logo: String?
    slug: String
    members: Array<{
        user: Ref<User>
        role: "role_organization_member" | "role_organization_admin" | "role_organization_owner"
    }>
    plan: "Free" | "Pro" | "Enterprise"
}

and the following function (simplified)

@role(server)
function createOrganization ( data: { name: String, logo: String?, slug: String } ): User {
  let user: Any = Query.identity()
  let user: User = user

  [...]

user {
    id,
    ttl,
    ts,
    firstName,
    lastName,
    primaryEmail,
    emails,
    emailVerification,
    avatar,
    activeOrganization {
      id,
      coll,
      ts,
      name,
      logo,
      slug,
      members,
      plan
    },
    organizations {
      id,
      coll,
      ts,
      name,
      logo,
      slug,
      members,
      plan
    },
    accounts
  }
}

As soon as I add the projection of activeOrganization and organization to the function, the Fauna CLI starts complaining. At this moment it is also telling me that I need to handle null cases like this as part of the projection:

cause: Type `Null` does not have field `ts`
at src/lib/db/schema/fsl/functions/organization/createOrganization.fsl:44:7
   |
44 |       ts,
   |       ^^
   |
hint: Use the ! or ?. operator to handle the null case
at src/lib/db/schema/fsl/functions/organization/createOrganization.fsl:44:7
   |
44 |       !ts,
   |       +
   |

but following this suggestion is only leading to an invalid function definition.

You should be able to use projection on nullable types, but those errors are a good first clue to track down. I wonder if there is something about the types specifically within the UDF. If you run the body of the UDF directly, does that work okay?

As a workaround, does it work to use .map() instead of the projection? Projection on arrays and sets is effectively sugar for .map(), but maybe the typer will resolve it differently. Something like

    activeOrganization: .activeOrganization?.map(ao => ao {
      id,
      coll,
      ts,
      name,
      logo,
      slug,
      members,
      plan
    }),

Thx Paul, your hint was leading to the solution:

This is satisfying the type validation:

user {
    id,
    ttl,
    ts,
    firstName,
    lastName,
    primaryEmail,
    emails,
    emailVerification,
    avatar,
    activeOrganization: .activeOrganization {
      id,
      ttl,
      ts,
      name,
      logo,
      slug,
      members,
      plan
    },
    organizations: .organizations?.map(ao => ao {
      id,
      ttl,
      ts,
      name,
      logo,
      slug,
      members,
      plan
    }),
    accounts: .accounts.map(a => a {
      id,
      ttl,
      ts,
      user,
      socialProvider,
      passkey
    })
  }

This is not satisfying the CLI type validation:

user {
    id,
    ttl,
    ts,
    firstName,
    lastName,
    primaryEmail,
    emails,
    emailVerification,
    avatar,
    activeOrganization {
      id,
      ttl,
      ts,
      name,
      logo,
      slug,
      members,
      plan
    },
    organizations {
      id,
      ttl,
      ts,
      name,
      logo,
      slug,
      members,
      plan
    },
    accounts {
      id,
      ttl,
      ts,
      user,
      socialProvider,
      passkey
    }
  }

So for now I have my workaround, but overall I would prefer that the second approach also works.

1 Like

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.