You should cut out your steps in a transaction to delete the tags not in the query before inserting the new ones. Then you can create and update your data and return your post

With interactiveTransactions

In your schema.prisma you should enable interactiveTransactions, for this:

generator client {
  provider        = "prisma-client-js"
  previewFeatures = ["interactiveTransactions"]
}

Then cut out your steps in a transaction

// Remove <Post> if you aren't in typescript
await prisma.$transaction<Post>(async (trx) => {
  const existingTags = tags.filter(({ id }) => id);
  const newTags = tags.filter(({ id }) => !id);

  await trx.tag.deleteMany({
    where: {
      id: {
        notIn: existingTags.map(({ id }) => id),
      },
    },
  });
  // Update the existing tags
  await Promise.all(
    tags.map((tag) =>
      trx.tag.update({
        where: {
          id: tag.id,
        },
        data: {
          color: tag.color,
          name: tag.name,
        },
      })
    )
  );
  await trx.tag.createMany({
    data: newTags.map((tag) => ({
      ...tag,
      creator: {
        connect: {
          id: session.user.id,
        },
      },
    })),
  });
  return trx.post.update({
    where: {
      id: postId,
    },
    data: {
      title,
      content,
      updatedAt: new Date(),
    },
    include: {
      tags: true,
    },
  });
});
Answer from Pompedup on Stack Overflow
🌐
Prisma
v1.prisma.io › docs › 1.34 › prisma-client › basic-data-access › writing-data-TYPESCRIPT-rsc7
Prisma 1.34 - Writing Data (TypeScript) with TypeScript
TypeScript · Result · const updatedOrCreatedUser: User = await prisma.upsertUser({ where: { email: 'alice@prisma.io', }, update: { role: 'ADMIN', }, create: { name: 'Alice', email: 'alice@prisma.io', role: 'ADMIN', }, }) Copy · The Prisma client API offers special methods to update or delete many records at once.
🌐
SabinTheDev
sabinadams.hashnode.dev › basic-crud-operations-in-prisma
Prisma and TypeScript CRUD Basics - Sabin Adams
June 21, 2024 - We then via Prisma run a query that will look up the user with the given id and update the age value of that record. An upsert's where option filters on unique identifiers only
Discussions

