Convex 1.3 brings a new callback-based JavaScript client for using Convex in non-React apps and a way to propagate error information for better error handling UX. We’ve also made a bunch of improvements to our CLI and runtime!
Callback-based JavaScript client
The reactive paradigm is a great fit for Convex subscriptions but not everyone uses React, the library. There’s a new callback-based Convex JavaScript client for subscribing to queries and running mutations and actions; anything you’d do with the ConvexReactClient but with 100% fewer hooks!
import { ConvexClient } from "convex/browser";
import { api } from "./convex/_generated/api.js";
const client = new ConvexClient(process.env["CONVEX_URL"]);
client.onUpdate(api.messages.list, {}, (messages) => {
// do whatever you want with messages
console.log(messages);
});
This new client works the same way in browsers and Node.js.
Sometimes you just want your query results with no fuss: no installing packages, no build process, no TypeScript. A minimal HTML file watching a Convex query looks like this:
<!DOCTYPE html>
<script src="https://unpkg.com/convex@1.3.1/dist/browser.bundle.js"></script>
<script>
const client = new convex.ConvexClient("CONVEX_URL_GOES_HERE");
client.onUpdate("messages:list", {}, (messages) => {
// do whatever you want with messages
console.log(messages);
});
</script>
Use ConvexError
to build error UIs
Before 1.3 all errors thrown from queries, mutations and actions looked the same to client applications talking to a production deployment: A plain Error
with no additional information to discern what caused the error.
With the new version, you can now propagate data alongside thrown errors by throwing a ConvexError
:
// convex/muFunctions.ts
import { mutation } from "./_generated/server";
import { v } from "convex/values";
export const createTask = mutation({
args: { text: v.string() },
handler: async (ctx, args) => {
if (text.length === 0) {
// This data will be accessible from the client
throw new ConvexError({
message: "Empty text not allowed"
severity: "FATAL",
});
}
const newTaskId = await ctx.db.insert("tasks", { text: args.text });
return newTaskId;
},
});
You can propagate data from queries, mutations and actions called from other actions as well.
// src/App.tsx
import { ConvexError } from "convex/values";
import { useMutation } from "convex/react";
import { api } from "../convex/_generated/api";
export function MyApp() {
const doSomething = useMutation(api.myFunctions.mutateSomething);
const handleSomething = async () => {
try {
await doSomething();
} catch (error) {
const errorMessage =
error instanceof ConvexError
? (error.data as { message: string }).message
: "Unexpected error occurred";
// ...
}
};
// ...
}
Check out the docs for full details.
Server logs in the terminal
You can now run npx convex dev --tail-logs
to see console.log
s run inside queries, mutations, actions and even HTTP actions!
Other improvements
ConvexReactClient
'squery()
method now rejects if the query function threw.- NPM libraries can now target the Convex default runtime specifically with the
convex
condition:
// package.json
{
"exports": {
".": {
"convex": "./forConvex.js"
"default": "./index.js"
},
},
}
- We’ve added
ReadableStream
,EventTarget
and a bunch of other browser APIs to the Convex default runtime, you can now find the full list of Supported APIs in our docs.