1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

B2BエンジニアがT3 Stackに入門してみたAdvent Calendar 2022

Day 7

t3-stack入門 (6)TodoにOwnerを追加する(Server側)

Posted at

TodoにOwnerを追加して

  • 自分のTodoだけ更新・削除削除可能
  • ログイン状態の時だけ、Todo追加可能
  • 未ログインの場合は、閲覧だけ可能

BtoBのシステムではよくある仕様で、このぐらいやらないと開発の実際の体験はわからない気がします。

TodoにOwnerプロパティを追加

--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -56,6 +56,7 @@ model User {
   image         String?
   accounts      Account[]
   sessions      Session[]
+  todos         Todo[]
 }
 
 model VerificationToken {
@@ -70,6 +71,8 @@ model Todo {
   id            Int    @id @default(autoincrement())
   title         String
   description   String
+  ownerId       String
+  owner         User     @relation(fields: [ownerId], references: [id])
   createdAt DateTime @default(now())
   updatedAt DateTime @updatedAt
 }

prismaでMigrateとgenerateしときます。

~/t3-app-example$ npx prisma migrate dev
~/t3-app-example$ npx prisma generate

Trpc用の入力スキーマを追加

@@ -6,3 +6,11 @@ export const createTodoSchema = z.object({
     .string()
     .min(5, { message: "Must be 5 or more characters long" }),
 });
+
+export const updateTodoSchema = createTodoSchema.extend({
+  id: z.number(),
+});
+
+export const deleteTodoSchema = z.object({
+  id: z.number(),
+});

createだけのSchemaしかなかったので、delete,update用のSchemaを追加。

Trpc用のコードを追加

create

create: protectedProcedure
    .input(createTodoSchema)
    .mutation(({ input, ctx }) => {
      return ctx.prisma.todo.create({
        data: {
          title: input?.title,
          description: input?.description,
          ownerId: ctx.session.user.id,
        },
      });
    }),

ownerIdにsessionから取得したuserのidを設定します。

update

  update: protectedProcedure
    .input(updateTodoSchema)
    .mutation(async ({ input, ctx }) => {
      const prev = await ctx.prisma.todo.findUniqueOrThrow({
        where: { id: input.id },
      });
      if (prev.ownerId !== ctx.session.user.id) {
        throw new Error("Update Error");
      }
      const newTodo = await ctx.prisma.todo.update({
        where: { id: input.id },
        data: {
          title: input.title,
          description: input.description,
        },
      });
      return newTodo;
    }),

ownerIdがsessionのユーザのIdと一致しなかったらErrorを投げるようにしました。
trpcで異常が発生したときどうするのがベストかよくわかっていません。

getAll

  getAll: publicProcedure.query(async ({ ctx }) => {
    return ctx.prisma.todo.findMany({
      select: {
        id: true,
        title: true,
        description: true,
        owner: {
          select: {
            name: true,
          },
        },
      },
    });
  }),

getAllでOwnerの名前も一緒に取得したいのですが

をみるとPrismaでリレーション先のデータも取得したい場合、includeを指定するとリレーション先丸ごと取得、selectを指定すると特定のカラムのみ取得できるようです。
ということはincludeを指定してPrismaの結果をそのまま返すとUser.crypted_passwordも返してしまいます。いくら暗号化されているとはいえさすがにまずい。
selectで、必要なカラムのみ取得するように指定するか、includeで指定して取得したデータから必要な分だけ返すようにするかする必要がありそうです。今回はとりあえずselectでowner.nameのみ取得するように指定しました。

Unit Test中に発行されるSQLをみたい

テストコードを追加して動作することを確認します。
ただ、開発用サーバを利用しているときは実際に実行されているSQLがコンソールに表示されるのですが、UnitTest実行中は表示されないみたいです。PrismaClientを生成しているのはsrc/server/db/client.tsで、env.NODE_ENVがdevelopmentの時だけ、queryが表示されるようになっていました。testの時も表示されるよう修正します。

@@ -11,7 +11,9 @@ export const prisma =
   global.prisma ||
   new PrismaClient({
     log:
-      env.NODE_ENV === "development" ? ["query", "error", "warn"] : ["error"],
+      env.NODE_ENV === "development" || env.NODE_ENV === "test"
+        ? ["query", "error", "warn"]

実際に実行されるログを見てみると、リレーション先のデータは別のQueryで取得してるみたいです。
N:1リレーションの場合、Left Joinすれば1回のQueryで取得できそうですが、そのような方法もあるのでしょうか。

stdout | src/server/trpc/router/todo.test.ts > getAll > getAll
prisma:query SELECT `main`.`Todo`.`id`, `main`.`Todo`.`title`, `main`.`Todo`.`description`, `main`.`Todo`.`ownerId` FROM `main`.`Todo` WHERE 1=1 LIMIT ? OFFSET ?
prisma:query SELECT `main`.`User`.`id`, `main`.`User`.`name` FROM `main`.`User` WHERE `main`.`User`.`id` IN (?) LIMIT ? OFFSET ?

今までの変更は以下の通りです。前回まではすべての変更を説明していましたが、長くなりすぎるので一部省くことにしました。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?