Documentation Index Fetch the complete documentation index at: https://mintlify.com/rjdellecese/confect/llms.txt
Use this file to discover all available pages before exploring further.
Quickstart
This guide will walk you through creating your first Confect application. You’ll build a simple notes app with a database schema, backend functions, and a React frontend - all with full type safety from end to end.
Prerequisites
Make sure you have Node.js 22+ and pnpm 10+ installed on your system.
You’ll also need:
Installation
Install Confect Packages
Install all required Confect packages along with their peer dependencies: pnpm add @confect/core @confect/server @confect/react @confect/cli
pnpm add effect convex react react-dom
pnpm add -D @types/react @types/react-dom typescript
Confect requires Effect 3.19.16+, Convex 1.30.0+, and React 18 or 19.
Initialize Convex
If you haven’t already, initialize Convex in your project: This creates a convex/ directory and convex.json configuration file.
Configure Convex Code Generation
Update your convex.json to enable static API and data model generation: {
"codegen" : {
"staticApi" : true ,
"staticDataModel" : true
}
}
Create Confect Directory Structure
Create a confect/ directory alongside your convex/ directory: mkdir confect
mkdir confect/tables
mkdir confect/spec
mkdir confect/impl
Define Your Database Schema
Create your first table using Effect Schema:
confect/tables/Notes.ts
confect/schema.ts
import { Table } from "@confect/server" ;
import { Schema } from "effect" ;
export const Notes = Table . make (
"notes" ,
Schema . Struct ({
text: Schema . String . pipe ( Schema . maxLength ( 100 )),
tag: Schema . optional ( Schema . String ),
})
)
. index ( "by_text" , [ "text" ]);
The schema uses Effect’s powerful validation - maxLength(100) ensures notes are never too long, and it’s validated at runtime automatically!
Define Your Function Specifications
Specify the API for your backend functions:
confect/spec/notes.ts
confect/spec.ts
import { FunctionSpec , GroupSpec , GenericId } from "@confect/core" ;
import { Schema } from "effect" ;
import { Notes } from "../tables/Notes" ;
export const notes = GroupSpec . make ( "notes" )
. addFunction (
FunctionSpec . publicMutation ({
name: "insert" ,
args: Schema . Struct ({ text: Schema . String }),
returns: GenericId . GenericId ( "notes" ),
})
)
. addFunction (
FunctionSpec . publicQuery ({
name: "list" ,
args: Schema . Struct ({}),
returns: Schema . Array ( Notes . Doc ),
})
)
. addFunction (
FunctionSpec . publicMutation ({
name: "delete_" ,
args: Schema . Struct ({ noteId: GenericId . GenericId ( "notes" ) }),
returns: Schema . Null ,
})
);
FunctionSpec defines the contract for your functions - their names, arguments, return types, and visibility (public/internal).
Implement Your Functions
Write the actual business logic using Effect and Confect services:
confect/impl/notes.ts
confect/impl.ts
import { FunctionImpl , GroupImpl } from "@confect/server" ;
import { Effect , Layer } from "effect" ;
import api from "../_generated/api" ;
import { DatabaseReader , DatabaseWriter } from "../_generated/services" ;
const insert = FunctionImpl . make (
api ,
"notes" ,
"insert" ,
({ text }) =>
Effect . gen ( function* () {
const writer = yield * DatabaseWriter ;
return yield * writer . table ( "notes" ). insert ({ text });
}). pipe ( Effect . orDie )
);
const list = FunctionImpl . make (
api ,
"notes" ,
"list" ,
() =>
Effect . gen ( function* () {
const reader = yield * DatabaseReader ;
return yield * reader
. table ( "notes" )
. index ( "by_creation_time" , "desc" )
. collect ();
}). pipe ( Effect . orDie )
);
const delete_ = FunctionImpl . make (
api ,
"notes" ,
"delete_" ,
({ noteId }) =>
Effect . gen ( function* () {
const writer = yield * DatabaseWriter ;
yield * writer . table ( "notes" ). delete ( noteId );
return null ;
}). pipe ( Effect . orDie )
);
export const notes = GroupImpl . make ( api , "notes" ). pipe (
Layer . provide ( insert ),
Layer . provide ( list ),
Layer . provide ( delete_ )
);
DatabaseReader and DatabaseWriter are Effect services that provide type-safe access to your Convex database with automatic schema validation.
Generate Code
Run the Confect CLI to generate TypeScript types and React hooks:
This generates:
confect/_generated/api.ts - API references for your functions
confect/_generated/refs.ts - Type-safe references for React hooks
confect/_generated/services.ts - Effect services for database access
And more!
Keep confect dev running during development - it watches for changes and regenerates types automatically.
Build Your React Frontend
Use the generated hooks in your React components:
import { useQuery , useMutation } from "@confect/react" ;
import { ConvexProvider , ConvexReactClient } from "convex/react" ;
import { Array } from "effect" ;
import { useState } from "react" ;
import refs from "../confect/_generated/refs" ;
const App = () => {
const convexClient = new ConvexReactClient ( import . meta . env . VITE_CONVEX_URL );
return (
< ConvexProvider client = { convexClient } >
< NotesApp />
</ ConvexProvider >
);
};
const NotesApp = () => {
const [ note , setNote ] = useState ( "" );
// Fully typed query - autocomplete and type checking!
const notes = useQuery ( refs . public . notes . list , {});
// Fully typed mutation
const insertNote = useMutation ( refs . public . notes . insert );
const deleteNote = useMutation ( refs . public . notes . delete_ );
const handleInsert = () => {
void insertNote ({ text: note }). then (() => setNote ( "" ));
};
if ( notes === undefined ) {
return < p > Loading ...</ p > ;
}
return (
< div >
< h1 > My Notes </ h1 >
< div >
< textarea
rows = { 4 }
cols = { 50 }
value = { note }
onChange = {(e) => setNote (e.target.value)}
placeholder = "Enter your note (max 100 chars)"
/>
< br />
< button onClick = { handleInsert } > Add Note </ button >
</ div >
< ul >
{ Array . map ( notes , ( note ) => (
< li key = {note. _id } >
< p >{note. text } </ p >
< button onClick = {() => void deleteNote ({ noteId : note . _id })} >
Delete
</ button >
</ li >
))}
</ ul >
</ div >
);
};
export default App ;
Notice how refs.public.notes.list provides full autocomplete and type checking - if you try to pass wrong arguments, TypeScript will catch it!
Run Your Application
Start your development servers:
Terminal (All-in-One)
Terminal (Separate)
pnpm concurrently \
--prefix-colors= 'cyan,yellow,green' \
--names= 'VITE,CONVEX,CONFECT' \
'vite' \
'convex dev --tail-logs' \
'confect dev'
The all-in-one command requires concurrently installed: pnpm add -D concurrently
Your application should now be running at http://localhost:5173!
What You’ve Built
Congratulations! You’ve created a fully type-safe notes application with:
Schema Validation Effect Schema validates all data at runtime and compile-time
Type-Safe API Full TypeScript types from database to React components
Effect Services Composable, testable business logic using Effect patterns
Real-Time Updates Automatic UI updates via Convex’s real-time infrastructure
Next Steps
Schema & Tables Learn about advanced schema features like indexes and vector search
Functions Explore queries, mutations, actions, and HTTP APIs
Services Use Effect services for database access and platform capabilities
React Integration Master the React hooks and client-side patterns