Prisma Client Rust

website·Indexed 32 days ago

https://prisma.brendonovich.dev

A Rust-specific client for using Prisma in Rust projects. Documentation covers installation, setup, and data operations including find, create, update, delete, and upsert queries. It also provides guidance on advanced features such as pagination, relational fetching, transactions, batching, raw queries, mocking, and integration with rspc.

Tokens
12.2K
Snippets
80
Records
98
Agent score
63%

What's inside Prisma Client Rust

  1. Overview of Prisma Client Rust

    Prisma Client Rust is a community-developed client for using Prisma with Rust projects. While not an official Prisma product, it is supported via the Prisma FOSS Fund. It shares core concepts with the official Prisma documentation, so users are encouraged to refer to the main Prisma docs for general schema and database concepts.
  2. Overview of PCR Query Traits

    Query Traits (available since version 0.6.4) allow developers to modify query builders in a typesafe and decoupled manner. Instead of writing repetitive query functions for every specific query type, these traits provide a standardized way to add parameters to different types of query builders.
  3. Deploy pending migrations in production with _migrate_deploy

    Use PrismaClient::_migrate_deploy to apply all pending migrations that were previously generated via the Prisma CLI. This should be used after schema changes have been finalized and migrations have been created using migrate dev.
  4. Handle dynamic raw SQL queries using the `Raw` struct

    When SQL queries are constructed dynamically and cannot be used within the raw! macro, use the Raw struct. Create a Raw instance by calling Raw::new(query, values), where query is the SQL string and values is a Vec<PrismaValue>.
  5. Instantiate the Prisma Client in Rust

    The Prisma Client requires the tokio async runtime. Use PrismaClient::_builder().build().await to instantiate the client.

    If you need to connect to a specific database dynamically (e.g., for desktop apps where environment variables are impractical), use the .with_url() method on the builder.

    // Stops the client from outputting a huge number of warnings during compilation.
    #[allow(warnings, unused)]
    mod prisma;
     
    use prisma::PrismaClient;
    use prisma_client_rust::NewClientError;
     
    #[tokio::main]
    async fn main() {
        let client: Result<PrismaClient, NewClientError> = PrismaClient::_builder().build().await;
    }
  6. Implement cursor-based pagination

    Use the .cursor() method to start pagination from a specific record. The cursor method requires a unique filter as its argument. This is often used in combination with order_by for efficient pagination.
    use prisma::post;
     
    let posts: Vec<post::Data> = client
        .post()
        .find_many(vec![])
        .cursor(post::id::equals("abc".to_string()))
        .exec()
        .await?;
  7. Manage transactions manually

    For fine-grained control over commits and rollbacks, use client._transaction().begin(). This returns a tuple containing a TransactionManager and a dedicated PrismaClient. To finalize the transaction, call tx.commit(client) or tx.rollback(client). Note that these methods consume the transaction-specific client to prevent its use after the transaction ends.
    let (tx, client) = client
    	._transaction()
    	.begin()
    	.await?;
    
    // ... perform operations using the transaction client ...
    
    tx.commit(client).await?;
    // or 
    tx.rollback(client).await?;
  8. Use dedicated update and delete actions in v0.6.0

    Update and delete operations no longer chain off of find_unique or find_many. Instead, use the dedicated .update(), .delete(), .update_many(), and .delete_many() methods, passing the filter as the first argument.
    // Single record: Before
    client.user().find_unique(user::id::equals(0)).update(vec![])
    // Single record: After
    client.user().update(user::id::equals(0), vec![])
    
    // Multiple records: Before
    client.user().find_many(vec![user::name::contains("Brendan".to_string())]).delete()
    // Multiple records: After
    client.user().delete_many(vec![user::name::contains("Brendan".to_string())])
  9. Filter queries using model and field modules

    Prisma generates Rust modules for each model and field in the schema, following snake_case naming conventions. These modules provide functions to construct modifiers for filtering queries. To filter, pass these modifiers into the query method (e.g., find_many).
    use prisma::post;
     
    client
        .post()
        .find_many(vec![
            // Pattern: model::field::method()
            post::title::equals("Test".to_string())
        ])
        .exec()
        .await;
  10. Update relations in Prisma queries

    Relations can be modified using connect and disconnect within update queries for single records.

    Caveat: Using connect or disconnect within update_many will cause the query to return an error. To update relations for multiple records, you must set the relation's scalar fields (foreign keys) directly.

    // Single record: Disconnecting a relation
    use prisma::{comment, post};
     
    let updated_comment: comment::Data = client
        .post()
        .update(
            comment::id::equals("id".to_string()),
            vec![comment::post::disconnect()]
        )
        .exec()
        .await?;
    
    // Many records: Updating relation via scalar field (foreign key)
    let updated_comments_count: i64 = client
        .post()
        .update_many(
            vec![comment::post::is(
                post::id::equals("id".to_string())
            )],
            vec![comment::post_id::set("post".to_string())]
        )
        .exec()
        .await?;
  11. Define named types for selections to use outside queries

    By adding a name before the root selection in the macro, Prisma generates a module with that name containing a Data struct and a helper function. This allows you to use the selection's type in function signatures (e.g., as a return type).
    // Generates mod post_only_title { pub struct Data { title: String }, pub fn select() }
    post::select!(post_only_title {
        title
    })
    
    async fn do_query() -> Vec<post_only_title::Data> {
        client
            .post()
            .find_many(vec![])
            .select(post_only_title::select())
            .exec()
            .await
            .unwrap()
    }
  12. Execute transactions using closures

    Use client._transaction().run() to execute a series of queries within a transaction closure. The closure must return an async move block that accepts a dedicated PrismaClient instance and returns a Result. If the closure returns Ok, the transaction commits; if it returns Err, it rolls back.
    let (user, post) = client
    	._transaction()
    	.run(|client| async move {
    		let user = client
    			.user()
    			.create("brendan".to_string(), vec![])
    			.exec()
    			.await?;
     
    		client
    			.post()
    			.create(
    				"test".to_string(),
    				true,
    				vec![post::author::connect(
    					user::id::equals(user.id.clone()))],
    			),
    			.exec()
    			.await
    			// if query succeeds, return user + post from transaction
    			.map(|post| (user, post))
    	})
    	.await?;