Automated summary
Guide for writing type-safe raw SQL with Prisma Client using $queryRaw, $executeRaw, and the Prisma.sql template tag.
Strengths
- Clearly explains automatic parameterization of tagged templates and warns about Prisma.raw() without an allowlist
- Covers dynamic query composition with Prisma.sql, Prisma.join, and Prisma.empty
- Includes raw queries inside transactions with row locking, plus result validation via Zod
- Documents return-type pitfalls: real column names, non-serializable BigInt, Prisma.Decimal, and Date
Limitations
- Examples are PostgreSQL-oriented (ILIKE, LOCK TABLE) and not portable as-is to other databases
- Does not address error handling, performance benchmarking, or the migration workflow beyond one diff command
- Only briefly covers $queryRawUnsafe, advising against it without detailing escaping strategies
Use when a Prisma Client query cannot express the SQL you need: CTEs, window functions, bulk upserts, or database-specific features.
Avoid when the query can be expressed with Prisma Client's typed API, since raw SQL bypasses type generation and loses its safety guarantees.
Security analysis
SafeThis is a pure reference/documentation skill for Prisma's raw query APIs. It declares no tools, contains no executable scripts, and makes no network or filesystem calls. The only code shown is illustrative TypeScript that uses parameterized queries and explicitly warns against injection risks with $queryRawUnsafe and Prisma.raw(), recommending allowlist validation of identifiers—guidance that improves rather than degrades safety.
No concerns found
How this analysis is madeExamples
Write a Prisma $queryRaw query with a CTE that returns the top 10 users by post count, and validate the rows with Zod.Replace a loop of prisma.post.upsert() with a single $executeRaw using INSERT ... ON CONFLICT to upsert 500 posts at once.Inside a prisma.$transaction, lock the Inventory row for a given SKU with SELECT ... FOR UPDATE and decrement the quantity atomically with $executeRaw.Prisma Raw Queries
Execute type-safe raw SQL with $queryRaw, $executeRaw, and Prisma.sql template tag
When to Use
- Running SQL queries that Prisma Client cannot express (CTEs, window functions, complex joins)
- Performing bulk operations more efficiently than Prisma's generated queries
- Calling database-specific features (full-text search, advisory locks, lateral joins)
- Executing data migrations or one-off scripts
Instructions
- Read query — use
$queryRawwith a tagged template literal. Always parameterize inputs:
const users = await prisma.$queryRaw<User[]>`
SELECT id, email, name FROM "User" WHERE role = ${role}
`;
The template tag automatically parameterizes ${role} as a prepared statement parameter — never string-interpolated.
- Write query — use
$executeRawfor INSERT, UPDATE, DELETE. Returns the affected row count:
const count = await prisma.$executeRaw`
UPDATE "Post" SET "viewCount" = "viewCount" + 1 WHERE id = ${postId}
`;
- Dynamic table or column names — use
Prisma.raw()for identifiers that cannot be parameterized:
import { Prisma } from '@prisma/client';
const column = Prisma.raw(`"${columnName}"`);
const result = await prisma.$queryRaw`
SELECT ${column} FROM "User" WHERE id = ${userId}
`;
Validate columnName against an allowlist before using Prisma.raw() to prevent SQL injection.
- Build dynamic queries with
Prisma.sqlandPrisma.join:
const ids = [1, 2, 3];
const result = await prisma.$queryRaw`
SELECT * FROM "User" WHERE id IN (${Prisma.join(ids)})
`;
- Compose query fragments with
Prisma.sql:
const where = searchTerm ? Prisma.sql`WHERE name ILIKE ${'%' + searchTerm + '%'}` : Prisma.empty;
const users = await prisma.$queryRaw`SELECT * FROM "User" ${where}`;
- Type the result —
$queryRawreturnsunknown[]by default. Pass a generic type or validate with Zod:
import { z } from 'zod';
const UserRow = z.object({ id: z.string(), email: z.string() });
const raw = await prisma.$queryRaw`SELECT id, email FROM "User"`;
const users = z.array(UserRow).parse(raw);
- Use raw queries inside transactions:
await prisma.$transaction(async (tx) => {
await tx.$executeRaw`LOCK TABLE "Inventory" IN EXCLUSIVE MODE`;
const [item] = await tx.$queryRaw<Inventory[]>`
SELECT * FROM "Inventory" WHERE sku = ${sku} FOR UPDATE
`;
await tx.$executeRaw`
UPDATE "Inventory" SET quantity = quantity - ${qty} WHERE sku = ${sku}
`;
});
Details
Raw queries bypass Prisma's query engine and type generation. The SQL runs directly against the database connection, but parameter binding still uses prepared statements for safety.
Table and column naming: Prisma maps model names to table names via @@map and field names to column names via @map. In raw SQL, you must use the actual database names, not the Prisma model names. Check with prisma migrate diff --from-empty --to-schema-datamodel prisma/schema.prisma --script to see the real table names.
Return type caveats:
$queryRawreturns rows with exact database column names (not Prisma field names)BigIntcolumns return JavaScriptBigInt, which is not JSON-serializable — convert toNumberorStringbefore sending to clientsDecimalcolumns returnPrisma.Decimalobjects, notnumberDateTimecolumns returnDateobjects
$queryRawUnsafe / $executeRawUnsafe: Accept a plain string instead of a tagged template. These do NOT parameterize — you are responsible for preventing SQL injection. Avoid unless you are building a query builder that handles parameterization itself.
Performance advantages of raw SQL:
- CTEs and window functions are impossible with Prisma Client but often the most efficient solution
INSERT ... ON CONFLICT(upsert) on multiple rows is significantly faster than loopingprisma.model.upsert()- Bulk updates with CASE expressions are faster than individual
updatecalls
Source
https://prisma.io/docs/orm/prisma-client/queries/raw-database-access
Process
- Read the instructions and examples in this document.
- Apply the patterns to your implementation, adapting to your specific context.
- Verify your implementation against the details and edge cases listed above.
Harness Integration
- Type: knowledge — this skill is a reference document, not a procedural workflow.
- No tools or state — consumed as context by other skills and agents.
Success Criteria
- The patterns described in this document are applied correctly in the implementation.
- Edge cases and anti-patterns listed in this document are avoided.
From the file to a first result
The download contains exactly the content displayed on this page. This copy may differ from the latest version in the source repository.
View the source and supporting filesThis download contains SKILL.md only. Any scripts, references, templates and dependencies are not included. Get the required files from the source, preserving their directory structure, before continuing.
1. Download the file
Download SKILL.mdFrom the root of a test project, save the file as .skill-downloads/prisma-raw-queries-ms47vpud/SKILL.md, or use the command below. This folder is for inspecting the skill before installation.
Command for macOS/Linux or WSL: requires a POSIX shell and curl. On Windows without WSL, use the download button and create the folder manually.
mkdir -p '.skill-downloads/prisma-raw-queries-ms47vpud' && curl --fail --show-error --location 'https://skills-guides.com/api/skills/prisma-raw-queries-ms47vpud/download' --output '.skill-downloads/prisma-raw-queries-ms47vpud/SKILL.md.part' && mv '.skill-downloads/prisma-raw-queries-ms47vpud/SKILL.md.part' '.skill-downloads/prisma-raw-queries-ms47vpud/SKILL.md'2. Check prerequisites
Read SKILL.md before using it. If it references scripts or external services, inspect them and prepare the required access in your test environment.
No recognized prerequisites section in this file. This does not mean it is self-contained: libraries, tools and access requirements may be mentioned elsewhere in the content or source repository.
The platforms on this page are catalog indications. The paths below follow tool documentation; they do not prove this skill has been run in those tools. No minimum tool version is confirmed for this listing.
3. Load the skill
This file does not provide a usable name/description header for this native procedure. It remains available to download and inspect; use the manual method below.
Manual method or another tool: attach SKILL.md to a conversation, or open it in an agent that can read files. Include any required supporting resources. No universal folder is recognized by every tool.
4. Try a first task
Use a short, synthetic input: an API endpoint, a small document or an interface brief. In a new conversation, attach the file or give the agent its path, then adapt this request:
Read .skill-downloads/prisma-raw-queries-ms47vpud/SKILL.md and apply its instructions to this example: [describe a test input]. Identify any missing files, dependencies or access. Produce the result requested by the skill without inventing data or running unreviewed scripts.Expected result: output matching the skill instructions, or a precise list of missing prerequisites. A read confirmation alone does not validate a first use. If an external resource is unavailable, that path remains untested.
React Hook Form Best Practices
Development
Master React Hook Form: avoid anti-patterns, manage subscriptions, and handle reset, dirty state, and controlled inputs correctly.
Emotion to Mantine Migration
Development
Migrate Emotion styled-components to Mantine components with style props and CSS modules. Use when converting .styled.tsx files or removing @emotion imports.
Scope Creep Detector
Development
Analyzes git diffs against a stated intent to detect scope creep, unrelated files, broad PRs, and oversized changes, then suggests keep, split, or justify actions.