`upsert()` `where` is defined as `number | undefined` but does not allow `undefined` to not select anything
Hi, the where object shows a hint that it is of type number | undefined, but I am unable to use undefined. I must specify where: {unique_id: 0} to get it working or am I missing something? More on github.com
🌐 github.com
33
January 21, 2021
How is .upsertMany() implemented in Prisma ORM?
Prisma doesn't natively support upsertMany. More on stackoverflow.com
🌐 stackoverflow.com
`<Tablename>UpsertArgs` select field does not match type for `db.<tablename>.upsert(item)`
Bug description On upgrading to Prisma version 5.0.0, the typescript types for the select field of a Prisma. UpsertArgs datatype is no longer compatible with the actual upsert funct... More on github.com
🌐 github.com
6
July 16, 2023
Unsafe return of any typed value, using prisma upsert
© 2026 Hedgehog Software, LLC · TwitterGitHubDiscord · System · Light · CommunitiesDocsAboutTermsPrivacy · Unsafe return of any typed value, using prisma upsert - Theo's Typesafe Cult · Theo's Typesafe Cult•4y ago• · 18 replies · Christian Lind · Unsafe return of any typed value, ... More on answeroverflow.com
🌐 answeroverflow.com
January 29, 2023
🌐
Prisma
prisma.io › home › type safety overview › type safety overview › type safety overview
Type safety | Prisma Documentation
The UncheckedInput types are a special set of generated types that allow you to perform some operations that Prisma Client considers "unsafe", like directly writing relation scalar fields. You can choose either the "safe" Input types or the "unsafe" UncheckedInput type when doing operations like create, update, or upsert.
🌐
Prisma
prisma.io › home › prisma client api › prisma client api › prisma client api
Prisma Client API | Prisma Documentation
When Prisma Client does an upsert, it first checks whether that record already exists in the database. To make this check, Prisma Client performs a read operation with the where clause from the upsert operation.
🌐
Prisma
prisma.io › home › crud › crud › crud › crud
CRUD (Reference) | Prisma Documentation
const upsertUser = await prisma.user.upsert({ where: { email: "viola@prisma.io" }, update: { name: "Viola the Magnificent" }, create: { email: "viola@prisma.io", name: "Viola the Magnificent" }, });
🌐
GitHub
github.com › prisma › prisma › issues › 5233
`upsert()` `where` is defined as `number | undefined` but does not allow `undefined` to not select anything · Issue #5233 · prisma/prisma
January 21, 2021 - upsert() where is defined as number | undefined but does not allow undefined to not select anything#5233 ... tech/typescriptIssue for tech TypeScript.Issue for tech TypeScript.topic: client typesTypes in Prisma ClientTypes in Prisma Clienttopic: undefinedtopic: upsertnested upsertnested upserttopic: upsert()
Author   prisma
Find elsewhere
🌐
Paigeniedringhaus
paigeniedringhaus.com › blog › tips-and-tricks-for-using-the-prisma-orm
Tips and Tricks for Using the Prisma ORM | Paige Niedringhaus
/** * Insert or update the event ... like we are in our project, just define the JSON field value as an object and then simply pass it in as one of the values in the Prisma upsert() function....
🌐
xjavascript
xjavascript.com › blog › how-to-upsert-many-fields-in-prisma-orm
How to Upsert Multiple Fields in Prisma ORM with a Single Query: A Complete Guide — xjavascript.com
This is especially useful for use ... consistency. Prisma ORM, a popular TypeScript/JavaScript ORM, simplifies database interactions with its type-safe API....
🌐
GitHub
github.com › prisma › prisma › issues › 20243
`<Tablename>UpsertArgs` select field does not match type for `db.<tablename>.upsert(item)` · Issue #20243 · prisma/prisma
July 16, 2023 - On upgrading to Prisma version 5.0.0, the typescript types for the select field of a Prisma.<TableName>UpsertArgs datatype is no longer compatible with the actual upsert function for that table.
Author   prisma
🌐
Angularfix
angularfix.com › 2022 › 03 › how-is-upsertmany-implemented-in-prisma.html
How is .upsertMany() implemented in Prisma ORM? ~ angularfix
March 17, 2022 - As of now the best approach would be to loop over the data and invoke upsert in the loop along with using $transaction. ... const collection = await prisma.$transaction( userData.map(cur => prisma.cur.upsert({ where: { id: cur.id }, update: {}, create: { id: cur.id }, }) ) )
🌐
Answer Overflow
answeroverflow.com › m › 1069229816378822826
Unsafe return of any typed value, using prisma upsert
January 29, 2023 - setTheoryModule: protectedProcedure .input(z.object({ moduleName: z.string(), state: z.boolean() })) .mutation(async ({ input, ctx }) => { const user = ctx.session.user.id; const { moduleName, state } = input; const setTheoryModule = await ctx.prisma.theoryProgress.upsert({ where: { userId: user, moduleName: moduleName, }, create: { userId: user, moduleName: moduleName, state: state, }, update: { state: state, }, }); return setTheoryModule; }), Its saying that "Unsafe assignment of an
🌐
GitHub
github.com › prisma › prisma › issues › 3573
Create a common type for upsert's `update` and `create` · Issue #3573 · prisma/prisma
September 9, 2020 - kind/featureA request for a new feature.A request for a new feature.tech/typescriptIssue for tech TypeScript.Issue for tech TypeScript.topic: client typesTypes in Prisma ClientTypes in Prisma Clienttopic: createnested createnested createtopic: dxtopic: prisma-clienttopic: updateNested query `update`Nested query `update`topic: upsertnested upsertnested upserttopic: upsert()
Author   prisma
Top answer
1 of 2
14

I'm providing my solution based on the clarifications you provided in the comments. First I would make the following changes to your Schema.

Changing the schema

model A_User {
  id        Int          @id
  username  String
  age       Int
  bio       String       @db.VarChar(1000)
  createdOn DateTime     @default(now())
  features  A_Features[]
}

model A_Features {
  id          Int      @id @default(autoincrement())
  description String   @unique
  users       A_User[]
}

Notably, the relationship between A_User and A_Features is now many-to-many. So a single A_Features record can be connected to many A_User records (as well as the opposite).

Additionally, A_Features.description is now unique, so it's possible to uniquely search for a certain feature using just it's description.

You can read the Prisma Guide on Relations to learn more about many-to-many relations.

Writing the update query

Again, based on the clarification you provided in the comments, the update operation will do the following:

  • Overwrite existing features in a A_User record. So any previous features will be disconnected and replaced with the newly provided ones. Note that the previous features will not be deleted from A_Features table, but they will simply be disconnected from the A_User.features relation.

  • Create the newly provided features that do not yet exist in the A_Features table, and Connect the provided features that already exist in the A_Features table.

You can perform this operation using two separate update queries. The first update will Disconnect all previously connected features for the provided A_User. The second query will Connect or Create the newly provided features in the A_Features table. Finally, you can use the transactions API to ensure that both operations happen in order and together. The transactions API will ensure that if there is an error in any one of the two updates, then both will fail and be rolled back by the database.


//inside async function
const disconnectPreviouslyConnectedFeatures =  prisma.a_User.update({
    where: {id: 1},
    data: {
        features: {
            set: []  // disconnecting all previous features
        }
    }
})

const connectOrCreateNewFeatures =  prisma.a_User.update({
    where: {id: 1},
    data: {
        features: {
            // connect or create the new features
            connectOrCreate: [
                {
                    where: {
                        description: "'first feature'"
                    }, create: {
                        description: "'first feature'"
                    }
                },
                {
                    where: {
                        description: "second feature"
                    }, create: {
                        description: "second feature"
                    }
                }
            ]
        }
    }
})

// transaction to ensure either BOTH operations happen or NONE of them happen.
await prisma.$transaction([disconnectPreviouslyConnectedFeatures, connectOrCreateNewFeatures ])

If you want a better idea of how connect, disconnect and connectOrCreate works, read the Nested Writes section of the Prisma Relation queries article in the docs.

2 of 2
2

The TypeScript definitions of prisma.a_User.update can tell you exactly what options it takes. That will tell you why the 'features' does not exist in type error is occurring. I imagine the object you're passing to data takes a different set of options than you are specifying; if you can inspect the TypeScript types, Prisma will tell you exactly what options are available.

If you're trying to add new features, and update specific ones, you would need to specify how Prisma can find an old feature (if it exists) to update that one. Upsert won't work in the way that you're currently using it; you need to provide some kind of identifier to the upsert call in order to figure out if the feature you're adding already exists.

https://www.prisma.io/docs/reference/api-reference/prisma-client-reference/#upsert

You need at least create (what data to pass if the feature does NOT exist), update (what data to pass if the feature DOES exist), and where (how Prisma can find the feature that you want to update or create.)

You also need to call upsert multiple times; one for each feature you're looking to update or create. You can batch the calls together with Promise.all in that case.

const upsertFeature1Promise = prisma.a_User.update({
  data: {
    // upsert call goes here, with "create", "update", and "where"
  }
});
const upsertFeature2Promise = prisma.a_User.update({
  data: {
    // upsert call goes here, with "create", "update", and "where"
  }
});
const [results1, results2] = await Promise.all([
  upsertFeaturePromise1,
  upsertFeaturePromise2
]);
🌐
GitHub
github.com › prisma › prisma-client-js › issues › 781
Upsert should allow where clause with only undefined · Issue #781 · prisma/prisma-client-js
July 23, 2020 - kind/improvementAn improvement to existing feature and code.An improvement to existing feature and code.tech/typescriptIssue for tech TypeScript.Issue for tech TypeScript. ... Currently when using upsert, you have to specify at least one argument in the where clause. This is problematic when creating an upsert interface since the id might be undefined/null. This requires us to do something like: ... Argument where of type MyModelWhereUniqueInput needs at least one argument. Available args are listed in green. ... Prisma should treat this case as a create case instead of throwing an error.
Author   prisma
🌐
GitHub
github.com › prisma › prisma › labels › topic: upsert
Issues · prisma/prisma
Next-generation ORM for Node.js & TypeScript | PostgreSQL, MySQL, MariaDB, SQL Server, SQLite, MongoDB and CockroachDB - Issues · prisma/prisma
Author   prisma
🌐
GitHub
github.com › prisma › prisma › issues › 21841
Proposal: Enhance Prisma `upsert` Method with Callback Support · Issue #21841 · prisma/prisma
November 7, 2023 - Proposal: Enhance Prisma upsert Method with Callback Support#21841 · Copy link · Labels · domain/clientIssue in the "Client" domain: Prisma Client, Prisma Studio etc.Issue in the "Client" domain: Prisma Client, Prisma Studio etc.kind/improvementAn improvement to existing feature and code.An improvement to existing feature and code.tech/enginesIssue for tech Engines.Issue for tech Engines.tech/typescriptIssue for tech TypeScript.Issue for tech TypeScript.topic: upsertnested upsertnested upsert ·
Author   prisma