Idiomatic options with possibly undefined values

Should be a simple one, I've tried looking around on the forum and the internet.

What's the idiomatic was of handling this? I pass an array of ids, objects to create or {type: "none"}, I want to return a list of both the existing items and the created items and I don't want to have to manually filter in the caller.

export const upsertNormalizedTags = internalMutation({
  args: {
    tags: v.array(
      v.union(
        v.object({
          type: v.literal("none"),
        }),
        v.object({
          type: v.literal("existing"),
          existingId: v.id("normalized_tags"),
        }),
        v.object({
          type: v.literal("new"),
          newTag: v.object({
            name: v.string(),
            category: v.optional(tagCategoryValidator),
          }),
        }),
      ),
    ),
  },
  handler: (ctx, args) =>
    // ^ I want this to return only the created items and filter out any empty values
    Effect.forEach(args.tags, (tag) =>
      Effect.gen(function* () {
        if (tag.type === "existing") {
          // will return error if the tag does not exist
          return yield* ctx.db.get(tag.existingId).pipe(Effect.orDie);
        }

        // will return the new tag ids
        if (tag.type === "new") {
          return yield* ctx.db
            .insert("normalized_tags", {
              name: tag.newTag.name,
              normalizedName: tag.newTag.name.toLowerCase().trim(),
              category: tag.newTag.category,
            })
            .pipe(Effect.option);
        }

        // For demonstration purposes
        return Option.none();
      }),
    ),
});


Notes:

- This is Convex DB there's no need bulk insert, it's guaranteed to be transactional. It's weirdly cool.
- This uses @maple/convex-effect which allows me to use effects as mutation/query handlers.
Was this page helpful?