apps/blog/content/blog/wnip-q1-dsk0golh8v/index.mdx
Learn about everything in the Prisma ecosystem and community from January to March 2022.
undefined fields by defaultisSet filter operationunset operationupdateMany operationdeleteMany operation@default(dbgenerated()) is now replaced with @default(auto())@db.Array(ObjectId) is now updated to @db.ObjectIddataProxy and interactiveTransactions Preview features are now mutually exclusiveOur engineers have been working hard, issuing new releases with many improvements and new features. This quarter, we adjusted our release cadence to every three weeks. Here is an overview of the most exciting features we've launched in the last three months.
You can stay up-to-date about all upcoming features on our roadmap.
This quarter was packed with lots of new features and improvements to the MongoDB connector, now Generally Available. MongoDB was launched in Preview in July 2021 we've been working hard towards pushing it towards stability and polishing it to make it production-ready.
Here are some of the feature highlights we developed over this period:
prisma db pushIn 3.10.0 we introduced support for reading and modifying embedded documents. Embedded documents provide access to a new type keyword in your Prisma schema you can use to define composite types.
model Product {
id String @id @default(auto()) @map("_id") @db.ObjectId
name String
photos Photo[]
}
type Photo {
height Int
width Int
url String
}
Given the schema above, you can now read and write to the embedded photos array:
// Create a new product with an embedded list of photos
const product = await prisma.product.create({
data: {
name: "Forest Runners",
// Create an embedded list of photos in the product
photos: [
{ height: 100, width: 200, url: "1.png" },
{ height: 300, width: 400, url: "2.png" },
],
},
})
In 3.4.0, we added preview support for the introspection of embedded documents. Running prisma db pull against your MongoDB database will generate type definitions within your Prisma schema.
When introspecting your database, you can switch off the introspection of composite types with --composite-type-depth=0 or limit the depth level of the analysis with, for example, --composite-type-depth=2.
We took this further and updated the type inference behavior for embedded documents with mixed data types on introspection. From version 3.11.0, Prisma now defaults to a Json type for all fields that have mixed data types.
Prisma will also show a warning on the console and add a comment to the introspected Prisma schema file to clarify where a field has a mixed data type.
In 3.12.0, we added support for adding indexes on embedded document fields in MongoDB. You can now define a normal, unique, or full-text index in your schema.
type Address {
street String
number Int
}
model User {
id Int @id
email String
address Address
@@index([email, address.number])
@@unique([email, address.street])
@@fulltext([email, address.street])
}
In 3.11.0, we added the ability to filter embedded documents.
Given the following schema:
model Product {
id String @id @default(auto()) @map("_id") @db.ObjectId
photos Photo[]
}
model Order {
id String @id @default(auto()) @map("_id") @db.ObjectId
shippingAddress Address
billingAddress Address?
}
type Photo {
height Int
width Int
url String
}
type Address {
street String
city String
zip String
}
You can now add filters within an embedded document:
// find all orders with the same shipping address
const orders = await prisma.order.findMany({
where: {
shippingAddress: {
equals: {
street: "555 Candy Cane Lane",
city: "Wonderland",
zip: "52337",
},
},
},
})
You can also add a filter on a "contains many" relationship:
// find all products that don't have photos
const product = prisma.product.findMany({
where: {
photos: {
isEmpty: true
}
},
})
This is just the tip of the iceberg of what's possible. To learn more, refer to our documentation for a complete list of available operations.
In addition to embedded document filters, 3.11.0 added support for sorting by embedded documents.
Using the schema from the previous example, you can sort orders by their zip code:
// sort orders by zip code in ascending order
const orders = await prisma.order.findMany({
orderBy: {
shippingAddress: {
zip: "asc",
},
},
})
Raw queries in MongoDB help in writing queries Prisma doesn't support yet, such as:
// To find zero or more documents matching a filter
const result = await prisma.user.findRaw({
filter: { age: { $gt: 25 } },
options: { projection: { _id: false } },
})
// To perform aggregation operations on a collection
await prisma.user.aggregateRaw({
pipeline: [
{ $match: { status: 'registered' } },
{ $group: { _id: '$country', total: { $sum: 1 } } },
],
})
// To run a command against the database
await prisma.$runCommandRaw({
aggregate: 'User',
pipeline: [
{ $match: { name: 'Bob' } },
{ $project: { email: true, _id: false } },
],
explain: false,
})
The raw query API for MongoDB differs from Prisma's $queryRaw SQL API to handle some low-level differences between databases to give you a better API and developer experience.
You can enable query logging in Prisma Client when working with MongoDB from version 3.11.0.
const prisma = new PrismaClient({
log: [
{
emit: 'event',
level: 'query',
},
]
})
prisma.$on('query', (e) => console.log(e.query))
Prisma Client logs queries in the same format as the mongosh console. You can use the queries from your logs directly into your shell.
undefined fields by defaultIn 3.11.1, we've changed the data returned when filtering MongoDB documents on undefined fields. The new rule is that undefined fields are excluded by default unless explicitly filtered for. This allows you to query for undefined and null values separately.
For example, given the following Prisma schema:
model Address {
id Int @id @map("_id")
city String
street String? // Note that street is optional
}
For MongoDB, optional fields can either be null or undefined (absent). The following documents are all valid for the schema above:
{ "_id": 1, "city": "San Fransisco", "street": "Market st." }
{ "_id": 2, "city": "Seattle", "street": null }
{ "_id": 3, "city": "Chicago" }
Prior to 3.11.1, if you queried for where: { street: null }, you'd get _id: 2 and _id: 3. In 3.11.1, you'll only get _id: 2. The ability to also query for the missing fields has also been added. Refer to the new isSet below to learn more.
There are a few exceptions to this new default:
having filter on an aggregated field will return undefined fields. This is because aggregation on undefined fields yields null, not undefined, thus matching the filter.undefined) will currently include those relations in the result set.isSet filter operationTo compensate for missing fields on documents no longer being returned by the filters above, we’ve added a new isSet: bool filter. This filter can be used to include fields that are undefined on documents.
The isSet operation has been added to all scalar and embedded fields that are optional.
Using the example above, to include the undefined fields, you can use an OR:
await prisma.address.findMany({
where: {
OR: [
{ street: { isSet: false } },
{ street: null }
]
}
})
unset operationFrom 3.11.1, you can also remove a field with the unset operation.
Using the example above, let's write a query to remove the street field:
// set the `street` field to `undefined` in the database
await prisma.address.update({
where: {
id: 10,
},
data: {
street: {
unset: true,
},
},
})
updateMany operationPrisma now supports updating embedded documents that match specific criteria.
For example, given the following schema:
model Product {
id Int @id @map("_id")
name String @unique
photos Photo[]
}
type Photo {
height Int @default(200)
width Int @default(100)
url String
}
You would then update photo with a url of 1.png to 2.png:
const product = prisma.product.update({
where: {
id: 10,
},
data: {
photos: {
updateMany: {
where: {
url: '1.png',
},
data: {
url: '2.png',
},
},
},
},
})
deleteMany operationSimilar to updateMany, you can also remove embedded documents that match specific criteria.
Using the Prisma schema from the previous example, you can delete all photos with a height of 100:
const product = prisma.product.update({
where: {
id: 10,
},
data: {
photos: {
deleteMany: {
where: {
height: 100,
},
},
},
},
})
From 3.10.0, Prisma now enforces all the arguments in a MongoDB many-to-many relation. A @relation attribute must define fields and references argument to both sides.
The fields argument must point to a scalar field in the same model and must be an array. The references argument must point to a scalar field in the opposite model, and it must be a singular type of the same base type as the referencing array on the other side.
model Post {
id String @id @map("_id") @default(auto()) @db.ObjectId
category_ids String[] @db.ObjectId
- categories Category[] @relation(fields: [category_ids])
+ categories Category[] @relation(fields: [category_ids], references: [id])
}
model Category {
id String @id @map("_id") @default(auto()) @db.ObjectId
post_ids String[] @db.ObjectId
- posts Post[] @relation(fields: [post_ids])
+ posts Post[] @relation(fields: [post_ids], references: [id])
}
@default(dbgenerated()) is now replaced with @default(auto())The original purpose of dbgenerated is to support SQL expressions Prisma doesn’t understand yet. However, MongoDB doesn’t have a concept of default value expressions like SQL does. We took this opportunity to simplify how we handle the default values in MongoDB:
model Post {
- id String @id @default(dbgenerated()) @map("_id") @db.ObjectId
+ id String @id @default(auto()) @map("_id") @db.ObjectId
}
@db.Array(ObjectId) is now updated to @db.ObjectIdWe've adjusted the Prisma schema format for scalar lists with native types (like lists of Object IDs). This will likely affect those with many-to-many relationships in MongoDB. We made this change to align MongoDB with our existing SQL databases better:
model Post {
id String @id @default(auto()) @map("_id") @db.ObjectId
- categoryIDs String[] @db.Array(ObjectId)
+ categoryIDs String[] @db.ObjectId
categories Category[] @relation(fields: [categoryIDs], references: [id])
}
model Category {
id String @id @default(auto()) @map("_id") @db.ObjectId
- postIDs String[] @db.Array(ObjectId)
+ postIDs String[] @db.ObjectId
posts Post[] @relation(fields: [postIDs], references: [id])
}
In 3.12.0, we busted a ghost that has been bugging teams since the early days of the Prisma ORM. Under certain amounts of load, some people reported that the connection pool would sometimes drop connections or deadlock and not recover.
After many sightings and a lot of head-scratching, we could finally reproduce the issue. This allowed us to narrow down the problem to one of our dependencies and fix the problem.
CockroachDB is a distributed SQL database that shines in its ability to scale efficiently while maintaining developer agility and reducing operational overhead.
CockroachDB support is the product of collaboration with the Cockroach Labs team. You can use Prisma to introspect your existing database with db pull and evolve your schema and propagate changes to your database using Prisma Migrate.
Please give it a spin and let us know what you think to help us iron out the kinks before we push it to General Availability.
We rewrote our internal logger in 3.11.0 to reduce lock contention and enable future features like tracing. This is the first of many upcoming changes to improve Prisma Client's throughput.
We introduced 2 new CLI commands into Preview to improve the experience of troubleshooting migrations:
prisma migrate diffprisma db executeSince we pushed Prisma Migrate for General Availability last year, we've gotten a ton of feedback to understand the challenges developers experience when building, testing, and deploying migrations.
The prisma migrate diff command creates a diff of your database schema, Prisma schema file, or the migration history. You would have to feed the command with a from state and a to state to get a SQL script or human-readable diff in return.
As a companion to the prisma migrate diff, we also built prisma db execute to execute SQL scripts against a database. You can pipe the output from prisma migrate diff directly to prisma db execute --stdin.
Both commands are non-interactive, so it's possible to build many new workflows such as forward and backward migrations with some automation tooling.
prisma migrate diff using an exit codeWe also introduced a new --exit-code flag to the prisma migrate diff command to detect the state of a diff in several ways.
You can use the flag as follows:
npx prisma migrate diff --preview-feature \
--exit-code \
--from-[...] \
--to-[...]
These are the default and changed behaviour of the error codes:
## Default behavior of exit codes
0: Returned when the diff is empty or non-empty
1: Returned on error
## Changed behavior when --exit-code is used
0: Returned when the diff is empty
1: Returned on error
2: Returned when the diff is non-empty
In 3.8.0 we added Preview support for full-text search in MySQL. You can enable full-text support by adding the fullTextIndex and fullTextSearch Preview flags in your Prisma schema and defining @@fulltext() indexes on fields you'd like to use full-text search on.
generator client {
provider = "prisma-client-js"
previewFeatures = ["fullTextIndex", "fullTextSearch"]
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
model Post {
id Int @id @default(autoincrement())
title String @unique
@@fulltext([title])
}
Running prisma db push or prisma migrate dev will update your database schema, then running prisma generate will re-generate your Prisma Client, enabling you to use full-text search in your application.
// search for titles that contain cat, but not fox
await prisma.post.findMany({
where: {
title: {
search: "+cat -fox",
},
},
})
dataProxy and interactiveTransactions Preview features are now mutually exclusiveBefore 3.8.0, Prisma $transaction queries would fail whenever the Data Proxy and Interactive Transactions Preview features were used together. The interactiveTransactions and dataProxy Preview flags cannot be used together in this release. Generating the Prisma Client when both Preview features are enabled will throw an error.
We fixed a number of issues in version 3.9.0 around timeouts and rollbacks when there were concurrent reads and writes.
If you experienced timeouts or your interactive transactions weren't working quite as you expected, give it another go and let us know what you think. You can learn more about Interactive Transactions in our documentation.
We wouldn't be where we are today without our amazing community of developers. Our Slack has almost 50k members and is a great place to ask questions, share feedback and initiate discussions all around Prisma.
<Meetup title="Prisma Meetup Online #9: Remix edition" meetupLink="https://www.meetup.com/Berlin-Prisma-Meetup/events/283400662/" youtubeLink="https://youtu.be/D5XIMOzm3YE" talks={[ { title: 'Up and Running with Remix and the Prisma Data Proxy', author: 'Austin Crim', }, { title: 'Update While you Wait: Optimistic UI with Remix and Prisma', author: 'Chance Strickland', }, { title: 'Wrecking Remix Resource Routes', author: 'Alex Anderson', } ]} imagePath="/blog/posts/meetup-prisma.png" />
<Meetup title="Serverless Berlin Meetup – an online edition" meetupLink="https://www.meetup.com/Serverless-Berlin/events/283616972/" youtubeLink="https://youtu.be/jE4fk-rSGPw" talks={[ { title: 'Lessons from running AppSync in production', author: 'Yan Cui', }, { title: 'Designing Testable Serverless Applications', author: 'Aleksandar Simović', }, { title: 'Lambda Powertools', author: 'Heitor Lessa', }, ]} imagePath="/blog/posts/meetup-prisma.png" />
<Meetup title="GraphQL Berlin Meetup #25" meetupLink="https://www.meetup.com/graphql-berlin/events/283094727/" youtubeLink="https://youtu.be/srkTmlFNOyw" talks={[ { title: 'GraphQL caching demystified', author: 'Matteo Collina', }, { title: 'Diving into the new Nexus Prisma', author: 'Jason Kuhrt', }, { title: 'Federating the Content Layer with GraphCMS', author: 'Jamie Barton', }, ]} imagePath="/blog/posts/meetup-graphql.png" />
</MeetupList>Every other Thursday, Nikolas Burk, Sabin Adams, and Alex Ruheni discuss the latest Prisma release and other news from the Prisma ecosystem and community. If you want to travel back in time and learn about a past release, you can find all the shows from this quarter here:
<Youtube videoId="TGMF8ykS29M" />Some highlights of this quarter include the interviews with A-J Roos about prisma-redis-middleware, Michael Hayes about Pothos GraphQL, Peter Cilliers-Pistorius about seeding databases using Snaplet and Cerbos Authorization with Alex Olivier.
We published several videos this quarter on our YouTube channel. Check them out and subscribe to not miss out on future videos.
This quarter, we also recorded some interviews with Prisma employees:
We also published several videos showcasing how to work with Prisma and Planetscale:
<Youtube videoId="iaHt5_hg44c" /> <Youtube videoId="hPa2UaLV5jU" />Be sure to subscribe to our YouTube channel to not miss any videos in the future:
During this quarter, we published several technical articles on the Data Guide that you might find useful:
mysql_config_editor to manage MySQL credentialsWe also published several useful articles on our blog:
This quarter, several Prisma folks have appeared on external channels and livestreams. Here's an overview of all of them:
Also, we're hiring for various roles! If you're interested in joining us, check out our jobs page.
We love seeing laptops decorated with Prisma stickers, so we're shipping them for free to our community members! In this quarter, we've sent out over 500(!) sticker packs to developers that are excited about Prisma!
Prisma Day is back this year, and it'll be on June 15 - 16 at the James&June Sommergarten in Berlin. Join us in-person or online for talks and workshops on modern application development and databases. If you'd like to share your project or give a talk, feel free to submit a CFP.
The best places to stay up-to-date about what we're currently working on are GitHub issues and our public roadmap.
You can also engage in conversations in our Slack channel, and start a discussion on GitHub or join one of the many Prisma meetups around the world.
If you never want to miss any news from the Prisma community, follow us on X/Twitter.