Introduction β
ποΈ Nevr is the Entity-First Full-Stack Framework for TypeScript.
Building industrial-grade backends shouldn't mean gluing together 15 different libraries. Nevr provides a cohesive, type-safe architecture where your Entity is the single source of truth.
Why Nevr? β
| Traditional Approach | Nevr Approach |
|---|---|
| Define Prisma schema | Define Entity once |
| Create DTO classes | Auto-generated from Entity |
| Write controllers | Auto-generated REST API |
| Add validation decorators | Declarative field validation |
| Build auth middleware | Built-in with .ownedBy() and .rules() |
| Generate OpenAPI manually | Auto-generated from Entity |
| Write TypeScript interfaces | Inferred from Entity |
Result: 80% less boilerplate, 100% type-safe.
The Core Philosophy β
Most frameworks force you to write code in multiple places:
- Database Schema (SQL/Prisma)
- API Controllers (validation logic)
- TypeScript Interfaces (types)
- Authorization Middleware (permissions)
With Nevr, you define the Entity once.
import { entity, float, string, belongsTo } from "nevr"
const order = entity("order", {
total: float,
status: string.default("pending"),
customer: belongsTo(() => user),
})
.ownedBy("customer") // Sets owner field + default rules
.rules({
create: ["authenticated"], // Only logged-in users can create
update: ["owner"], // Only owner can update
})What This Gives You β
From this single definition, Nevr gaves you everything you need for a production-ready app:
| Generated Output | Description |
|---|---|
| Database Schema | Prisma schema with proper relations |
| REST API | GET /orders, POST /orders, PUT /orders/:id, etc. |
| Validation | Type checking + custom validators |
| Authorization | Rule-based access control per operation |
| E2E Type Safety | End-to-end type safety from backend to frontend |
| OpenAPI Spec | Documentation with all endpoints |
3 Pillars of Industrial Architecture β
Nevr goes beyond CRUD APIs. It provides the architectural patterns needed for complex, real-world applications.
1. Workflow Engine (Sagas) π β
Why? When business operations span multiple services (inventory, payments, shipping), failures can leave your system in an inconsistent state. The Workflow Engine implements the Saga pattern with automatic compensation (rollback).
import { action, step } from "nevr"
// If step 2 (charge) fails, step 1 is automatically compensated (release)
const checkout = action("checkout")
.workflow([
step("reserve", inventory.reserve, inventory.release), // reserve β release on failure
step("charge", stripe.charge, stripe.refund), // charge β refund on failure
step("ship", shipping.createLabel), // No compensation needed
])| Workflow Method | Description |
|---|---|
action(name) | Create a new action builder |
step(name, execute, compensate?) | Define a saga step with optional rollback |
.workflow(steps[]) | Chain steps into a saga |
.handler(fn) | Simple action without saga pattern |
2. Service Container (DI) π§© β
Why? Dependency Injection keeps your code testable and decoupled. Nevr's container is functional (no classes required) with automatic lifecycle management.
import { ServiceContainer} from "nevr"
const container = ServiceContainer()
// Register a service with a factory
container.register("payments", () => new StripeService(process.env.STRIPE_KEY))
// Register with lifecycle options
container.register("cache", () => new RedisCache(), {
lifecycle: "singleton" // singleton | transient | scoped
})
// Resolve in handlers
const payments = container.resolve<PaymentService>("payments")| Container Method | Description |
|---|---|
ServiceContainer() | Create a new DI container |
.register(id, factory, options?) | Register a service factory |
.registerInstance(id, instance) | Register an existing instance |
.resolve<T>(id) | Synchronously resolve a service |
.resolveAsync<T>(id) | Async resolve (for services needing init) |
.has(id) | Check if service exists |
3. Remote Joiner π β
Why? Modern apps integrate with external services (Stripe, CMS, microservices). Remote Joiner lets you include external data in API responses without N+1 queries.
import { entity, belongsTo } from "nevr"
const user = entity("user", {
email: string.email(),
// This relation fetches from Stripe, not the database
subscription: belongsTo(() => stripePlan).remote("stripe"),
})
// API request: GET /api/users?include=subscription
// Nevr fetches user from DB, subscription from Stripe adapter| Remote Joiner Method | Description |
|---|---|
.remote(adapterName) | Mark relation as remote (fetched from external service) |
Architecture Overview β
Nevr fits into your existing stack. It is framework-agnostic at the HTTP layer and driver-agnostic at the database layer.
graph TD
Client[React / Mobile / cURL] --> API[HTTP Adapter]
subgraph "Nevr Core"
API --> Router[Router & Validation]
Router --> Auth[Auth & Permissions]
Auth --> Workflow[Actions & Workflows]
Workflow --> Entity[Entity Logic]
Entity --> Driver[Database Driver]
end
Driver --> DB[(PostgreSQL / MySQL)]Supported Adapters β
| Layer | Options |
|---|---|
| HTTP | Express, Fastify, Hono, Bun, Node.js (standalone) |
| Database | Prisma (PostgreSQL, MySQL, SQLite, MongoDB) |
| Auth | Auth integration |
Full-Stack TypeScript β
Nevr is designed as a true full-stack TypeScript framework. Types flow seamlessly from your entity definitions to your frontend clientβno code generation, no runtime overhead, just pure TypeScript inference.
The $Infer Pattern β
Nevr uses a $Infer pattern to extract types from your server:
// === server.ts ===
export const api = nevr({
entities: [user, product, order],
plugins: [auth()],
})
// Export the server type (no runtime import on client)
export type API = typeof api// === client.ts ===
import { createTypedClient, entityClient } from "nevr/client"
import type { API } from "./server" // Type-only import
const client = createTypedClient<API>({
baseURL: "http://localhost:3000",
plugins: [entityClient({ entities: ["user", "product", "order"] })],
})
// Full autocomplete and type checking!
const { data } = await client.users.create({
email: "user@example.com", // β
Required field
name: "John", // β
Required field
// unknownField: "oops", // β Type error
})Type Inference Utilities β
| Type | Description |
|---|---|
$Infer<T> | Extract all types from server |
InferEntityData<E> | Get entity data type |
InferCreateInput<E> | Get create input type (no id/timestamps) |
InferUpdateInput<E> | Get update input type (all optional) |
InferSessionFromClient<C> | Get session type from client |
ListOptions<T> | Typed filtering, sorting, pagination |
Why This Matters β
| Traditional Approach | Nevr Approach |
|---|---|
| Generate types with codegen | Types inferred at compile time |
| Sync OpenAPI β TypeScript | Single source of truth |
| Runtime validation only | Compile-time + runtime validation |
| Manual type exports | Automatic via $Infer |
Result: Changes to your entities automatically propagate to your frontend types. No regeneration step, no sync issues.
Core Exports Reference β
| Export | Category | Description |
|---|---|---|
entity | Entity DSL | Create an entity definition |
string, int, float, bool, datetime, json | Fields | Primitive field types |
belongsTo, hasMany, hasOne | Relations | Define relationships |
action, step | Actions | Create custom operations & workflows |
everyone, authenticated, owner, admin | Rules | Built-in authorization rules |
ServiceContainer | DI | Create service container |
NevrErrorClass | Errors | Base error class |
Next Steps β
- Comparison - See how Nevr stacks up against NestJS and tRPC
- Installation - Set up your first project in minutes
- Basic Usage - Build a complete API
