| name | type-boundary-patterns |
| description | Vendor type boundaries ($Infer, third-party APIs). Parse at boundary, never assert. |
| allowed-tools | Read, Write, Edit |
| token-budget | 1500 |
| version | 1.0.0 |
Type Boundary Patterns
The Problem
Vendors like BetterAuth provide $Infer types:
type Session = typeof auth.$Infer.Session;
This gives compile-time shape. Runtime data is still UNTRUSTED until parsed.
The Lie
// DANGEROUS
const session = await auth.api.getSession({ headers });
return session as AuthSession; // LIES TO COMPILER
The Solution
import { Schema, Effect } from "effect";
// 1. Define Schema matching expected shape
// CRITICAL: Use DateFromSelf for vendor Date objects, Date for ISO strings
const AuthSessionSchema = Schema.Struct({
session: Schema.Struct({
id: Schema.String,
userId: Schema.String,
token: Schema.String,
expiresAt: Schema.DateFromSelf, // Vendor returns Date, not string
}),
user: Schema.Struct({
id: Schema.String,
email: Schema.String,
phoneNumber: Schema.optional(Schema.String),
}),
});
// 2. Derive type FROM schema
type AuthSession = typeof AuthSessionSchema.Type;
// 3. Parse at boundary
const getSession = (header: string | undefined) =>
Effect.gen(function* () {
const raw = yield* betterAuthGetSession(header);
if (!raw) return null;
return yield* Schema.decodeUnknown(AuthSessionSchema)(raw).pipe(
Effect.mapError((e) => new AuthError({ code: 'INTERNAL', message: String(e) }))
);
});
Schema.Date vs Schema.DateFromSelf
| Schema Type | Input | Output | Use When |
|---|---|---|---|
Schema.Date |
ISO string | Date object | API returns "2024-01-01T00:00:00Z" |
Schema.DateFromSelf |
Date object | Date object | Vendor returns actual Date |
BetterAuth, Drizzle ORM, and most Node libraries return actual Date objects.
Use Schema.DateFromSelf for these.
Key Principle
| Source | Compile-Time | Runtime Safety |
|---|---|---|
$Infer |
Yes | No |
as Type |
Yes (lie) | No |
Schema.decodeUnknown |
Yes | Yes |