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.
Confect provides Effect services that wrap Convex platform capabilities. These services give you type-safe, composable access to the database, file storage, scheduling, and authentication.
Available Services
DatabaseReader Query and read from the database
DatabaseWriter Insert, update, and delete documents
Storage Store and retrieve files
Scheduler Schedule functions to run later
Auth Access user authentication data
Accessing Services
Services are accessed in function implementations using Effect’s service pattern:
import { FunctionImpl } from "@confect/server" ;
import { Effect } from "effect" ;
import api from "../../_generated/api" ;
import { DatabaseReader } from "../../_generated/services" ;
export const list = FunctionImpl . make (
api ,
"notes" ,
"list" ,
() =>
Effect . gen ( function* () {
// Access the DatabaseReader service
const reader = yield * DatabaseReader ;
// Use the service
const notes = yield * reader
. table ( "notes" )
. index ( "by_creation_time" , "desc" )
. collect ();
return notes ;
}). pipe ( Effect . orDie ),
);
Services are automatically provided by Confect’s runtime. You don’t need to manually provide them in most cases.
DatabaseReader
The DatabaseReader service provides type-safe read access to your database.
Querying Tables
const reader = yield * DatabaseReader ;
// Get a single document by ID
const note = yield * reader
. table ( "notes" )
. get ( noteId );
// Query with an index
const recentNotes = yield * reader
. table ( "notes" )
. index ( "by_creation_time" , "desc" )
. take ( 10 );
Query Methods
Get a document by ID. const note = yield * reader . table ( "notes" ). get ( noteId );
// Returns: Effect<Document | null>
Query using an index. const query = reader
. table ( "notes" )
. index ( "by_creation_time" , "desc" );
Order: "asc" (default) or "desc"
Filter results with a predicate function. const filtered = yield * reader
. table ( "notes" )
. index ( "by_user" )
. filter (( q , note ) => q . eq ( note . status , "published" ))
. collect ();
Collect all results into an array. const allNotes = yield * reader
. table ( "notes" )
. index ( "by_creation_time" )
. collect ();
// Returns: Effect<Document[]>
Take the first N results. const firstTen = yield * reader
. table ( "notes" )
. index ( "by_creation_time" )
. take ( 10 );
// Returns: Effect<Document[]>
Get the first result. const firstNote = yield * reader
. table ( "notes" )
. index ( "by_creation_time" )
. first ();
// Returns: Effect<Option<Document>>
Get a unique result (for unique indexes). const user = yield * reader
. table ( "users" )
. index ( "by_email" )
. eq ( "email" , userEmail )
. unique ();
// Returns: Effect<Option<Document>>
System Tables
Access system tables like _storage:
const file = yield * reader
. table ( "_storage" )
. get ( storageId );
System tables are automatically included and type-safe.
DatabaseWriter
The DatabaseWriter service provides write access to your database.
Insert Documents
const writer = yield * DatabaseWriter ;
const noteId = yield * writer . table ( "notes" ). insert ({
text: "Hello, World!" ,
userId: user . id ,
tags: [ "greeting" ],
});
System fields (_id, _creationTime) are automatically added by Convex.
Update Documents
Patch (Partial Update)
// Update specific fields
yield * writer . table ( "notes" ). patch ( noteId , {
text: "Updated text" ,
});
// Remove a field by setting it to undefined
yield * writer . table ( "notes" ). patch ( noteId , {
userId: undefined ,
});
Replace (Full Update)
// Replace the entire document (except system fields)
yield * writer . table ( "notes" ). replace ( noteId , {
text: "New text" ,
userId: user . id ,
tags: [],
});
replace requires all non-optional fields. Use patch for partial updates.
Delete Documents
yield * writer . table ( "notes" ). delete ( noteId );
Writer Methods
Insert a new document. const id = yield * writer . table ( "notes" ). insert ({
text: "Hello" ,
});
// Returns: Effect<GenericId<TableName>>
Partially update a document. yield * writer . table ( "notes" ). patch ( noteId , {
text: "Updated" ,
});
// Returns: Effect<void>
Replace a document entirely. yield * writer . table ( "notes" ). replace ( noteId , {
text: "New" ,
userId: "123" ,
});
// Returns: Effect<void>
Delete a document. yield * writer . table ( "notes" ). delete ( noteId );
// Returns: Effect<void>
Storage
The Storage service provides access to Convex’s file storage.
StorageReader
Available in queries and mutations:
import { Storage } from "@confect/server" ;
const reader = yield * Storage . StorageReader ;
// Get a URL for a stored file
const url = yield * reader . getUrl ( storageId );
StorageWriter
Available in mutations:
const writer = yield * Storage . StorageWriter ;
// Generate an upload URL
const uploadUrl = yield * writer . generateUploadUrl ();
// Delete a file
yield * writer . delete ( storageId );
StorageActionWriter
Available in actions for direct file operations:
const storage = yield * Storage . StorageActionWriter ;
// Get file contents
const blob = yield * storage . get ( storageId );
// Store a file
const storageId = yield * storage . store ( blob );
Use StorageActionWriter for server-side file processing. For client uploads, use generateUploadUrl.
Scheduler
The Scheduler service allows you to schedule functions to run later.
Schedule After Duration
import { Scheduler } from "@confect/server" ;
import { Duration } from "effect" ;
import { api } from "../_generated/api" ;
const scheduler = yield * Scheduler ;
// Schedule a function to run in 1 hour
yield * scheduler . runAfter (
Duration . hours ( 1 ),
api . notes . sendReminder ,
{ noteId }
);
Schedule At Specific Time
import { DateTime } from "effect" ;
const scheduler = yield * Scheduler ;
// Schedule for a specific date/time
const tomorrow = DateTime . now (). pipe (
DateTime . addDays ( 1 )
);
yield * scheduler . runAt (
tomorrow ,
api . notes . archive ,
{ noteId }
);
Scheduled functions must be mutations or actions, not queries.
Auth
The Auth service provides access to user authentication.
Get User Identity
import { Auth } from "@confect/server" ;
const auth = yield * Auth ;
// Get the current user's identity
const identity = yield * auth . getUserIdentity ;
// Access user properties
const userId = identity . subject ;
const email = identity . email ;
const name = identity . name ;
Handle Unauthenticated Requests
import { Effect } from "effect" ;
const identity = yield * auth . getUserIdentity . pipe (
Effect . catchTag ( "NoUserIdentityFoundError" , () =>
Effect . fail ( new Error ( "User must be authenticated" ))
)
);
getUserIdentity fails with NoUserIdentityFoundError if the user is not authenticated.
Custom Claims
Access custom claims from your auth provider:
import { UserIdentity } from "@confect/core" ;
import { Schema } from "effect" ;
// Define custom claims schema
const CustomClaims = UserIdentity . UserIdentity (
Schema . Struct ({
role: Schema . Literal ( "admin" , "user" ),
})
);
// Access custom claims
const identity = yield * auth . getUserIdentity ;
const role = identity . role ; // "admin" | "user"
Service Composition
Services compose naturally with Effect:
import { FunctionImpl } from "@confect/server" ;
import { Effect } from "effect" ;
import api from "../../_generated/api" ;
import { DatabaseReader , DatabaseWriter , Auth } from "../../_generated/services" ;
export const create = FunctionImpl . make (
api ,
"notes" ,
"create" ,
({ text }) =>
Effect . gen ( function* () {
// Use multiple services together
const auth = yield * Auth ;
const reader = yield * DatabaseReader ;
const writer = yield * DatabaseWriter ;
// Get authenticated user
const identity = yield * auth . getUserIdentity ;
// Check user hasn't exceeded quota
const noteCount = yield * reader
. table ( "notes" )
. index ( "by_user" )
. eq ( "userId" , identity . subject )
. collect ()
. pipe ( Effect . map (( notes ) => notes . length ));
if ( noteCount >= 100 ) {
return yield * Effect . fail ( new Error ( "Note quota exceeded" ));
}
// Create the note
const noteId = yield * writer . table ( "notes" ). insert ({
text ,
userId: identity . subject ,
});
return noteId ;
}). pipe ( Effect . orDie ),
);
Service Availability
Different services are available in different function types:
Service Query Mutation Action Node Action DatabaseReader ✅ ✅ ❌ ❌ DatabaseWriter ❌ ✅ ❌ ❌ StorageReader ✅ ✅ ❌ ❌ StorageWriter ❌ ✅ ❌ ❌ StorageActionWriter ❌ ❌ ✅ ❌ Scheduler ❌ ✅ ❌ ❌ Auth ✅ ✅ ✅ ✅
Actions and Node actions can call mutations to access the database indirectly.
Error Handling
Services return Effects that can fail:
const note = yield * reader
. table ( "notes" )
. get ( noteId )
. pipe (
Effect . flatMap (( maybeNote ) =>
maybeNote === null
? Effect . fail ( new Error ( "Note not found" ))
: Effect . succeed ( maybeNote )
)
);
Or use Option for nullable results:
import { Option } from "effect" ;
const maybeNote = yield * reader
. table ( "notes" )
. index ( "by_creation_time" )
. first ();
// Returns: Option<Document>
if ( Option . isSome ( maybeNote )) {
const note = maybeNote . value ;
// Use note
}
Next Steps
Schema Restrictions Learn about Effect schema limitations in Confect
Spec-Impl Model Understand how to use services in implementations