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

prismaのfeature機能TypedSQLを完全に理解したのでアウトプットしていくゥ

Last updated at Posted at 2025-05-07

これはなに

最近会社でNext.js(App Router)でダッシュボードアプリを開発しております。

そのアプリでは、PrismaというTypedScriptのORMツールを用いてDBとのアクセスを行っています。
今回はそのPrismaの機能の1つであるTypedSQLという機能を触ったのでアウトプットしていこうかと思います。


まず、結論からいうとTypedSQLは若干クセがあるものの、めちゃくちゃおすすめなツールです。

この記事では以下を説明しようかと思います。

  1. TypedSQLとは何か
  2. TypedSQLの基本的な使い方
  3. TypedSQLを使う上で詰まったポイント

この記事を読めば、こうなります!!

前:🤪「TypedSQLてなんやね〜〜〜ん。ブフォ!

後:😎「え?TypedSQL使ってないの?危機感持った方がいいよ。

では早速いきましょう!

🐶「この記事はPrismaについて最低限の知識がある前提で書いています」

1. TypedSQLとは何か?

概要

TypedSQLはざっくりいうと『生SQLから型安全なデータを取得できるツール』です。

.sqlにSQLを記述することでPrismaクライアント側で使うことができる型定義とクエリ関数を生成してくれます。

SQLから得られる結果を自動的にTypeScriptの型定義に変換してくれるため、「いちいち型の定義をしなくても良い」かつ「型安全にDBのデータを扱うことができる」ようになるのです…!

TypedSQLを初心者でも分かるように解説!

1. TypedSQL とは?

Prisma の TypedSQL は、自分で書いた SQL ファイル(.sql)をもとに、Prisma Client 側で型定義とクエリ関数を自動生成し、結果とパラメータの両方に型チェックを効かせられる機能です。
これにより、次のメリットがあります:

2. 導入手順(Getting started)

  1. バージョンの確認

    • @prisma/clientprisma を少なくとも 5.19.0 以上にアップデートします。
    npm install @prisma/client@latest
    npm install -D prisma@latest
    

    (Writing Type-safe SQL with TypedSQL and Prisma Client | Prisma Documentation)

  2. プレビュー機能の有効化

    • schema.prismagenerator client ブロックに previewFeatures = ["typedSql"] を追加します。
    generator client {
      provider        = "prisma-client-js"
      previewFeatures = ["typedSql"]
    }
    

    (Writing Type-safe SQL with TypedSQL and Prisma Client | Prisma Documentation)

  3. SQL ファイルの配置

    • プロジェクトの prisma ディレクトリ内に sql フォルダを作成し、そこにクエリを .sql として保存します(ファイル名は JS の識別子ルールに従い、$ で始めない)。
    mkdir -p prisma/sql
    
  4. SQL ファイルの作成例

    • 例:prisma/sql/getUsersWithPosts.sql
    SELECT
      u.id,
      u.name,
      COUNT(p.id) AS "postCount"
    FROM "User" u
    LEFT JOIN "Post" p
      ON u.id = p."authorId"
    GROUP BY
      u.id,
      u.name;
    

    (Writing Type-safe SQL with TypedSQL and Prisma Client | Prisma Documentation)

  5. クライアントの再生成

    • --sql フラグをつけて Prisma Client を生成すると、TypedSQL 用の関数と型が作成されます。
    prisma generate --sql
    # 変更を監視しながら生成する場合
    prisma generate --sql --watch
    

    (Writing Type-safe SQL with TypedSQL and Prisma Client | Prisma Documentation)

  6. TypeScript での利用例

    import { PrismaClient }             from '@prisma/client';
    import { getUsersWithPosts }       from '@prisma/client/sql';
    
    async function main() {
      const prisma = new PrismaClient();
      // 型安全に SQL を実行
      const usersWithPostCounts = await prisma.$queryRawTyped(
        getUsersWithPosts()
      );
      console.log(usersWithPostCounts);
    }
    main();
    

    (Writing Type-safe SQL with TypedSQL and Prisma Client | Prisma Documentation)

3. パラメータ付きクエリの書き方

TypedSQL では、SQL ファイル内にプレースホルダーを記述し、TypeScript 側で引数を渡すことで安全にパラメータ化されたクエリを実行できます。

PostgreSQL の例($1, $2…)

-- prisma/sql/getUsersByAge.sql
SELECT
  id,
  name,
  age
FROM
  users
