Skip to main content

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.

Components in Confect allow you to create modular, reusable backend functionality that can be shared across projects or within your application.
Components are an advanced feature that builds on Convex’s component system. This page provides an overview of how components work with Confect.

What are Components?

Components are self-contained modules that encapsulate:

Database Tables

Component-specific schemas and tables

Functions

Queries, mutations, and actions

Dependencies

Other components and packages

Configuration

Component-specific settings

Creating a Component

Define a component as a separate package:
component/schema.ts
import { DatabaseSchema, Table } from "@confect/server";
import { Schema } from "effect";

const commentsTable = Table.make(
  "comments",
  Schema.Struct({
    text: Schema.String,
    authorId: Schema.String,
    parentId: Schema.String,
    createdAt: Schema.Number,
  })
)
  .index("by_parent", ["parentId", "createdAt"]);

export const componentSchema = DatabaseSchema.make()
  .addTable(commentsTable);
component/api.ts
import { Api } from "@confect/core";
import { componentSchema } from "./schema";

export const api = Api.make({
  name: "comments",
  runtime: "Convex" as const,
  databaseSchema: componentSchema,
  groups: {
    comments: {
      list: FunctionSpec.query({
        args: Schema.Struct({ parentId: Schema.String }),
        returns: Schema.Array(
          Schema.Struct({
            id: Schema.String,
            text: Schema.String,
            authorId: Schema.String,
            createdAt: Schema.Number,
          })
        ),
      }),
      add: FunctionSpec.mutation({
        args: Schema.Struct({
          parentId: Schema.String,
          text: Schema.String,
        }),
        returns: Schema.Struct({ id: Schema.String }),
      }),
    },
  },
});

Using Components

Import and use components in your application:
confect/app.ts
import { CommentsComponent } from "@company/comments-component";
import { Effect } from "effect";

const getPostWithComments = (postId: string) =>
  Effect.gen(function* () {
    const db = yield* DatabaseReader<typeof databaseSchema>();
    const comments = yield* CommentsComponent.queries.list({ parentId: postId });
    
    const post = yield* db.table("posts").get(postId);
    
    return {
      post,
      comments,
    };
  });

Component Isolation

Components maintain isolation:

Separate Namespaces

Component tables don’t conflict with app tables

Type Safety

Full type safety across component boundaries

Independent Deploys

Components can be versioned and deployed separately

Encapsulation

Internal component details are hidden

Component Communication

Components communicate through defined interfaces:
// In component
export const api = Api.make({
  name: "notifications",
  groups: {
    notifications: {
      send: FunctionSpec.mutation({
        args: Schema.Struct({
          userId: Schema.String,
          message: Schema.String,
        }),
        returns: Schema.Struct({ sent: Schema.Boolean }),
      }),
    },
  },
});

// In app
const notifyUser = (userId: string, message: string) =>
  Effect.gen(function* () {
    const result = yield* NotificationsComponent.mutations.send({
      userId,
      message,
    });
    
    return result;
  });

Publishing Components

Components can be published as npm packages:
package.json
{
  "name": "@company/comments-component",
  "version": "1.0.0",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "dependencies": {
    "@confect/server": "^1.0.0",
    "effect": "^3.0.0"
  }
}

Example Components

Common component patterns:

Authentication Component

export const authComponent = Component.make({
  name: "auth",
  functions: {
    login: mutation({
      args: { email: Schema.String, password: Schema.String },
      returns: { token: Schema.String },
    }),
    logout: mutation({
      args: {},
      returns: { success: Schema.Boolean },
    }),
    getCurrentUser: query({
      args: {},
      returns: Schema.optional(User),
    }),
  },
});

Analytics Component

export const analyticsComponent = Component.make({
  name: "analytics",
  functions: {
    track: mutation({
      args: {
        event: Schema.String,
        properties: Schema.Record(Schema.String, Schema.Any),
      },
      returns: { tracked: Schema.Boolean },
    }),
    getStats: query({
      args: { period: Schema.String },
      returns: Stats,
    }),
  },
});

File Storage Component

export const storageComponent = Component.make({
  name: "storage",
  functions: {
    upload: mutation({
      args: { filename: Schema.String },
      returns: { uploadUrl: Schema.String },
    }),
    download: query({
      args: { fileId: Schema.String },
      returns: { url: Schema.String },
    }),
  },
});

Best Practices

1

Define Clear Interfaces

Make component APIs explicit and well-documented.
2

Version Components

Use semantic versioning for component packages.
3

Minimize Dependencies

Keep components loosely coupled and self-contained.
4

Test Thoroughly

Write comprehensive tests for component functionality.
Components are a powerful way to share functionality, but they add complexity. Use them when you need reusable, isolated modules.

Component Configuration

Components can accept configuration:
export const createEmailComponent = (config: {
  apiKey: string;
  fromAddress: string;
}) =>
  Component.make({
    name: "email",
    config,
    functions: {
      send: mutation({
        args: {
          to: Schema.String,
          subject: Schema.String,
          body: Schema.String,
        },
        handler: ({ to, subject, body }) =>
          Effect.gen(function* () {
            // Use config.apiKey and config.fromAddress
            yield* sendEmail(config, to, subject, body);
            return { sent: true };
          }),
      }),
    },
  });

Limitations

  • Components cannot directly access other components’ tables
  • Component communication happens through functions only
  • Components must be statically defined (no dynamic component loading)
Components are currently an experimental feature. The API may change in future versions.

Next Steps

Functions

Define component functions

Database

Create component schemas

Project Structure

Organize your components

Convex Components

Learn more about Convex components