🙆‍♀️

【Drizzle ORM】NextJs14 と Drizzle ORM【#26 Profile Table Relation】

2024/11/04に公開

【#26 Profile Table Relation】

YouTube: https://youtu.be/3XYTK-kqKW4
https://youtu.be/3XYTK-kqKW4

今回はユーザーデータの取得の際に
プロフィールのデータも取得する方法について実装していきます。

こちらは「leftJoin」か「with」を使用します。
「innerJoin」を使用するとプロフィールのデータが存在しない場合に、
ユーザーのデータも取得されない場合があるので注意が必要です。

「leftJoin」と「with」で取得時のデータの構造が異なりますので、
こちらも注意が必要です。

app/api/[[...route]]/users.ts
import { Hono } from "hono";
import { z } from "zod";
import { zValidator } from "@hono/zod-validator";
import { clerkMiddleware, getAuth } from "@hono/clerk-auth";

import { db } from "@/db/drizzle";
import { profile, users } from "@/db/schema";
import { eq } from "drizzle-orm";

const app = new Hono()
  .get("/", async (c) => {
    const data = await db.select().from(users);

    return c.json({
      data,
    });
  })
  .get("/me", clerkMiddleware(), async (c) => {
    const auth = getAuth(c);

    if (!auth?.userId) {
      return c.json({ error: "Unauthorized" }, 401);
    }

    // const [data] = await db
    //   .select()
    //   .from(users)
    //   .leftJoin(profile, eq(users.id, profile.userId))
    //   .where(eq(users.clerkId, auth.userId));

    const data = await db.query.users.findFirst({
      where: eq(users.clerkId, auth.userId),
      with: {
        profile: {
          columns: {
            message: true,
          },
        },
      },
    });

    if (!data) {
      return c.json({ error: "User not found" }, 404);
    }

    return c.json({ data });
  })
  .get(
    "/:id",
    zValidator(
      "param",
      z.object({
        id: z.string(),
      })
    ),
    async (c) => {
      // const id = c.req.param("id");
      const { id } = c.req.valid("param");

      const [data] = await db.select().from(users).where(eq(users.id, id));

      if (!data) {
        return c.json({ error: "User not found" }, 404);
      }

      // if (data.length === 0) {
      //   return c.json({ error: "User not found" }, 404);
      // }

      return c.json({
        user: data,
      });
    }
  );

export default app;

「with」を使用する場合は、
「users」と「profile」のテーブルそれぞれに
Relationの設定をする必要があります。

「profile」のRelationの設定として、
「profileRelations」を追記しています。

こちらはマイグレーションをしなくても動作します。

db/schema.ts
import { relations } from "drizzle-orm";
import { pgTable, text, timestamp, primaryKey } from "drizzle-orm/pg-core";
import { createInsertSchema } from "drizzle-zod";

export const users = pgTable("users_table", {
  id: text("id").primaryKey(),
  clerkId: text("clerk_id").notNull().unique(),
  name: text("name").notNull(),
  email: text("email").notNull().unique(),
  imageUrl: text("image_url"),
  createdAt: timestamp("created_at").notNull().defaultNow(),
  updatedAt: timestamp("updated_at")
    .notNull()
    .$onUpdate(() => new Date()),
});

export const usersRelations = relations(users, ({ one, many }) => ({
  profile: one(profile),
  posts: many(posts),
  comments: many(comments),
  usersToGroups: many(usersToGroups),
}));

export const insertUsersSchema = createInsertSchema(users);

export const profile = pgTable("profile_table", {
  id: text("id").primaryKey(),
  userId: text("user_id").references(() => users.id, { onDelete: "cascade" }),
  message: text("message"),
  createdAt: timestamp("created_at").notNull().defaultNow(),
  updatedAt: timestamp("updated_at")
    .notNull()
    .$onUpdate(() => new Date()),
});

export const profileRelations = relations(profile, ({ one, many }) => ({
  user: one(users, {
    fields: [profile.userId],
    references: [users.id],
  }),
}));

export const insertProfileSchema = createInsertSchema(profile);

export const posts = pgTable("posts_table", {
  id: text("id").primaryKey(),
  userId: text("user_id").references(() => users.id),
  content: text("content"),
  createdAt: timestamp("created_at").notNull().defaultNow(),
  updatedAt: timestamp("updated_at")
    .notNull()
    .$onUpdate(() => new Date()),
});

export const postsRelations = relations(posts, ({ one, many }) => ({
  author: one(users, {
    fields: [posts.userId],
    references: [users.id],
  }),
  comments: many(comments),
}));

export const insertPostsSchema = createInsertSchema(posts);

export const comments = pgTable("comments_table", {
  id: text("id").primaryKey(),
  userId: text("user_id").references(() => users.id),
  postId: text("post_id").references(() => posts.id),
  comment: text("comment"),
  createdAt: timestamp("created_at").notNull().defaultNow(),
  updatedAt: timestamp("updated_at")
    .notNull()
    .$onUpdate(() => new Date()),
});

export const commentsRelations = relations(comments, ({ one, many }) => ({
  user: one(users, {
    fields: [comments.userId],
    references: [users.id],
  }),
  post: one(posts, {
    fields: [comments.postId],
    references: [posts.id],
  }),
}));

export const insertCommentsSchema = createInsertSchema(comments);

export const groups = pgTable("groups_table", {
  id: text("id").primaryKey(),
  name: text("name"),
});

export const groupsRelations = relations(groups, ({ many }) => ({
  usersToGroups: many(usersToGroups),
}));

export const usersToGroups = pgTable(
  "users_to_groups",
  {
    userId: text("user_id")
      .notNull()
      .references(() => users.id),
    groupId: text("group_id")
      .notNull()
      .references(() => groups.id),
  },
  (t) => ({
    pk: primaryKey({ columns: [t.userId, t.groupId] }),
  })
);

export const usersToGroupsRelations = relations(usersToGroups, ({ one }) => ({
  group: one(groups, {
    fields: [usersToGroups.groupId],
    references: [groups.id],
  }),
  user: one(users, {
    fields: [usersToGroups.userId],
    references: [users.id],
  }),
}));

Discussion