WHERE
  age > $1
  AND age < $2;
import { PrismaClient }         from '@prisma/client';
import { getUsersByAge }        from '@prisma/client/sql';

async function main() {
  const prisma = new PrismaClient();
  const minAge = 18;
  const maxAge = 30;
  // 引数をそのまま渡す
  const users = await prisma.$queryRawTyped(
    getUsersByAge(minAge, maxAge)
  );
  console.log(users);
}
main();

Point: パラメータ化することで SQL インジェクションを防ぎ、型チェックも効かせられます。

4. 配列を引数に渡す(PostgreSQL のみ)

PostgreSQL の ANY 演算子と組み合わせることで、配列をパラメータとして渡せます。

-- prisma/sql/getUsersByIds.sql
SELECT
  id,
  name,
  email
FROM
  users
WHERE
  id = ANY($1);
import { PrismaClient }      from '@prisma/client';
import { getUsersByIds }     from '@prisma/client/sql';

async function main() {
  const prisma = new PrismaClient();
  const userIds = [1, 2, 3];
  const users = await prisma.$queryRawTyped(
    getUsersByIds(userIds)
  );
  console.log(users);
}
main();

TypedSQL が配列の型(number[] など)も自動で推論してくれます。ただし、大量の要素を一度に送る場合はプレースホルダー数の上限に注意してください (Writing Type-safe SQL with TypedSQL and Prisma Client | Prisma Documentation)

5. SQL ファイル内でのパラメータ型定義

SQL 側でパラメータの型やエイリアスをコメントで定義すると、より詳細な型情報を生成できます。

-- @param {Int}    $1:minAge  ユーザーの最小年齢
-- @param {Int}    $2:maxAge  ユーザーの最大年齢
SELECT
  id,
  name,
  age
FROM
  users
WHERE
  age > $1
  AND age < $2;

Note: 配列型の引数については手動での型定義はできず、TypedSQL の型推論に任せます。

6. TypedSQL の制限事項

  1. 対応データベース

  2. データベース接続が必須

  3. 動的カラム選択には非対応

ちなみに、TypedSQLはPrismaのPreview機能となっております。そのためか、公式ドキュメントはかなり少ない…

公式ドキュメント

🐶「記事を書いている時点(2025/04)では、TypedSQLはPrismaのPreview機能」

ちなみに以下の記事がめちゃくちゃわかりやすかったのでよかったらご覧ください。

めちゃくちゃわかりやすい記事


これは余談ですが、Golangにも似たようなsqlcというライブラリが存在します。

こちらも同様にSQLから型安全なコードを生成してくれます。

TypedSQLが生まれた背景

TypedSQLがざっくりとどんなものかは理解してもらえ方と思います。

続いて、TypedSQLが誕生した背景を調べたのでまとめました。



ChatGPTが…!!

TypedSQLが誕生した背景 以下では、TypedSQL が誕生した背景を「なぜ Prisma チームがこの機能を必要としたのか」という観点で解説します。まず要点をまとめ、その後で詳しく掘り下げます。

Prisma ORM の TypedSQL は、従来の $queryRaw を使った生の SQL 実行が「型安全性の欠如」「開発体験(DX)の低さ」といった課題を抱えていたため、これらを解消するために生まれました。Prisma Client API がカバーできない複雑クエリ(およそ 5%)に対して、TypeScript 上で入力と出力の両方に型チェックとオートコンプリートを提供し、手動で型定義を書く手間やメンテナンスコストを大幅に削減します。また、PgTyped や sqlx など他言語/他ツールの「型付き SQL」実装から着想を得ており、Prisma の高レベル抽象 (findMany など) と生 SQL の自由度をシームレスに両立する狙いがあります。

1. 従来の生 SQL 実行の課題

2. 逃げ道(Escape Hatch)だけでは不十分

3. 型安全な生 SQL「TypedSQL」の登場

4. 他プロジェクトからのインスピレーション

5. コミュニティと開発チームの声


まとめると、TypedSQL は「Prisma Client API が対応しきれない複雑クエリを、手間なく型安全かつ快適に扱いたい」という開発者ニーズと、「ORM と生 SQL の良さを両立したい」という Prisma チームのビジョンから生まれました。今後は GA に向けて安定性を高めつつ、さらなる開発体験の向上が期待されています。


