nevr()
Create and configure a Nevr API instance. This is the main entry point for your application.
Signature
function nevr<TEntities extends readonly Entity[]>(
config: NevrConfig & { entities: TEntities }
): TypedNevrInstance<TEntities>defineConfig()
Create a type-safe configuration object. This is the recommended way to define your config so it can be shared between the CLI (generate, db:push) and the runtime (server).
Signature
function defineConfig<const T extends NevrConfig>(config: T): TThe const generic preserves the exact tuple type of your entities, so type inference (like $Infer) works when you spread the config into nevr().
Usage
// src/nevr.config.ts
import { defineConfig } from "nevr"
import { user } from "./entities/user.js"
import { post } from "./entities/post.js"
export const config = defineConfig({
database: "sqlite",
entities: [user, post],
plugins: [],
})
export default configThen in your server:
// src/server.ts
import { nevr } from "nevr"
import { prisma } from "nevr/drivers/prisma"
import { PrismaClient } from "@prisma/client"
import { config } from "./nevr.config.js"
const api = nevr({ ...config, driver: prisma(new PrismaClient()) })
// Full type inference works:
type User = typeof api.$Infer.Entities["user"]Why defineConfig?
- Single source of truth — entities and plugins defined once, used by both CLI and runtime
- Type preservation — the
constgeneric preserves entity tuple types through the spread - CLI compatibility —
npx nevr generateandnpx nevr db:pushauto-discovernevr.config.ts
NevrConfig
Full configuration options:
interface NevrConfig {
/** Entity definitions */
entities: readonly Entity[]
/** Database driver (runtime only — not needed in defineConfig) */
driver: Driver
/** Plugins to use */
plugins?: readonly Plugin[]
/** Database type (for CLI schema generation) */
database?: "sqlite" | "postgresql" | "mysql"
/** CORS configuration */
cors?: CorsOptions
/** Security settings */
security?: SecurityOptions
/**
* Request context factory - adds custom fields to each request
*/
context?: (req: NevrRequest) => Record<string, unknown> | Promise<Record<string, unknown>>
/**
* Nevr context for isolated state management
*/
nevrContext?: NevrContext
/**
* Entity routing options
*/
entityRoutes?: {
pluralize?: ((entityName: string) => string) | undefined;
disablePluralization?: boolean | undefined;
routes?: Record<string, string> | undefined;
} | undefined
}TIP
defineConfig(). The config file focuses on entities, plugins, and database type for schema generation. so always use defineConfig() to create your config object(nevr.config.ts), then spread it into nevr() at runtime to add the driver and any runtime-only options.
Configuration Options
entities (required)
Array of entity definitions:
import { entity, string, int } from "nevr"
const user = entity("user", {
email: string.email().unique(),
name: string,
})
const post = entity("post", {
title: string,
views: int.default(0),
})
const api = nevr({
entities: [user, post],
driver: prisma(db),
})driver (required)
Database driver instance:
import { prisma } from "nevr/drivers/prisma"
import { PrismaClient } from "@prisma/client"
const api = nevr({
entities: [user],
driver: prisma(new PrismaClient()),
})plugins
Array of plugins:
import { auth } from "nevr/plugins/auth"
const api = nevr({
entities: [post],
driver: prisma(db),
plugins: [
auth({
session: { expiresIn: "7d" },
emailAndPassword: { enabled: true },
}),
],
})cors
CORS configuration:
const api = nevr({
entities: [user],
driver: prisma(db),
cors: {
origin: ["http://localhost:3000", "https://myapp.com"],
methods: ["GET", "POST", "PUT", "DELETE"],
credentials: true,
allowedHeaders: ["Content-Type", "Authorization"],
},
})security
Security options:
const api = nevr({
entities: [user],
driver: prisma(db),
security: {
helmet: true,
},
})NevrInstance Methods
handleRequest()
Process an HTTP request:
const response = await api.handleRequest({
method: "GET",
path: "/api/users",
headers: { authorization: "Bearer token" },
query: { take: "10" },
body: null,
})getEntity()
Get an entity definition by name:
const userEntity = api.getEntity("user")
console.log(userEntity.config.fields)getDriver()
Get the database driver:
const driver = api.getDriver()
const users = await driver.findMany("user")registerService()
Register a service in the container:
api.registerService("stripe", () => new Stripe(process.env.STRIPE_KEY), {
lifecycle: "singleton",
tags: ["payments"],
})
api.registerService("mailer", (ctx) => {
return new MailerService(ctx.resolve("smtp"))
})resolve() / resolveAsync()
Resolve a service:
const stripe = api.resolve<Stripe>("stripe")
const db = await api.resolveAsync<Database>("database")hasService()
Check if service exists:
if (api.hasService("stripe")) {
const stripe = api.resolve("stripe")
}addRoute()
Add a custom route:
api.addRoute({
method: "GET",
path: "/health",
handler: async (ctx) => {
return { status: "ok", timestamp: new Date() }
},
})
api.addRoute({
method: "POST",
path: "/webhooks/stripe",
handler: async (ctx) => {
const event = ctx.body
// Process webhook
return { received: true }
},
})addMiddleware()
Add middleware after initialization:
api.addMiddleware({
name: "custom-auth",
fn: async (ctx, next) => {
// Custom logic
return next()
},
})getRoutes()
Get all registered routes:
const routes = api.getRoutes()
routes.forEach(route => {
console.log(`${route.method} ${route.path}`)
})executeAction()
Execute an entity action programmatically:
const result = await api.executeAction("post", "publish", {
resourceId: "post_123",
input: {},
user: currentUser,
})Type Inference
Infer types from your API:
const api = nevr({
entities: [user, post, comment],
driver: prisma(db),
})
// Infer entity types
type User = typeof api.$Infer.Entities["user"]
type Post = typeof api.$Infer.Entities["post"]
// Infer entity names
type EntityNames = typeof api.$Infer.EntityNames
// "user" | "post" | "comment"
// Use in functions
function getUser(id: string): Promise<User> {
return api.getDriver().findOne("user", { id })
}Integration Examples
Next.js
import { toNextHandler } from "nevr/adapters/nextjs"
import { api } from "@/lib/nevr"
export const { GET, POST, PUT, PATCH, DELETE } = toNextHandler(api)Express
import express from "express"
import { expressAdapter } from "nevr/adapters/express"
const app = express()
app.use("/api", expressAdapter(api))
app.listen(3000)Hono
import { Hono } from "hono"
import { honoAdapter} from "nevr/adapters/hono"
const app = new Hono()
app.route("/api", honoAdapter(api))
export default appFull Example
Using defineConfig with the config spread pattern:
import { defineConfig, entity, string, int, text, bool, belongsTo } from "nevr"
import { auth } from "nevr/plugins/auth"
const user = entity("user", {
email: string.email().unique(),
name: string.trim(),
role: string.default("user"),
})
const post = entity("post", {
title: string.min(3).max(200),
content: text,
published: bool.default(false),
views: int.default(0),
author: belongsTo(() => user),
})
.ownedBy("author")
.rules({
create: ["authenticated"],
read: ["everyone"],
update: ["owner", "admin"],
delete: ["owner", "admin"],
})
export const config = defineConfig({
database: "postgresql",
entities: [user, post],
plugins: [
auth({
session: { expiresIn: "7d" },
emailAndPassword: { enabled: true },
}),
],
})
export default configimport express from "express"
import { nevr } from "nevr"
import { prisma } from "nevr/drivers/prisma"
import { expressAdapter } from "nevr/adapters/express"
import { PrismaClient } from "@prisma/client"
import { config } from "./nevr.config.js"
const api = nevr({
...config,
driver: prisma(new PrismaClient()),
cors: {
origin: ["http://localhost:3000"],
credentials: true,
},
})
// Register services
api.registerService("stripe", () => new Stripe(process.env.STRIPE_KEY))
api.registerService("mailer", () => new MailerService())
// Mount
const app = express()
app.use(express.json())
app.use("/api", expressAdapter(api))
app.listen(3000)