Skip to content
masterclassBuddyTech Buzz5 fresh blogs · 5:00 AM IST

Technology handbook

Prisma

Schema modelling, generated client, relations, migrations, transactions and production operation.

Topics
24
Sections
4

Schema & modelling

Datasource and generator

Declare the database connection and the client the schema generates.

  • One block names the database, another names the client to generate.
  • The generated client is a build artifact, so it must be regenerated after a schema edit.
  • The connection URL is read at generation and at runtime, which are different moments.

Models and fields

Map a model to a table with typed, explicitly nullable fields.

  • A model maps to a table, and every field states its type and nullability.
  • Optional in the schema is nullable in the database, not merely absent from a write.
  • Renaming a field renames the column unless a mapping keeps the old name.

Attributes and defaults

Set primary keys, defaults, unique constraints and database-level mappings.

  • Keys, defaults, unique constraints and column mappings are declared as attributes.
  • A default declared here is applied by the database, not by the client.
  • A mapping is what lets a friendly field name sit over an existing column name.

Relations

Express one-to-one, one-to-many and many-to-many with explicit relation fields.

  • One-to-one, one-to-many and many-to-many are expressed with explicit relation fields.
  • The scalar field holds the key; the relation field is how the client traverses it.
  • An implicit many-to-many creates a join table you never named.

Enums and composite types

Model a constrained set of values and reuse a shared field shape.

  • A constrained value set becomes a real database enum where the dialect has one.
  • Adding a value is safe; removing one breaks rows that still hold it.
  • A composite type reuses a field shape without creating another table.

Indexes

Add indexes for the queries a feature actually runs, not for every column.

  • Indexes are added for the queries a feature runs, not for every column.
  • A composite index only helps when the query filters on its leading columns.
  • Every index is a cost on write, so an unused one is pure overhead.

Querying

findMany and filtering

Compose where conditions the client types check before the query runs.

  • Conditions compose as objects the generated types check before the query runs.
  • A typo in a field name fails at compile time rather than returning nothing.
  • An empty filter object matches everything, which is rarely what was meant.

select and include

Return only needed columns and relations instead of whole rows by default.

  • Only the needed columns and relations come back instead of whole rows.
  • Without either, every scalar column is selected by default.
  • select and include cannot both be used at the same level.

Nested writes

Create or connect related records in a single, consistent operation.

  • Related records are created or connected in one consistent operation.
  • It runs in a single transaction, so a partial write cannot survive.
  • connect fails if the target does not exist, which is the check you want.

Pagination

Choose cursor pagination for stable paging over offset for arbitrary jumps.

  • Cursor paging is stable under concurrent inserts; offset paging is not.
  • Offset is right for jumping to an arbitrary page and wrong for a feed.
  • A deep offset makes the database count rows it then discards.

Aggregations and groupBy

Count, sum and group in the database rather than in application memory.

  • Counting, summing and grouping happen in the database rather than in memory.
  • Pulling rows to aggregate in the application moves data that is then thrown away.
  • Grouped queries must name every non-aggregated field they select.

Raw queries

Drop to SQL safely with parameterized queries when the client cannot express it.

  • SQL is available, parameterised, for what the client cannot express.
  • Parameters are bound rather than interpolated, which is what keeps input out of the statement.
  • The result is untyped, so its shape is asserted rather than inferred.

Migrations

migrate dev

Generate and apply a migration during development from a schema change.

  • A schema change becomes a migration file and is applied locally.
  • It can reset the database when history and schema have diverged, so it is development only.
  • It uses transactions to read the current state, which pooled connections can break.

migrate deploy

Apply pending migrations in CI or production without generating new ones.

  • Pending migrations are applied without generating anything new.
  • It is the documented command for CI and production.
  • Behind PgBouncer it must use the direct connection, since the Schema Engine needs a single connection.

Expand and contract

Ship a breaking column change as separate additive, backfill and cleanup steps.

  • A breaking column change ships as additive, backfill and cleanup steps.
  • Each step leaves the old and new code paths both working.
  • Doing it in one step means the deploy and the migration must be simultaneous.

Baselining

Adopt Prisma on a database that already has tables without recreating them.

  • An existing database is adopted without recreating its tables.
  • The first migration is marked applied rather than actually run.
  • Skipping this makes the tool try to create tables that already exist.

Drift detection

Recognize when the database no longer matches the migration history.

  • The tool reports when the database no longer matches the migration history.
  • Drift usually means someone changed the schema outside the migrations.
  • Resolving it by resetting destroys data, so it is the wrong reflex in production.

Seeding

Populate reference data reproducibly and keep it separate from test fixtures.

  • Reference data is populated reproducibly and kept apart from test fixtures.
  • Seeds must be idempotent, because they will run more than once.
  • Mixing fixtures into seeds puts test rows into a real environment.

Transactions & production

Interactive transactions

Run dependent reads and writes atomically with an explicit timeout.

  • $transaction(async tx => …) takes maxWait, timeout and isolationLevel.
  • The documented defaults are 2000ms to acquire and 5000ms to run.
  • Every query must use the tx handle, or it runs outside the transaction.

Batch transactions

Submit independent operations together when none depends on another's result.

  • Independent operations are submitted together when none needs another's result.
  • There is no round trip between them, which is where the speed comes from.
  • A later operation cannot read what an earlier one in the batch wrote.

Isolation levels

Choose an isolation level deliberately when concurrent writes can interleave.

  • isolationLevel is set per transaction, defaulting to the database's own setting.
  • Prisma.TransactionIsolationLevel.Serializable is the strictest option.
  • The docs note isolation levels are not supported on MongoDB.

Connection pooling

Size the pool for the runtime and pool externally on serverless platforms.

  • DATABASE_URL carries the pooled string for runtime queries.
  • DIRECT_URL bypasses the pooler, which the CLI needs for migrations and introspection.
  • In Prisma 7 the runtime client is constructed with a driver adapter such as @prisma/adapter-pg.

Client lifecycle

Reuse a single client across requests and shut it down cleanly.

  • One client is reused across requests and shut down cleanly.
  • A client per request exhausts connections under any real load.
  • Hot reload in development creates clients repeatedly unless one is cached.

Query logging

Log slow queries and inspect the SQL the client actually emitted.

  • Slow queries are logged and the emitted SQL inspected.
  • Seeing the real SQL is what explains a query the types made look cheap.
  • Logging every query in production is itself a performance cost.

Sources

  • Prisma official documentation

Ask follow-up questions on any topic in your dashboard.

Start learning