3
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?

【TypeScript】Prisma構文内のキー名に変数を、CRUDを使いまわすぜ

Last updated at Posted at 2025-04-16

導入

初めまして、新卒のエンジニアです。
NestJSを使用して個人開発を進めています。
僕は処理をスマートに書くことができない、雑魚エンジニアです。
それを何とかしようと、以下の本を読んだところ、非常に感銘を受けました。

↓amazonのリンクです。
良いコード/悪いコードで学ぶ設計入門 ―保守しやすい 成長し続けるコードの書き方

クラス構造やクラス間の関係性など、なかなかイメージに落とし込むことが難しい範囲を、具体ベースに教えてくれています。これぞ良書!

それから、効率的なコードを書くことに対してモチベーションが爆上がりしています。
TypeScriptで書いたロジックを共通化してみたため、それを共有します。

ただ共通化してみただけですが、Prismaの学習にもなります。
Prisma構文内のキーに変数を用いています。

似た3つのtoggleロジック

まず、本記事で共通化するロジックは以下の機能です。

  1. いいね機能
  2. ストック機能
  3. フォロー機能

他ユーザが投稿したコンテンツに対していいね、ストックする機能と他ユーザをフォローする機能です。
これらの機能には共通点があります。

それは、新しい場合はcreate、重複した場合はdeleteするということです。

例えば、X(旧twitter)を想像してください。
あるツイートをいいねしてみたとします。また再度、そのツイートのいいねボタンを押すと、いいねが外れます。
この処理を表しています。
いいねもストックも同様です。

これを本記事ではtoggleロジックと表現していきます。
これら3つのロジックを共通化していきます。

解決策

ここからはコードベースで記していきます。

クラス構造

like.service.ts

コントローラがユーザからのリクエストを受け取り、サービスはコントローラからリクエスト情報を受け取ります。

import { Injectable, Request } from "@nestjs/common";
import { ToggleEvent } from "src/domain/domain-service/toggle.event";

@Injectable({})
export class LikeService {
    constructor(private readonly toggleEvent: ToggleEvent
    ) { }

    private readonly likeModel: string = 'like';
    private readonly activeKey: string = 'stamper_id';
    private readonly passiveKey: string = 'content_id';
    private readonly uniqueKey: string = 'unique_stamper_match_review';

    async toggleLikeState(@Request() req, insert_content_id: bigint) {
        try {
            const userId = req.user.userId;
            return this.toggleEvent.actToggle(this.likeModel, this.activeKey, this.passiveKey, this.uniqueKey, userId, insert_content_id);
        } catch (error) {
            console.error('toggle like state :', error);
            throw error;
        }
    }
}

これは余談ですが、NestJSはモジュールで依存関係を管理しています。
ToggleEventクラスはモジュールで読み込む必要があるため、注意してください。
他のstock, followのサービスクラスも同じ構造です。

渡す引数が多いですが、このような意味があります。

this.likeModel
Prisma ORMのモデル名です。likesテーブルとマッピングされています。
this.activeKey
実行したユーザIDのカラム名です。他のstock、followとはカラム名が異なるため、能動的な意味を込めて、activekeyと命名しています。あくまでカラム名です。
this.passivekey
activeKeyの逆です。受動的な意味を込めています。いいねやストックはされたコンテンツID、フォローはフォローされたユーザIDです。あくまでカラム名です。
this.uniqueKey
テーブルにあるユニークキー名です。2つのカラム複合でユニークキーがあります。このキー名がそれぞれ異なるため、引数で渡しています。
userId
ユーザIDです。this.activeKeyカラムに挿入されます。
passiveId
受動側のIDです。コンテンツIDやフォローされたユーザIDなどです。this.passiveKeyカラムに挿入されます。

toggle.event.ts

それでは共通化部分です。

import { Injectable } from "@nestjs/common";
import { PrismaService } from "src/infrastructure/prisma/prisma.service";

@Injectable({})
export class ToggleEvent {
    constructor(private readonly prisma: PrismaService) {}

    async checkExists(insertDB: string, activeKey: string, passiveKey: string, uniqueKey: string, userId: bigint, passiveId: bigint) {
        const existedBool = await this.prisma[insertDB].findUnique({
            where: {
                [uniqueKey]: {
                    [activeKey]: userId,
                    [passiveKey]: passiveId
                }
            }
        });

        if (existedBool) {
            await this.prisma[insertDB].delete({
                where: {
                    [uniqueKey]: {
                        [activeKey]: userId,
                        [passiveKey]: passiveId
                    }
                }
            });
            return { message: `${uniqueKey} removed` };
        } else {
            await this.prisma[insertDB].create({
                data: {    
                    [activeKey]: userId,
                    [passiveKey]: passiveId
                }
            });
            return { message: `${uniqueKey} added` };
        }
    }
}

渡されたイベントごとに、テーブルに同値が存在するか判別、生成または削除を行います。

以下は重複するカラムが存在するか、という箇所です

const existedBool = await this.prisma[insertDB].findUnique({
    where: {
        [uniqueKey]: {
            [activeKey]: userId,
            [passiveKey]: passiveId
        }
    }
});

1行目の[insertDB]は本当ならこういう書き方をします

const existedBool = await this.prisma.like.findUnique({

ですが、今回はロジックの共通化です。likeだけではなく、stockもfollowも実行する必要があります。そのため、this.prisma[insertDB]と変数を入れています。

次に注目してほしいのはprisma構文内の[uniqueKey]です。
prismaは大変良くできており、マイグレーション時点でそれぞれのモデルごとに、メソッドごとの相応しい型付けを勝手に行ってくれます。
likesテーブルのユニークキーはunique_stamper_contentという名前です。
stamper_idcontent_idというカラムがあります。
likeモデルのfinduniqueメソッドでは、以上のキーが必要です。
ですがこれらのキー名は、like、stock、followで異なります。
そこで、各サービスから固有値をキー名で渡し、それを変数としてprisma構文内のキーで呼び出しています。

キー名を変数で、よくできた言語ですねえ。

改善点

サービスファイルから渡す引数が多いことが気になります。
脳筋感が満載です。
次はこれを何とかします。

また、本記事で取り上げたコードはNestJS内で書いています。
NestJSが好きな方はいいね、コメントしてくれると嬉しいです!
同志よ、立ち上がれ 。

3
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
3
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?