JamalJ
Convex Communityโ€ข8mo agoโ€ข
10 replies
Jamal

Query/Mutation Error Handling & Transactional Integrity Advice

Hey Convex team and community!

We're working on improving the developer experience and type safety of our convex API by standardizing our response types, and we'd love your advice on the idiomatic "Convex way" to approach this. Our goal is to move away from implicitly throwing errors for business logic failures (e.g., "Document not found," "Permission denied") and toward a more explicit API contract.

Our initial idea was to adopt a Result type for all our queries and mutations, like Promise<Result<SuccessData, AppError>>. This would make our function signatures self-documenting and allow for type-safe error handling on the client without try/catch boilerplate for every call.

However, we then realized a critical detail: throwing an Error is fundamental to how Convex rolls back transactions in mutations. If we stop throwing and instead return an error object (e.g., { status: 'error', ... }), we're concerned we'll lose the automatic ACID guarantees for our failure paths, which is one of the features we love most about Convex.

So, our question for the team is:

1. What is the recommended pattern in Convex for returning structured, typed errors from queries and mutations?
2. Is there a way to use a Result-like pattern without breaking transactional rollbacks? For instance, is there a way to manually cancel a transaction within a mutation before returning the error object?
3. Or, is the best practice to create a custom AppError extends Error class,
throw
it from our functions, and then use type guards on the client to inspect the error caught in a try/catch block?

Any insight into best practices here would be super helpful. Thanks so much
Was this page helpful?