FILAR
Application framework for Effect TypeScript.
Define your application around domain operations.
Expose them through honest boundaries.
Simple
const PayInvoice = Filar.operation({
name: "PayInvoice",
input: PayInvoiceInput,
output: Payment,
// This is the application logic.
run: ({ invoiceId }) => Payments.pay(invoiceId),
})
const app = Filar.make()
// Reuse the operation contract directly.
.expose(Http.post("/invoices/pay", PayInvoice))
// The same operation can have other boundaries.
.expose(Cli.command("invoice pay", PayInvoice))Advanced
const PayInvoice = Filar.operation({
name: "PayInvoice",
input: PayInvoiceInput,
output: Payment,
errors: [InvoiceNotFound, PaymentRejected],
run: ({ invoiceId, paymentMethodId }) =>
Payments.pay(invoiceId, paymentMethodId),
})
const app = Filar.make().expose(
Http.post("/invoices/:invoiceId/pay", PayInvoice, {
input: {
// The HTTP contract can have its own shape.
path: Schema.Struct({
invoiceId: Schema.String,
}),
body: Schema.Struct({
paymentMethod: Schema.String,
}),
// Map the transport input into the operation input.
toOperation: ({ path, body }) => ({
invoiceId: InvoiceId.make(path.invoiceId),
paymentMethodId: PaymentMethodId.make(body.paymentMethod),
}),
},
output: {
schema: Schema.Struct({
id: Schema.String,
status: Schema.Literal("paid"),
}),
// Decide exactly what crosses this boundary.
fromOperation: (payment) => ({
id: payment.id,
status: payment.status,
}),
},
errors: {
// Domain errors get an HTTP representation here.
InvoiceNotFound: Http.error(404),
PaymentRejected: Http.error(422),
},
}),
)Values
- Domain first: model application capabilities in domain language.
- Operations are the API: typed, composable, executable.
- Honest boundaries: HTTP, CLI, RPC, jobs and events keep their own contracts.
- Effect-native: use Effect directly for services, errors, layers, streams and runtime.
- Inspectable by design: the framework should understand the application it is running.
- Sane defaults included: storage, images, email, queues, tasks, and other common app needs come with solid default implementations.
Paired with:
- Bring your own implementation: every capability can be backed by your own Effect service or layer.
Anti-goals
- No ORM first: Filar should never make tables or models the center of the application.
- No tactical DDD by default: no required aggregates, repositories, command buses, or ceremony.
- No Effect hidden away: Filar should extend Effect, not replace its programming model.
- No fake portability: provider differences should stay visible when they matter.
- No whole-stack ownership: Filar should not dictate your frontend, database, cloud, or deployment shape.
Start with the documentation.