-
Notifications
You must be signed in to change notification settings - Fork 464
Latest dependencies #1080
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Latest dependencies #1080
Conversation
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
|
Cursor Agent can help with this pull request. Just |
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
| -- @param {String} $1:like | ||
| -- @param {String} $2:like2 | ||
| SELECT | ||
| "User".id, | ||
| "User".username, | ||
| "User".name, | ||
| "UserImage".id AS imageId, | ||
| "UserImage".objectKey AS imageObjectKey | ||
| FROM "User" | ||
| LEFT JOIN "UserImage" ON "User".id = "UserImage".userId | ||
| WHERE "User".username LIKE :like | ||
| OR "User".name LIKE :like | ||
| WHERE "User".username LIKE ? | ||
| OR "User".name LIKE ? | ||
| ORDER BY ( | ||
| SELECT "Note".updatedAt | ||
| FROM "Note" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cursor, are the changes in this file necessary? Here's a snippet from the docs:
title: 'TypedSQL'
metaTitle: 'Writing Type-safe SQL with TypedSQL and Prisma Client'
metaDescription: 'Learn how to use TypedSQL to write fully type-safe SQL queries that are compatible with any SQL console and Prisma Client.'
sidebar_class_name: preview-badge
Getting started with TypedSQL
To start using TypedSQL in your Prisma project, follow these steps:
-
Ensure you have
@prisma/clientandprismainstalled and updated to at least version5.19.0.npm install @prisma/client@latest npm install -D prisma@latest -
Add the
typedSqlpreview feature flag to yourschema.prismafile:generator client { provider = "prisma-client" // or "prisma-client-js" previewFeatures = ["typedSql"] output = "../src/generated/prisma" }
:::tip[Using driver adapters with TypedSQL]
If you are deploying Prisma in serverless or edge environments, you can use driver adapters to connect through JavaScript database drivers. Driver adapters are compatible with TypedSQL, with the exception of
@prisma/adapter-better-sqlite3. For SQLite support, use@prisma/adapter-libsqlinstead. All other driver adapters are supported.:::
-
Create a
sqldirectory inside yourprismadirectory. This is where you'll write your SQL queries.mkdir -p prisma/sql:::note[Custom SQL folder location]
Starting with Prisma 6.12.0, you can configure a custom location for your SQL files using the Prisma config file. Create a
prisma.config.tsfile in your project root and specify thetypedSql.pathoption:import 'dotenv/config' import { defineConfig } from 'prisma/config' export default defineConfig({ schema: './prisma/schema.prisma', typedSql: { path: './prisma/sql', }, })
:::
-
Create a new
.sqlfile in yourprisma/sqldirectory. For example,getUsersWithPosts.sql. Note that the file name must be a valid JS identifier and cannot start with a$. -
Write your SQL queries in your new
.sqlfile. For example: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
-
Generate Prisma Client with the
sqlflag to ensure TypeScript functions and types for your SQL queries are created::::warning
Make sure that any pending migrations are applied before generating the client with the
sqlflag.:::
prisma generate --sqlIf you don't want to regenerate the client after every change, this command also works with the existing
--watchflag:prisma generate --sql --watch -
Now you can import and use your SQL queries in your TypeScript code:
import { PrismaClient } from './generated/prisma/client'
import { getUsersWithPosts } from './generated/prisma/sql'
const prisma = new PrismaClient()
const usersWithPostCounts = await prisma.$queryRawTyped(getUsersWithPosts())
console.log(usersWithPostCounts):::note
If you do not customize the generator output, you can import from @prisma/client and @prisma/client/sql instead.
:::
Passing Arguments to TypedSQL Queries
To pass arguments to your TypedSQL queries, you can use parameterized queries. This allows you to write flexible and reusable SQL statements while maintaining type safety. Here's how to do it:
-
In your SQL file, use placeholders for the parameters you want to pass. The syntax for placeholders depends on your database engine:
In SQLite, there are a number of different placeholders you can use. Positional placeholders (
$1,$2, etc.), general placeholders (?), and named placeholders (:minAge,:maxAge, etc.) are all available. For this example, we'll use named placeholders:minAgeand:maxAge:SELECT id, name, age FROM users WHERE age > :minAge AND age < :maxAge
:::note
See below for information on how to define argument types in your SQL files.
:::
-
When using the generated function in your TypeScript code, pass the arguments as additional parameters to
$queryRawTyped:
import { PrismaClient } from './generated/prisma/client'
import { getUsersByAge } from './generated/prisma/sql'
const prisma = new PrismaClient()
const minAge = 18
const maxAge = 30
const users = await prisma.$queryRawTyped(getUsersByAge(minAge, maxAge))
console.log(users)By using parameterized queries, you ensure type safety and protect against SQL injection vulnerabilities. The TypedSQL generator will create the appropriate TypeScript types for the parameters based on your SQL query, providing full type checking for both the query results and the input parameters.
Passing array arguments to TypedSQL
TypedSQL supports passing arrays as arguments for PostgreSQL. Use PostgreSQL's ANY operator with an array parameter.
SELECT id, name, email
FROM users
WHERE id = ANY($1)import { PrismaClient } from './generated/prisma/client'
import { getUsersByIds } from './generated/prisma/sql'
const prisma = new PrismaClient()
const userIds = [1, 2, 3]
const users = await prisma.$queryRawTyped(getUsersByIds(userIds))
console.log(users)TypedSQL will generate the appropriate TypeScript types for the array parameter, ensuring type safety for both the input and the query results.
:::note
When passing array arguments, be mindful of the maximum number of placeholders your database supports in a single query. For very large arrays, you may need to split the query into multiple smaller queries.
:::
Defining argument types in your SQL files
Argument typing in TypedSQL is accomplished via specific comments in your SQL files. These comments are of the form:
-- @param {Type} $N:alias optional descriptionWhere Type is a valid database type, N is the position of the argument in the query, and alias is an optional alias for the argument that is used in the TypeScript type.
As an example, if you needed to type a single string argument with the alias name and the description "The name of the user", you would add the following comment to your SQL file:
-- @param {String} $1:name The name of the userTo indicate that a parameter is nullable, add a question mark after the alias:
-- @param {String} $1:name? The name of the user (optional)Currently accepted types are Int, BigInt, Float, Boolean, String, DateTime, Json, Bytes, null, and Decimal.
Taking the example from above, the SQL file would look like this:
-- @param {Int} $1:minAge
-- @param {Int} $2:maxAge
SELECT id, name, age
FROM users
WHERE age > $1 AND age < $2The format of argument type definitions is the same regardless of the database engine.
:::note
Manual argument type definitions are not supported for array arguments. For these arguments, you will need to rely on the type inference provided by TypedSQL.
:::
Examples
For practical examples of how to use TypedSQL, please refer to the TypedSQL example in the Prisma Examples repo.
Limitations of TypedSQL
Supported Databases
TypedSQL supports modern versions of MySQL and PostgreSQL without any further configuration. For MySQL versions older than 8.0 and all SQLite versions, you will need to manually describe argument types in your SQL files. The types of inputs are inferred in all supported versions of PostgreSQL and MySQL 8.0 and later.
TypedSQL does not work with MongoDB, as it is specifically designed for SQL databases.
Active Database Connection Required
TypedSQL requires an active database connection to function properly. This means you need to have a running database instance that Prisma can connect to when generating the client with the --sql flag. TypedSQL uses the connection string defined in prisma.config.ts (datasource.url) to establish this connection.
Dynamic SQL Queries with Dynamic Columns
TypedSQL does not natively support constructing SQL queries with dynamically added columns. When you need to create a query where the columns are determined at runtime, you must use the $queryRaw and $executeRaw methods. These methods allow for the execution of raw SQL, which can include dynamic column selections.
Example of a query using dynamic column selection:
const columns = 'name, email, age'; // Columns determined at runtime
const result = await prisma.$queryRawUnsafe(
`SELECT ${columns} FROM Users WHERE active = true`
);In this example, the columns to be selected are defined dynamically and included in the SQL query. While this approach provides flexibility, it requires careful attention to security, particularly to avoid SQL injection vulnerabilities. Additionally, using raw SQL queries means foregoing the type-safety and DX of TypedSQL.
Acknowledgements
This feature was heavily inspired by PgTyped and SQLx. Additionally, SQLite parsing is handled by SQLx.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this change is necessary with our current setup. We’re using @prisma/adapter-better-sqlite3, and with that adapter the SQLite driver rejects positional placeholders like ?1 (and likewise named placeholders) at runtime — it throws “Too many parameter values were provided.” The same query works with plain ? placeholders, which is why the SQL now uses ? and the call passes both args explicitly.【F:/opt/cursor/artifacts/sqlite-param-test.log†L1-L16】【F:prisma/sql/searchUsers.sql†L1-L20】【F:app/routes/users/index.tsx†L11-L19】
This lines up with the TypedSQL docs caveat that @prisma/adapter-better-sqlite3 is not supported; so we need to use the form that the underlying driver accepts to keep TypedSQL working without swapping adapters.【F:/opt/cursor/artifacts/sqlite-param-test.log†L1-L16】【F:prisma/sql/searchUsers.sql†L1-L20】
|
@cursor, check CI and fix it please. |
Summary
Testing
|
Co-authored-by: me <me@kentcdodds.com>
|
@cursor, thanks! CI is still failing for the type checking and vitest with the following errors: Please figure out what's wrong. Feel free to look up docs for relevant libraries if needed. |
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
|
@cursor, the tests failed in CI again. Please make certain they're working before pushing: |
Summary
Testing
Notes:
Manual UI testing not run because this change is test/runtime infrastructure only and does not touch UI behavior. |
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Test Plan
npm installto ensure all new dependencies are installed.npm testto verify all unit and integration tests pass.npm run dev).Checklist
Screenshots
Note
Upgrade and refactors for DB, forms, and caching
PrismaBetterSqlite3adapter and CJS client import; updatesearchUserstyped SQL call to accept two params; add prisma/env/test setup tweaks@conform-to/zodimports to@conform-to/zod/v4and refactor validation (checkbox.coerce.boolean(), required fields via.superRefine, hidden-field handling) across auth, onboarding, profile, 2FA, and notes routesnode:sqliteviacreateRequire, centralize prepared statements, and fall back to in-memory LRU in test env; keep buffer (de)serializationuploadToStorageunstable_patternto auth callback tests; adjust 2FA verify refinement; minor route logic/typing cleanupsWritten by Cursor Bugbot for commit 468ed86. This will update automatically on new commits. Configure here.