ryu110
@ryu110

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

Prismaのmutation deleteを使いたい

Backendでnode.js(ライブラリーはexpress.js),FrontendでReactを使用しています。
Prisma2でGraphQLを操作しています。
現在は下記のようなコードです。
Frontで投稿したポストをボタン押したときに消したいです。
backendのdeleteのところが間違っていると思います。

Frontでボタンを押したときにdata:nullで返ってきてerrorになります。

{errors: [{,…}], data: null}
data: null
errors: [{,…}]
0: {,…}
locations: [{line: 2, column: 3}]
0: {line: 2, column: 3}
column: 3
line: 2
message: "\nInvalid `prisma.user.update()` invocation:\n\n{\n  where: {\n    id: 1\n  },\n  data: {\n    posts: {\n      delete: {\n        '0': {\n?         id?: Int\n        }\n      },\n?     create?: PostCreateWithoutAuthorInput | PostCreateWithoutAuthorInput[] | PostUncheckedCreateWithoutAuthorInput | PostUncheckedCreateWithoutAuthorInput[],\n?     connectOrCreate?: PostCreateOrConnectWithoutAuthorInput | PostCreateOrConnectWithoutAuthorInput[],\n?     upsert?: PostUpsertWithWhereUniqueWithoutAuthorInput | PostUpsertWithWhereUniqueWithoutAuthorInput[],\n?     createMany?: {\n?       data: PostCreateManyAuthorInput,\n?       skipDuplicates?: Boolean\n?     },\n?     connect?: PostWhereUniqueInput | PostWhereUniqueInput[],\n?     set?: PostWhereUniqueInput | PostWhereUniqueInput[],\n?     disconnect?: PostWhereUniqueInput | PostWhereUniqueInput[],\n?     update?: PostUpdateWithWhereUniqueWithoutAuthorInput | PostUpdateWithWhereUniqueWithoutAuthorInput[],\n?     updateMany?: PostUpdateManyWithWhereWithoutAuthorInput | PostUpdateManyWithWhereWithoutAuthorInput[],\n?     deleteMany?: PostScalarWhereInput | PostScalarWhereInput[]\n    }\n  }\n}\n\nArgument data.posts.delete of type PostWhereUniqueInput needs at least one argument. Available args are listed in green.\n\nNote: Lines with ? are optional.\n"
path: ["deletePost"]
0: "deletePost"

datamodel post

model Post {
  id        Int      @id @default(autoincrement())
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  title     String   @db.VarChar(255)
  content   String?
  published Boolean  @default(false)
  author    User     @relation(fields: [authorId], references: [id])
  authorId  Int
}

Backend

import express from "express";
import cors from "cors";
import { PrismaClient } from "@prisma/client";
import { graphqlHTTP } from "express-graphql";
import { makeExecutableSchema } from "@graphql-tools/schema";

export interface Context {
  prisma: PrismaClient;
}

const prisma = new PrismaClient();

const typeDefs = `
  type User {
    id:Int!
    name: String
    email: String!
  }
  type Post {
    id: Int
    title: String!
    content:String!
  }
  type Query {
    allUsers: [User!]!
    allPosts: [Post!]!
  }
  type Mutation {
    createPost(
      title: String!
      content:String!
      ):Post!
    deletePost(
      id: Int
      ):Post!
    createUser(
      name: String
      email:String!
      ):User!
  }
`;

type argsPostType = {
  title: string;
  content: string;
};
type argsDeletePostType = {
  id: number ;
};
type argsUserType = {
  name: string;
  email: string;
};

const resolvers = {
  Query: {
    allUsers: () => {
      return prisma.user.findMany();
    },
    allPosts: () => {
      return prisma.post.findMany();
    },
  },
  Mutation: {
    createPost: (parent: any, args: argsPostType, context: any, info: any) => {
      const newPost = prisma.post.create({
        data: {
          title: args.title,
          content: args.content,
          author: {
            connect: {
              id: 1,
            },
          },
          published: true,
        },
      });
      return newPost;
    },
    deletePost: (
      parent: any,
      args: argsDeletePostType,
      context: any,
      info: any
    ) => {
      const deletedPost = prisma.user.update({
        where: {
          id: 1,
        },
        data: {
          posts: {
            delete: [{ id: args.id }],
          },
        },
      });
      return deletedPost;
    },
    createUser: (parent: any, args: argsUserType, context: any, info: any) => {
      const newUser = prisma.user.create({
        data: {
          name: args.name,
          email: args.email,
        },
      });
      return newUser;
    },
  },
};

export const schema = makeExecutableSchema({
  resolvers,
  typeDefs,
});

const app = express();
app.use(cors());
app.use(
  "/graphql",
  graphqlHTTP({
    schema,
    graphiql: true,
  })
);

