How to update a relation

I have a form that contains basically a customer with 1 or more addresses and 1 or more phone numbers. I can create and display everything just fine but I’m running into trouble with updates of the addresses and phone numbers. I currently have something like

export const updateCustomerWithRelations = ({ id, input }) => {
  return db.customer.update({
    data: {
      customer: input.customer:
      addresses: [{
        update: {
          data: { ... },
          where: { ... }
        }
      }],
      phoneNumbers: [{
        update: {
          data: { ... },
          where: { ... }
        }
      }]
    },
    where: { id },
  });
};

But I’m getting an error

Field "update" is not defined by type "UpdateAddressInput".
...
Field "update" is not defined by type "UpdatePhoneNumberInput"

Is it possible to do an update of multiple relations at the same time like this?

I think you are looking for connectOrCreate [ref: Relation queries (Concepts)]

export const updateCustomerWithRelations = ({ id, input }) => {
  return db.customer.update({
    data: {
      customer: input.customer:
      addresses: {
        connectOrCreate: {
          data: { ... },
          where: { ... }
        }
      },
      phoneNumbers: {
        connectOrCreate: {
          data: { ... },
          where: { ... }
        }
      }
    },
    where: { id },
  });
};

Sorry, I took some time away from this but I’ve come back to it and I came up with a solution. I’m not sure it’s the best solution, it feels a little brute forced but I’m going to put it here in case it would help someone else or maybe come up with a better solution.

export const updateCustomerWithRelations = async ({ id, input }) => {
  const customer = await db.customer.update({
    data: {
      name: input?.customer?.name,
      email: input?.customer?.email,
      notes: input?.customer?.notes,
      photo: input?.customer?.photo,
    },
    where: { id },
  });

  for (const address of input.addresses) {
    if (address.id) {
      await db.address.update({
        data: address,
        where: { id: address.id },
      });
    } else {
      await db.address.create({
        data: {
          customerId: customer.id,
          ...address,
        },
      });
    }
  }

  for (const phoneNumber of input.phoneNumbers) {
    if (phoneNumber.id) {
      await db.phoneNumber.update({
        data: phoneNumber,
        where: { id: phoneNumber.id },
      });
    } else {
      await db.phoneNumber.create({
        data: {
          customerId: customer.id,
          ...phoneNumber,
        },
      });
    }
  }

  const addresses = await db.address.findMany({
    where: { customerId: customer.id },
  });
  const phoneNumbers = await db.phoneNumber.findMany({
    where: { customerId: customer.id },
  });

  if (addresses.length > input?.addresses?.length) {
    const removeAddresses = addresses.filter(
      ({ id: id1 }) => !input?.addresses?.some(({ id: id2 }) => id1 === id2)
    );

    for (const removeAddress of removeAddresses) {
      await db.address.delete({
        where: { id: removeAddress.id },
      });
    }
  }

  if (phoneNumbers.length > input?.phoneNumbers?.length) {
    const removePhones = phoneNumbers.filter(
      ({ id: id1 }) => !input?.phoneNumbers?.some(({ id: id2 }) => id1 === id2)
    );

    for (const removeNumber of removePhones) {
      await db.phoneNumber.delete({
        where: { id: removeNumber.id },
      });
    }
  }

  return customer;
};

You need to be careful about transactions here and at the least wrap in in a Prisma transaction with the same db client for each in case of error to ensure the rollback.

This is why Prisma has interactive transactions and also the nested writes to set the transaction boundaries.

I’d be concerned your data could get out of sync if one step fails.

@dthyresson thank you for your suggestion. I have updated to use transactions:

export const updateCustomerWithRelations = async ({ id, input }) => {
  const prisma = new PrismaClient();

  return await prisma.$transaction(async (tx) => {
    const customer = await tx.customer.update({
      data: {
        name: input?.customer?.name,
        email: input?.customer?.email,
        notes: input?.customer?.notes,
        photo: input?.customer?.photo,
      },
      where: { id },
    });

    for (const address of input.addresses) {
      if (address.id) {
        await tx.address.update({
          data: address,
          where: { id: address.id },
        });
      } else {
        await tx.address.create({
          data: {
            customerId: customer.id,
            ...address,
          },
        });
      }
    }

    for (const phoneNumber of input.phoneNumbers) {
      if (phoneNumber.id) {
        await tx.phoneNumber.update({
          data: phoneNumber,
          where: { id: phoneNumber.id },
        });
      } else {
        await tx.phoneNumber.create({
          data: {
            customerId: customer.id,
            ...phoneNumber,
          },
        });
      }
    }

    const addresses = await tx.address.findMany({
      where: { customerId: customer.id },
    });
    const phoneNumbers = await tx.phoneNumber.findMany({
      where: { customerId: customer.id },
    });

    if (addresses.length > input?.addresses?.length) {
      const removeAddresses = addresses.filter(
        ({ id: id1 }) => !input?.addresses?.some(({ id: id2 }) => id1 === id2)
      );

      for (const removeAddress of removeAddresses) {
        await tx.address.delete({
          where: { id: removeAddress.id },
        });
      }
    }

    if (phoneNumbers.length > input?.phoneNumbers?.length) {
      const removePhones = phoneNumbers.filter(
        ({ id: id1 }) =>
          !input?.phoneNumbers?.some(({ id: id2 }) => id1 === id2)
      );

      for (const removeNumber of removePhones) {
        await tx.phoneNumber.delete({
          where: { id: removeNumber.id },
        });
      }
    }

    return customer;
  });
};