TypedSQLが誕生した背景には大きく以下の2つがあるのかなと思います。

  1. ORMだと複雑なクエリを描こうとすると難しし、内部で実行されているSQLが思ってたのと違うってなる
  2. $queryRawを使えば生のSQLは書けるけど、デフォルトだと返り値の型がunknownなのであんまりイケてない…

って感じかと。

それをTypedSQLはどちらも叶えてくれる最強のツールとして誕生したというわけです…!!

TypedSQLを使えば生SQLを使うので複雑なクエリも書けてしまうし、返り値の型もTypedSQLが自動で定義してくれるので、型安全です。

全部可能にしてくれちゃってるんです。最強なんです。

🐶「これまで大変だったORMでの複雑なクエリの問題や生SQLを書いても型安全を保証できないという課題を全て解決…!!」


さらに詳しく知りたい方はこちらの記事を読んでもらえるといいかと!これまでのPrismaでの開発の課題などがわかりやすく書かれてあります。


ざっくりとTypedSQLについて理解できたかと思います。
次からは、どうやってTypedSQLを使うかを解説していきます!

2. TypedSQLの基本的な使い方

詳しくは以下の公式ドキュメントをご覧ください!

事前準備

schema.prismaファイルのpreviewFeaturesという箇所にtypedSqlを追加してください。

generator client {
  provider = "prisma-client-js"
  previewFeatures = ["typedSql"] # こんな感じ
}

sqlディレクトリにsqlを記述する

prismaディレクトリ配下にsqlというディレクトリを作成してください。
このディレクトリにsqlファイルを追加していきます。

mkdir -p prisma/sql

🐶「sqlファイルの名前が実行される関数名になります!例えば、fetchUser.sqlというファイルをprisma/sqlに配置すれば、呼び出す際はfetchUser()という関数になります!!」

以下のような.sqlファイルを追加すればOKです。

getUsersWithPosts.sql
SELECT u.id, u.name, COUNT(p.id) as "postCount"
FROM "User" u
LEFT JOIN "Post" p ON u.id = p."authorId"
GROUP BY u.id, u.name

ちなみにですが、変数をsqlファイル内に仕込みたい場合は$1, $2を用いることで可能(PostgreSQLの場合)です。

getUsersByAge.sql
SELECT id, name, age
FROM users
WHERE age > $1 AND age < $2

そのほかは以下のリンクを参照ください!

関数として呼び出すために、Prismaクライアントを作成する

以下のコマンドを実行することで、TypeScript関数として呼び出せる関数をPrismaが生成してくれます。

prisma generate --sql

そうすることで、以下のように関数を用いてSQLを実行することができるようになります。

import { PrismaClient } from '@prisma/client'
import { getUsersWithPosts } from '@prisma/client/sql'

const prisma = new PrismaClient()

const usersWithPostCounts = await prisma.$queryRawTyped(getUsersWithPosts())
console.log(usersWithPostCounts)

変数が必要な場合は、以下のように指定することができます。
めっちゃシンプルですね!!

import { PrismaClient } from '@prisma/client'
import { getUsersByAge } from '@prisma/client/sql'

const prisma = new PrismaClient()

const minAge = 18
const maxAge = 30
const users = await prisma.$queryRawTyped(getUsersByAge(minAge, maxAge))
console.log(users)

🐶「ざっくりと流れとしては、
schema.prismaファイルでtypedSQLを使えるようにする
sqlディレクトリに.sqlファイルを追加する
prisma generate --sqlコマンドを実行し、関数を作成
PrismaClientを経由して関数を呼び出す
ですっ!

個人的におすすめな使い方

個人的に好きなTypedSQLの使い方も共有しようかと思います。

それは、以下のように{sqlのファイル名}.Resultとすることで、自動的に生成された型をTypeScript内で柔軟に使えるようにすることです。

import { getUser } from '@prisma/client/sql';

type Result = getUser.Result;

Userの中身は以下の通り。(あくまでサンプルです)

export type Result = {
id: $runtime.Decimal | null
name: string | null
email: string | null
createdAt: Date | null
}

こうすることで

  • 型安全性の確保
  • IDEの自動保管サポート
  • リファクタリングの容易さの向上
  • 手動で型定義をする必要がなくなる

などのメリットを傍受できるかと思います!

3. TypedSQLを使う上で詰まったポイント

ここからはTypedSQLを使って実装を進める上で詰まったポイントについて共有していこうかと思います。

ドキュメントが少ない

まず、TypedSQLはPreview機能ということもあり、ドキュメントがめちゃくちゃ少ないです…!😭