if (process.env.NODE_ENV !== "production") {
  require("dotenv").config();
}

const PORT = process.env.PORT || 3001;

app.listen(PORT, () => {
  console.log(`接続完了! ${PORT}.`);
});

Front

import { useQuery, gql, useMutation } from '@apollo/client'
import { Button } from 'antd'

export const USER_DATA = gql`
  query allUsers {
    allUsers {
      id
      name
      email
    }
  }
`

export const POST_DATA = gql`
  query allPosts {
    allPosts {
      id
      title
      content
    }
  }
`

const DELETE_POST = gql`
  mutation deletePost($id: Int) {
    deletePost(id: $id) {
      id
    }
  }
`

export const PostsComponent = () => {
  const {
    data: user_data,
    loading: user_loading,
    error: user_error,
  } = useQuery(USER_DATA)
  const {
    data: post_data,
    loading: post_loading,
    error: post_error,
  } = useQuery(POST_DATA)

  const [deletePost] = useMutation(DELETE_POST, {
    refetchQueries: ['allPosts'],
  })

  const deletePostButton = () => {
    deletePost()
    alert('削除しました')
  }

  if (post_loading || user_loading) return <p>Loading...</p>
  if (post_error || user_error) return <p>Error...</p>
  return (
    <div>
      <div className="userData">
        {user_data.allUsers.length > 0 &&
          user_data.allUsers.map(
            ({
              id,
              name,
              email,
            }: {
              id: string
              name: string
              email: string
            }) => {
              return (
                <div key={id}>
                  <p>名前:{name}</p>
                  <p>メール:{email}</p>
                </div>
              )
            },
          )}
      </div>
      <div className="postData">
        {post_data.allPosts.length > 0 &&
          post_data.allPosts.map(
            ({
              id,
              title,
              content,
            }: {
              id: number | undefined
              title: string
              content: string
            }) => {
              return (
                <div key={id}>
                  <p>タイトル:{title}</p>
                  <p>内容:{content}</p>
          <Button onClick={() => deletePostButton()}>削除</Button>
                </div>
              )
            },
          )}
      </div>
    </div>
  )
}

0

3Answer

質問1

prisma.user.updateであっていますか?prismaの中にもdeleteというのもあるので今のupdateであっているのかと少し疑問です

間違っているというほどではないが、多数派ではない、というところでしょうか。

他の書き方としては、

prisma.post.delete({where: {id: postId}})

とする書き方があります。
この記法であればryuさんの記法と比べて「postをdeleteしようとしている」というのが明確になり、記述量も少ないというのが特徴ですが、ryuさんの記法であれば「userId: 1のuserに紐付いているpostの中で」かつ「Id: postId」のpostを削除するという限定条件がつくため、「userXに紐付いたpostを削除したかったのに、Front側がAPIの使い方を間違ってuserYさんのpostを削除してしまった」という事故を回避することができるという特徴もあります。
ただし、ryuさんの記法ではpostが本当に存在していなかったのか、たんにuserの紐付きが間違っていたのかが区別できないため、他userに紐付いたpostが削除されないようにするには

const post = await prisma.post.findUnique({where: {id: postId}})
if (post.authourId !== userId) throw new Forbidden("他のユーザーの投稿は削除できません")

await prisma.post.delete({where: {id: postId}})

などのようにバリデーションを行ってから削除するというのが一般的です。

質問2

次に、createPostをしているときはtitleとcontentをvariableとして渡っています。削除でする時にcreateの時の作成されたidのみで削除しようとしているのですが、上記のように今は間違った引数らしいですが、titleとcontentを渡すべきなのでしょうか。idだけで大丈夫だと思ったのですが。。。

何が間違っているのかについて、もう少し考えてみて、ご自分で色々試してみてください。

ryu110さんは prisma.user.update() を

