Generate type-safe ReScript bindings from your Convex backend functions. This tool parses your Convex TypeScript code using the TypeScript AST and generates idiomatic ReScript modules with proper types and React hooks.
- π― Type-safe bindings - Generates fully typed ReScript modules from Convex functions
- π React hooks - Ready-to-use
useQuery
anduseMutation
hooks - π¦ Schema support - Parses Convex schema to generate document types
- π AST-based parsing - Uses TypeScript compiler API for accurate parsing
- β‘ Blazing fast - Powered by Bun runtime (10x faster than Node.js)
- ποΈ Watch mode - Auto-regenerate on file changes with @parcel/watcher
- π¨ Idiomatic ReScript - Generates clean, idiomatic ReScript code
- π‘οΈ Edge case handling - Handles reserved keywords, polymorphic variants
This package is designed to run with Bun for best performance:
# Recommended: Install with Bun bun add -D convex-rescript-codegen # Also works with npm/yarn/pnpm (requires Bun runtime) npm install -D convex-rescript-codegen
Note: This package requires Bun runtime to be installed on your system.
- Install Bun if you haven't already:
curl -fsSL https://bun.sh/install | bash
- Add to your package.json scripts:
{ "scripts": { "codegen": "bunx convex-rescript", "codegen:watch": "bunx convex-rescript --watch" } }
- Run the generator:
bun run codegen # or directly with bunx convex-rescript
- Use the generated bindings:
// Your generated bindings are in src/bindings/generated/ open ConvexTypes @react.component let make = () => { // Use generated query hook let users = Convex_users.Query_list.use(~limit=Some(JSON.Encode.int(10))) // Use generated mutation hook let createUser = Convex_users.Mutation_create.use() // Type-safe function calls let handleCreate = async () => { let userId = await createUser({ name: "Alice", email: "alice@example.com" }) Console.log2("Created:", userId) } // Render your UI... }
src/bindings/generated/ βββ ConvexTypes.res # Document types from schema βββ ConvexBindings.res # Core Convex React bindings βββ ConvexGenerated.res # Module exports βββ Convex_users.res # Bindings for users.ts βββ Convex_rooms.res # Bindings for rooms.ts βββ Convex_messages.res # Bindings for messages.ts
convex-rescript [options] Options: -i, --input <path> Input directory (default: ./convex) -o, --output <path> Output directory (default: ./src/bindings/generated) -w, --watch Watch for changes -v, --verbose Verbose output -h, --help Show help
# Basic usage (uses defaults) convex-rescript # Watch mode convex-rescript --watch # Custom paths convex-rescript -i ./backend/convex -o ./frontend/src/bindings # Verbose output for debugging convex-rescript --verbose
Convex Type | ReScript Type |
---|---|
v.string() | string |
v.number() | float |
v.boolean() | bool |
v.null() | unit |
v.id("table") | tableId (e.g., usersId ) |
v.optional(...) | option<...> |
v.array(...) | array<...> |
v.object(...) | JSON.t |
v.any() | JSON.t |
v.union(...) | Polymorphic variants |
v.literal(...) | Polymorphic variants |
module Query_list = { type output = array<usersDoc> @module("convex/react") external useQuery: ('api, unit) => option<output> = "useQuery" let use = () => { useQuery(api["users"]["list"], ()) } }
module Query_getById = { type input = { userId: usersId, } type output = option<usersDoc> @module("convex/react") external useQuery: ('api, input) => option<output> = "useQuery" let use = (~userId) => { useQuery(api["users"]["getById"], { userId: userId }) } }
module Mutation_create = { type input = { name: string, email: string, avatar: option<string>, } type t = input => promise<usersId> @module("convex/react") external useMutation: 'api => t = "useMutation" let use = () => { useMutation(api["users"]["create"]) } }
type usersDoc = { @as("_id") id: usersId, @as("_creationTime") creationTime: float, name: string, email: string, avatar: option<string>, status: [| #online | #offline | #away], lastSeen: float, createdAt: float, }
The generator automatically escapes ReScript reserved keywords:
type
βtype_
with@as("type")
private
β#"private"
in polymorphic variants
Convex string unions are converted to ReScript polymorphic variants:
// Convex status: v.union( v.literal("online"), v.literal("offline"), v.literal("away") )
// ReScript status: [| #online | #offline | #away]
- Bun runtime 1.0+ (required) - Install Bun
- ReScript v11+ (tested with v12 RC)
- Convex backend with TypeScript functions
- React project (for the generated hooks)
- Complex nested types - Very complex nested object types default to
JSON.t
- Function return types - Currently infers common patterns, not all return types
- Convex validators - Complex validator compositions may not be fully supported
- Actions - Limited support for Convex actions (treated similar to mutations)
- Full return type inference from function implementations
- Support for Convex HTTP endpoints
- Custom type mapping configuration
- Generate mock data for testing
- VS Code extension for instant generation
- Support for convex-helpers library patterns
MIT
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
- Built for the ReScript and Convex communities
- Inspired by GraphQL Code Generator and similar tools
- Uses ts-morph for robust TypeScript AST parsing
Made with β€οΈ for type-safe full-stack development