Skip to content

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 ApproachNevr Approach
Define Prisma schemaDefine Entity once
Create DTO classesAuto-generated from Entity
Write controllersAuto-generated REST API
Add validation decoratorsDeclarative field validation
Build auth middlewareBuilt-in with .ownedBy() and .rules()
Generate OpenAPI manuallyAuto-generated from Entity
Write TypeScript interfacesInferred from Entity

Result: 80% less boilerplate, 100% type-safe.


The Core Philosophy ​

Most frameworks force you to write code in multiple places:

  1. Database Schema (SQL/Prisma)
  2. API Controllers (validation logic)
  3. TypeScript Interfaces (types)
  4. Authorization Middleware (permissions)

With Nevr, you define the Entity once.

typescript
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 OutputDescription
Database SchemaPrisma schema with proper relations
REST APIGET /orders, POST /orders, PUT /orders/:id, etc.
ValidationType checking + custom validators
AuthorizationRule-based access control per operation
E2E Type SafetyEnd-to-end type safety from backend to frontend
OpenAPI SpecDocumentation 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).

typescript
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 MethodDescription
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.

typescript
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 MethodDescription
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.

typescript
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 MethodDescription
.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.

mermaid
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 ​

LayerOptions
HTTPExpress, Fastify, Hono, Bun, Node.js (standalone)
DatabasePrisma (PostgreSQL, MySQL, SQLite, MongoDB)
AuthAuth 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:

typescript
// === 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
typescript
// === 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 ​

TypeDescription
$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 ApproachNevr Approach
Generate types with codegenTypes inferred at compile time
Sync OpenAPI β†’ TypeScriptSingle source of truth
Runtime validation onlyCompile-time + runtime validation
Manual type exportsAutomatic via $Infer

Result: Changes to your entities automatically propagate to your frontend types. No regeneration step, no sync issues.


Core Exports Reference ​

ExportCategoryDescription
entityEntity DSLCreate an entity definition
string, int, float, bool, datetime, jsonFieldsPrimitive field types
belongsTo, hasMany, hasOneRelationsDefine relationships
action, stepActionsCreate custom operations & workflows
everyone, authenticated, owner, adminRulesBuilt-in authorization rules
ServiceContainerDICreate service container
NevrErrorClassErrorsBase error class

Next Steps ​

Released under the MIT License.