where: {
id: 1
},
data: {
posts: {
delete: {
id: "Int"
},
を引数として呼び出しているようです。

このヒントをよく観察だけで、何が間違えているかは分かるはずです。

ちなみに、プログラムにパラメータが間違っていると言われた場合、大きくは

  • プログラムが期待しているパラメータと、自分が渡そうと思っているパラメータが違う場合 (プログラムはid: 1を期待しているが、自分はid: "1"を渡そうとしている)
  • 自分が渡そうと思っているパラメータと、実際に渡されているパラメータが違う場合(自分はid: 1を渡そうと思っていたが、実際に渡されているパラメータはid: "1"だった)

の2つがあることに気をつけてください

1Like

Comments

  1. @ryu110

    Questioner

    できました。
    関数のところからIdを付与していってvariablesにIdを渡すという形でした。
      const deletedPost = prisma.user.update({
        where: {
          id: 1,
        },
        data: {
          posts: {
-            delete: [{ id: args.id }],
+            delete: {id: args.id},
          },
        },
      });
      return deletedPost;
    },

としてみてください。

deleteキーは、単体のデータを削除するときの条件表現ですので、値に配列を入力することができません。

0Like

Comments

  1. @ryu110

    Questioner

    変更してみましたがvariableに何も入っていない状態になります。

    ```
    {errors: [{,…}], data: null}
    data: null
    errors: [{,…}]
    0: {,…}
    locations: [{line: 2, column: 3}]
    0: {line: 2, column: 3}
    column: 3
    line: 2
    message: "\nInvalid `prisma.user.update()` invocation:\n\n{\n where: {\n id: 1\n },\n data: {\n posts: {\n delete: {\n? id?: Int\n },\n? create?: PostCreateWithoutAuthorInput | PostCreateWithoutAuthorInput[] | PostUncheckedCreateWithoutAuthorInput | PostUncheckedCreateWithoutAuthorInput[],\n? connectOrCreate?: PostCreateOrConnectWithoutAuthorInput | PostCreateOrConnectWithoutAuthorInput[],\n? upsert?: PostUpsertWithWhereUniqueWithoutAuthorInput | PostUpsertWithWhereUniqueWithoutAuthorInput[],\n? createMany?: {\n? data: PostCreateManyAuthorInput,\n? skipDuplicates?: Boolean\n? },\n? connect?: PostWhereUniqueInput | PostWhereUniqueInput[],\n? set?: PostWhereUniqueInput | PostWhereUniqueInput[],\n? disconnect?: PostWhereUniqueInput | PostWhereUniqueInput[],\n? update?: PostUpdateWithWhereUniqueWithoutAuthorInput | PostUpdateWithWhereUniqueWithoutAuthorInput[],\n? updateMany?: PostUpdateManyWithWhereWithoutAuthorInput | PostUpdateManyWithWhereWithoutAuthorInput[],\n? deleteMany?: PostScalarWhereInput | PostScalarWhereInput[]\n }\n }\n}\n\nArgument data.posts.delete of type PostWhereUniqueInput needs at least one argument. Available args are listed in green.\n\nNote: Lines with ? are optional.\n"
    path: ["deletePost"]

    ```

フロント側の呼び出しにも問題があるようですね

エラーメッセージによると、

{
 where: {
 id: 1
 },
 data: {
 posts: {
 delete: {
? id?: Int
 },
? create?: PostCreateWithoutAuthorInput | PostCreateWithoutAuthorInput[] | PostUncheckedCreateWithoutAuthorInput | PostUncheckedCreateWithoutAuthorInput[],
? connectOrCreate?: PostCreateOrConnectWithoutAuthorInput | PostCreateOrConnectWithoutAuthorInput[],
? upsert?: PostUpsertWithWhereUniqueWithoutAuthorInput | PostUpsertWithWhereUniqueWithoutAuthorInput[],
? createMany?: {
? data: PostCreateManyAuthorInput,
? skipDuplicates?: Boolean
? },
? connect?: PostWhereUniqueInput | PostWhereUniqueInput[],
? set?: PostWhereUniqueInput | PostWhereUniqueInput[],
? disconnect?: PostWhereUniqueInput | PostWhereUniqueInput[],
? update?: PostUpdateWithWhereUniqueWithoutAuthorInput | PostUpdateWithWhereUniqueWithoutAuthorInput[],
? updateMany?: PostUpdateManyWithWhereWithoutAuthorInput | PostUpdateManyWithWhereWithoutAuthorInput[],
? deleteMany?: PostScalarWhereInput | PostScalarWhereInput[]
 }
 }
}

と書かれていますので、 ryu110さんは prisma.user.update()

 where: {
 id: 1
 },
 data: {
 posts: {
 delete: {
 id: "Int"
 },

を引数として呼び出しているようです。これが正しい引数になるように修正してみてください

0Like

Comments

  1. @ryu110

    Questioner

    2つほど質問なのですが、今現在しようとしているのが1ユーザの中の1つのポストを削除できるようにしようとしています。backendeでprisma.user.updateの中にdeleteとしてpostのidを指定しています。prisma.user.updateであっていますか?prismaの中にもdeleteというのもあるので今のupdateであっているのかと少し疑問です。

    次に、createPostをしているときはtitleとcontentをvariableとして渡っています。削除でする時にcreateの時の作成されたidのみで削除しようとしているのですが、上記のように今は間違った引数らしいですが、titleとcontentを渡すべきなのでしょうか。idだけで大丈夫だと思ったのですが。。。

Your answer might help someone💌