私の知る限りだと、公式ドキュメントは以下の記事のみです…

🐶「2025/04時点だと公式ドキュメントはこれしかない…!」

詳しくは後述しますが、TypedSQLはprisma generate --sqlコマンドの実行時に、接続可能なDATABASE_URLが必要です。

それは公式ドキュメントでは具体的な対策は記述されておらず、ほかの人はどうやってデプロイしているのか調べる時にかなり大変だったことを覚えています。


以下のGitHubのDiscussionsを参照しながら、頑張って開発を進めた記憶があります。

🐶「いろんな人のTipsがDiscussionsには載っていたので一度見てみることをおすすめします!」

デプロイが大変

ちらっと頭出ししましたが、TypedSQLはprisma generate --sqlコマンドの実行時に、接続可能なDATABASE_URLが必要です。

特に設定をしないのであれば、prisma generate --sqlで生成されるコードはnode_modulesに作成されます。

🐶「バージョン管理するのであれば、DATABASE_URLは不要なので楽だけど、自動生成されるコードをバージョン管理するのあんまりイケてないよね。」

自動生成されるコードをバージョン管理したくないので、それは問題ないのですが、そうなるとビルド時に接続可能なDATABASE_URLが必要になってきます。

それの対応が大変でした…


実際、TypedSQLに関するDiscussionsでも同じく悩んでいる人が多くいらっしゃいました。


これらの記事を見ながら色々試行錯誤した結果、『ビルド時にPostgreSQLを作成し、接続できるようにする』ことで対応しました。

FROM node:22-bookworm-slim AS base

# 色々な処理

# OpenSSLとその他の必要な依存関係をベースイメージにインストール
# 参考:https://docs.docker.jp/engine/articles/dockerfile_best-practice.html#run
RUN apt-get update && apt-get install -y --no-install-recommends openssl postgresql postgresql-contrib && rm -rf /var/lib/apt/lists/*

# PostgreSQL用ディレクトリの作成と権限付与
# 参考:https://github.com/prisma/prisma/issues/25124#issuecomment-2507850308
RUN mkdir -p /run/postgresql /var/lib/postgresql/data && \
    chown -R postgres:postgres /run/postgresql /var/lib/postgresql/data

# PostgreSQLの初期化
RUN su - postgres -c "/usr/lib/postgresql/15/bin/initdb -D /var/lib/postgresql/data"

# 一度起動してpostgresユーザーのパスワードを設定し、すぐ停止
RUN su - postgres -c "/usr/lib/postgresql/15/bin/pg_ctl start -D /var/lib/postgresql/data && \
    psql -c \"ALTER USER postgres PASSWORD 'postgres';\" && \
    /usr/lib/postgresql/15/bin/pg_ctl stop -D /var/lib/postgresql/data"

    # Dockerfile 内で DATABASE_URL を設定
ENV DATABASE_URL=postgresql://postgres:postgres@localhost:5432/postgres

# PostgreSQLを起動し、マイグレーションおよびprisma generateコマンドを実行
RUN su - postgres -c "/usr/lib/postgresql/15/bin/pg_ctl start -D /var/lib/postgresql/data" && \
    pg_isready -h localhost -p 5432 -U postgres && \
    npx prisma db push && \
    npx prisma generate && \
    npx prisma generate --sql && \
    su - postgres -c "/usr/lib/postgresql/15/bin/pg_ctl stop -D /var/lib/postgresql/data"

RUN npm run build

# 色々な処理

このようにアクセス可能なダミーのDBをDockerfile内で作成するようにすることでビルド時に接続可能なDATABASE_URLが必要問題は解決しました。

🐶「もし他にいい方法があるならご教示いただけますと幸いだワン」

まとめ

最後までお読みいただきありがとうございました!

TypedSQLはPreview機能ながらも、生のSQLを用いながら型安全に、かつ複雑なクエリも書くことができるイケイケツールです。

ビルドの際に、アクセス可能なDATABASE_URLが必要になるという若干の癖がありつつも、使いこなせるようになれば非常に便利なツールかと思います。

皆様もぜひ、この記事をきっかけにTypedSQLに興味を持ってくださいますと幸いです!


あらためて、最後までお読みいただき本当にありがとうございました!

🐶「ありがとうございました〜!」

🐶「今回紹介したTypedSQLのビルド時の方法よりも良い方法があればぜひコメントで共有いただけますと幸いです!」

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