Skip to main content

Writing Convex Functions

Defining Convex Functions

Functions exported from files inside the convex/ folder wrapped in query or mutation calls comprise the public API of a Convex application.

You can invoke a file's default export by passing its file path relative to the convex/ folder to a query-calling function like useQuery. Be sure to leave off the extension. Here's an example:

convex/folder_name/file_name.ts
export default query(...);
App.tsx
// Call "folder_name/file_name.ts"'s default export.
useQuery("folder_name/file_name");

You can call non-default exports too. Add a colon and the exported name to the end of the path.

convex/folder_name/file_name.ts
export const exportedName = query(...);
App.tsx
// Call `exportedName` within "folder_name/file_name.js"
useQuery("folder_name/file_name:exportedName");

Function Signatures

Since Convex functions run on the server, subscribing to a query or invoking a mutation requires serializing arguments and return values.

Convex automatically handles serialization and deserialization of arguments and return values. Convex supports a superset of JSON: See Types and Schemas for the supported types. In particular, functions, instances of user-defined classes, and undefined aren't supported.

When the server runs your Convex functions, it provides an additional first argument: a QueryCtx for queries or a MutationCtx for mutations. These context objects provide capabilities to query the database via the db property or check client authentication with the auth property.

Convex query functions can't evaluate to undefined on the client, since undefined isn't a valid Convex value and is used on the client to signal that a query has not yet returned a value. If a Convex function returns undefined, the server will translate this value to null.

// in convex/getUserByEmail.ts a function can return `undefined`...
export default query(async ({ db }, email: string) => {
const user = db
.table("users")
.filter(q => q.eq(email, q.field("email")))
.first();
if (user) {
return user.name;
}
});
// ...but in a React component this value will be translated to null.
const messages: Message[] | undefined | null = useQuery("getUserByEmail");

Environment

Convex functions have access to fewer built-in APIs than JavaScript running in browsers or in Node.js. Nearly all JavaScript global objects are available. But web APIs like fetch and window are absent, as are Node.js APIs like process and fs.

The implementation of some objects differs from stock V8, the JavaScript engine Convex uses to execute these functions, in order to run these functions deterministically.

Using Libraries

When you push Convex functions with npx convex push, the CLI tool bundles your functions to include any libraries they may use. Many libraries just work, but if a library depends on Node.js or browser-specific capabilities, it probably won't.

If there's a library you'd like to use that isn't working for you, let us know!

Convex functions, once bundled with dependencies, need to be less than 8MB. If you're running into this size limit, you can use npx convex push -v to print out each source file's bundled size.

Sharing Code with Your Frontend

Types and functions from your UI code can be imported and used by Convex functions.

import { FeedEntry, formatFeedEntry } from "../src/feedTypes.ts"

export default query(async ({ db }) => {
const entries: FeedEntry[] = [];
...
const formatted = formatEntry(entry)
...
});

Importing modules from your UI could lead you to inadvertently include code that can't run in the Convex environment. If you encounter type errors like Cannot find name 'window' or Cannot redeclare block-scoped variable 'console'. you may need to refactor your UI code to pull code you'd like to share out into separate files that can be imported by both UI and Convex function code.

Avoid importing Convex functions from UI code

Backend code in Convex functions might contain code you'd like to keep private. Importing Convex functions in UI code may cause your bundler to include this code in the frontend bundle, potentially exposing secret logic to users.