Full Type Inference
Every $group, $project, and $addFields stage produces a precisely inferred output type. Chain 10 stages and the final type is exactly what MongoDB returns — no casts, no generics, no guessing.
Pipeline Type Flow
Each stage's output becomes the next stage's input. Rename a field in $project? The next $group immediately sees the new shape. Typos are caught before your code ever runs.
Zero Runtime Cost
All type checking happens at compile time. Your production bundle contains only the MongoDB queries you wrote — no wrappers, no reflection, no overhead.
Schema Agnostic
Works with Effect Schema, Zod, or plain TypeScript types. Bring your own validation library, or skip runtime validation entirely.
Full MongoDB 8.0+
Every aggregation stage, expression operator, accumulator, and window function — typed. $facet, $setWindowFields,$lookup with sub-pipelines, all covered.
Effect Integration
Optional first-class Effect.ts support. Get tagged errors, dependency injection, and composable pipelines for production applications.
Types Flow Through Your Pipeline
Each stage transforms the document shape. Sluice infers the output type at every step — no manual annotations needed.
import { registry, $match, $group, $project, $sort } from "sluice-orm";
import { Schema as S } from "@effect/schema";
const OrderSchema = S.Struct({
_id: S.String,
userId: S.String,
amount: S.Number,
items: S.Array(S.Struct({
name: S.String,
price: S.Number,
quantity: S.Number,
})),
status: S.Literal("pending", "paid", "shipped"),
createdAt: S.Date,
});
const db = registry("8.0", { orders: OrderSchema });
const { orders } = db(client.db("shop"));
// Every stage's output type flows to the next — fully inferred
const report = await orders
.aggregate(
$match($ => ({ status: "paid" })),
// ^? { _id: string; userId: string; amount: number; ... }
$group($ => ({
_id: "$userId",
totalSpent: $.sum("$amount"),
orderCount: $.sum(1),
})),
// ^? { _id: string; totalSpent: number; orderCount: number }
$project($ => ({
userId: "$_id",
totalSpent: 1,
orderCount: 1,
avgOrder: $.divide("$totalSpent", "$orderCount"),
_id: 0,
})),
// ^? { userId: string; totalSpent: number; orderCount: number; avgOrder: number }
$sort({ totalSpent: -1 }),
)
.toList();
// report: { userId: string; totalSpent: number; orderCount: number; avgOrder: number }[]
Without Sluice
// No type safety — runtime errors waiting to happen
collection.aggregate([
{ $group: { _id: "$departement", avg: { $avg: "$salray" } } },
// ^ typo ^ typo
// You won't know until production 💥
]);
With Sluice
// Compile-time safety — errors caught instantly
$group($ => ({
_id: "$departement", // ❌ TS Error: no field "departement"
avg: $.avg("$salray"), // ❌ TS Error: no field "salray"
}));
// Fix the typos, ship with confidence ✅