Reading Data
Select & Include

Select & Include

Using with/fetch for fetching relations is okay, but leaves something to be desired in terms of type safety. The select & include query builder functions & macros can help with this, providing an exact type for each field and relation you choose to fetch.

Select also provides the ability to only fetch specific fields, whereas include will fetch all scalar fields and any specified relations.

The examples use the following schema:

model Post {
    id        String   @id @default(cuid())
    title     String
 
    comments Comment[]
}
 
model Comment {
    id        String   @id @default(cuid())
    content   String
 
    post   Post   @relation(fields: [postID], references: [id])
    postID String
}

Setup

select! and include! rely on recursion and call each other internally, so they have to be aware of the module path of the generated client. By default this is crate::prisma, which assumes your client is generated to a file named prisma.rs and sits at the root of the crate it is in. If you have configured your client to have a different name or be located somewhere else, you will need to provide this location through the module_path generator option.

module_path is a Rust path relative to crate that points to your generated client.

generator client {
    provider      = "cargo prisma"
    output        = "./src/generated/db.rs"
    // `select` macros will now point to `crate::generated::db`
    // instead of `crate::prisma`
    module_path   = "generated::db"
}

The Basics

select! and include! are very similar in syntax, with their only difference being what they do and don't fetch. Every model module contains select! and include! macros, the result of which can be provided to their respective query builder functions.

Fields to fetch can be specified as a space separated list inside {}:

post::select!({
    id    // select can pick individual fields
    title
})
 
// Above will generate
struct Data {
    id: String,
    title: String,
}
post::include!({
    comments // include can only pick relations
})
 
// Above will generate
struct Data {
    id: String,
    title: String,
    comments: Vec<comment::Data>
}

Nested Selections

select and include can also be applied while fetching a relation, to any depth in fact. Simply add a :, specify whether you want to select or include on the relation, and add your selection:

// Nested include inside select
post::select!({
    comments: include {
        post
    }
})
 
// Above will generate
struct Data {
    comments: comments::Data // refers to below module
}
 
// Module + data struct generated for nested selection
mod comments {
    pub struct Data {
        post: post::Data
    }
}
// Nested select inside include
post::include!({
    comments: select {
        content
    }
})
 
// Above will generate
struct Data {
    id: String,
    title: String,
    comments: comments::Data // refers to below module
}
 
// Module + data struct generated for nested selection
mod comments {
    pub struct Data {
        content: String
    }
}

Many Relation Options

When fetching many-relations, the fetching statement can act as an equivalent call to model::field::fetch, allowing for filtering, pagination and ordering to occur. This works in select! and include!.

post::select!({
    // Equivalent to post::comments::fetch(..) ..
    comments(vec![comment::content::contains("prisma".to_string())])
        .skip(5)
        .take(10): select { // You can add on nested selections too!
        id
        content
    }
})

Usage in Queries

Just pass the result of select! or include! to an equivalent query builder function:

// Type is anonymous and does not exist outside of the call to `include`
let posts: Vec<_> = client
    .post()
    .find_many(vec![])
    .include(post::include!({
        comments: select {
            id
        }
    }))
    .exec()
    .await?;
 
// Generated type is equivalent to
struct Data {
    id: String,
    title: String,
    comments: comments::Data
}
 
mod comments {
    pub struct Data {
        id: String
    }
}

Using Types Outside Queries

In some cases it can be useful to access the type generated by select! and include! outside of the call to the query builder functions, for example if you want to return the result of a query from a function.

This can be done by adding a name before the root selection. This will cause a module to be generated with that name and will contain a Data struct as well as either an include or select function, depending on what macro you use.

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()
}
 
// Generated type is equivalent to
pub mod post_only_title {
    pub struct Data {
        title: String
    }
 
    pub fn select() // return type is an internal detail
}

Passing Arguments

When performing a selection inline, outside values can be used as arguments just fine since they can be captured from outside the macro. When a selection is declared outside of a query builder function, this context cannot be captured. This is why the select and include functions aren't just structs, they can be passed arguments defined within the macros using the following syntax:

 
post::include!((filters: Vec<comment::WhereParam>, skip: i64, take: i64) => post_with_comments {
    comments(filters).skip(skip).take(take)
})
 
async fn do_query() -> Vec<post_only_title::Data> {
    let filters = vec![comment::content::contains("prisma".to_string())];
    let skip = 5;
    let take = 5;
 
    client
        .post()
        .find_many(vec![])
        .select(post_only_title::select(filters, skip, take))
        .exec()
        .await
        .unwrap()
}
 
// Generated type is equivalent to
pub mod post_with_comments {
    pub struct Data {
        id: String,
        title: String,
        comments: Vec<comment::Data>
    }
 
    pub fn select(filters: Vec<comment::WhereParam>, skip: i64, take: i64) // return type is an internal detail
}