Guides
Service-layer patterns
How to keep services thin and explicit on top of better-drizzle.
better-drizzle works best when the service layer stays thin and explicit. The delegate API already removes the query glue, so services should add domain meaning, not re-wrap calls for their own sake.
A good read
Compose exactly the shape the caller needs, once:
export function getUserProfile(userId: number) {
return client.users.findUnique({
where: { id: userId },
include: {
posts: {
select: { id: true, title: true, published: true },
},
},
});
}A good write
Let .throw() carry the not-found decision so the happy path stays linear:
export function renameUser(userId: number, name: string) {
return client.users
.update({ where: { id: userId }, data: { name } })
.throw(() => new Error('User not found'));
}A good transactional service
Keep multi-step writes inside a single transaction:
export function createUserAndDraft(input: {
email: string;
name: string;
title: string;
}) {
return client.transaction(async (tx) => {
const user = await tx.users.create({
data: { email: input.email, name: input.name, active: true },
});
return tx.posts.create({
data: { authorId: user.id, title: input.title, published: false },
});
});
}What to avoid
- wrapping every delegate call in another helper without adding real value
- hiding query shape so far away that callers cannot see payload cost
- rebuilding pagination, relation loading, or not-found behavior by hand in every service