DB client libs really should have this built in. Even the heavy-handed things wrapping them don't. I have my own that I copy into every new project with things like xactReadWrite(innerFunc) and xactReadOnly(innerFunc), where the read-only randomly picks from a pool of connections to read replicas.
Determining whether a query is appropriate to hit the read replica is something I've never seen done automatically and well. But if you design your system from the beginning that when you make queries the developer chooses whether to hit the reader or writer by way of essentially do_query / do_query_reader then it's a million times easier scale reads and it doesn't have to come with any additional operational complexity when you're small you can just have both handles point to the writer but have a read-only user or set TRANSACTION READ ONLY on the session.
But in practice, you want to fix a bug in chinese tokenization, or OpenAI releases the next version of its embeddings, or you want to add a few synonyms, or change the aggressiveness of the stemmer.
Then you have to rewrite your whole search index, and if its part of your primary db, you're pretty sad.