Try Live
Add Docs
Rankings
Pricing
Docs
Install
Theme
Install
Docs
Pricing
More...
More...
Try Live
Rankings
Enterprise
Create API Key
Add Docs
Nile
https://github.com/nile-js/nile
Admin
Nile is a TypeScript-first, service and actions oriented backend framework for building modern,
...
Tokens:
178,649
Snippets:
1,309
Trust Score:
7.5
Update:
2 weeks ago
Context
Skills
Chat
Benchmark
92.1
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# Nile Nile is a TypeScript-first, service-action oriented backend framework for building modern, fast, safe, and AI-ready backends. Built on Hono (HTTP), Zod (validation), and slang-ts (Result pattern), it uses a single POST endpoint architecture where all service communication flows through one route, discriminated by an `intent` field in the request body. You define actions, group them into services, and get a predictable API with validation, error handling, and schema export — no route definitions, no controllers, no middleware chains, just your business logic. The framework provides O(1) routing performance through pre-computed dictionary lookups, built-in JWT authentication, file upload handling with validation, a powerful hook system for before/after action processing, structured logging, and a zero-dependency typed client SDK. Every action with a Zod validation schema automatically exports its parameters as JSON Schema, making Nile AI-agent-ready out of the box — an LLM can discover your services, read the schemas, and make tool calls without custom integration code. ## Quick Start - Scaffold a New Project Create a complete Nile project with the CLI that includes services, database setup, and dev tooling pre-configured. ```bash npx @nilejs/cli new my-app cd my-app && bun install && bun run dev ``` ## Manual Installation Install the core framework with required dependencies for manual project setup. ```bash bun add @nilejs/nile zod slang-ts # If using the database layer (createModel, getZodSchema): bun add drizzle-orm drizzle-zod ``` ## createAction - Define Business Logic Actions are the core building blocks of Nile. Each action has a name, optional Zod validation schema, and a handler that returns a Result using Ok/Err pattern. ```typescript // services/tasks/create.ts import { Ok, Err } from "slang-ts"; import z from "zod"; import { createAction, type Action } from "@nilejs/nile"; const createTaskSchema = z.object({ title: z.string().min(1, "Title is required"), status: z.enum(["pending", "in-progress", "done"]).default("pending"), }); const createTaskHandler = async (data: Record<string, unknown>) => { // Validation has already passed at this point const task = { id: crypto.randomUUID(), title: data.title as string, status: (data.status as string) ?? "pending", }; return Ok({ task }); }; export const createTaskAction: Action = createAction({ name: "create", description: "Create a new task", validation: createTaskSchema, handler: createTaskHandler, }); ``` ## createServices - Group Actions Into Services Services group related actions together under a common namespace. Pass service arrays to createNileServer. ```typescript // services/services.config.ts import { createServices, type Services } from "@nilejs/nile"; import { createTaskAction } from "./tasks/create"; import { listTaskAction } from "./tasks/list"; import { getTaskAction } from "./tasks/get"; import { updateTaskAction } from "./tasks/update"; import { deleteTaskAction } from "./tasks/delete"; export const services: Services = createServices([ { name: "tasks", description: "Task management with CRUD operations", meta: { version: "1.0.0" }, actions: [ createTaskAction, listTaskAction, getTaskAction, updateTaskAction, deleteTaskAction, ], }, ]); ``` ## createNileServer - Start the Server Create and configure the Nile server with services, REST interface, authentication, and resources. ```typescript // index.ts import { createNileServer, createLogger } from "@nilejs/nile"; import { services } from "./services/services.config"; import { db } from "./db/client"; const logger = createLogger("my-app", { mode: "prod", chunking: "monthly" }); const server = createNileServer({ serverName: "my-app", services, resources: { logger, database: db, }, rest: { baseUrl: "/api", port: 8000, allowedOrigins: ["http://localhost:3000"], enableStatus: true, }, auth: { secret: process.env.JWT_SECRET!, method: "header", // or "cookie" }, }); if (server.rest) { const { fetch } = server.rest.app; Bun.serve({ fetch, port: 8000 }); console.log("Server running at http://localhost:8000"); } ``` ## REST API - Execute Actions All requests go to a single POST endpoint with intent, service, action, and payload fields. ```bash # Execute an action curl -X POST http://localhost:8000/api/services \ -H "Content-Type: application/json" \ -d '{ "intent": "execute", "service": "tasks", "action": "create", "payload": { "title": "Ship it", "status": "pending" } }' # Response: # { # "status": true, # "message": "Action 'tasks.create' executed", # "data": { # "task": { "id": "a1b2c3d4-...", "title": "Ship it", "status": "pending" } # } # } ``` ## REST API - Explore Services Discover available services and actions at runtime using the explore intent. ```bash # List all services curl -X POST http://localhost:8000/api/services \ -H "Content-Type: application/json" \ -d '{ "intent": "explore", "service": "*", "action": "*", "payload": {} }' # List actions in a specific service curl -X POST http://localhost:8000/api/services \ -H "Content-Type: application/json" \ -d '{ "intent": "explore", "service": "tasks", "action": "*", "payload": {} }' # Response: # { # "status": true, # "message": "Actions for 'tasks'", # "data": [ # { "name": "create", "description": "Create a new task", "isProtected": false, "validation": true }, # { "name": "list", "description": "List all tasks", "isProtected": false, "validation": false } # ] # } ``` ## REST API - Get Schemas for AI Integration Fetch Zod validation schemas as JSON Schema for dynamic form generation or AI agent integration. ```bash # Get schema for a specific action curl -X POST http://localhost:8000/api/services \ -H "Content-Type: application/json" \ -d '{ "intent": "schema", "service": "tasks", "action": "create", "payload": {} }' # Response: # { # "status": true, # "message": "Schema for 'tasks.create'", # "data": { # "create": { # "type": "object", # "properties": { # "title": { "type": "string", "minLength": 1 }, # "status": { "type": "string", "enum": ["pending", "in-progress", "done"], "default": "pending" } # }, # "required": ["title"] # } # } # } ``` ## createNileClient - Type-Safe Frontend Client Use the standalone, zero-dependency client for type-safe action invocation from any JavaScript environment. ```typescript import { createNileClient } from "@nilejs/client"; import type { ServicePayloads } from "./generated/types"; // Optional: generated types const nile = createNileClient<ServicePayloads>({ baseUrl: "http://localhost:8000/api", credentials: "include", timeout: 30000, }); // Execute an action const { error, data } = await nile.invoke({ service: "tasks", action: "create", payload: { title: "Buy milk" }, }); if (error) { console.error("Failed:", error); } else { console.log("Created:", data); } // Discover services const { data: services } = await nile.explore({ service: "*", action: "*" }); // Get schemas const { data: schemas } = await nile.schema({ service: "tasks", action: "*" }); ``` ## Protected Actions with JWT Authentication Mark actions as protected to require valid JWT tokens. Auth data is available via session context. ```typescript import { Ok, Err } from "slang-ts"; import { createAction, type Action } from "@nilejs/nile"; export const getProfile: Action = createAction({ name: "getProfile", description: "Get the current user's profile", isProtected: true, // Requires valid JWT handler: (data, context) => { const session = context?.getSession("rest"); if (!session) return Err("Not authenticated"); return Ok({ userId: session.userId, organizationId: session.organizationId, }); }, }); // Call with Authorization header: // curl -X POST http://localhost:8000/api/services \ // -H "Content-Type: application/json" \ // -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \ // -d '{ "intent": "execute", "service": "users", "action": "getProfile", "payload": {} }' ``` ## Action Hooks - Before/After Processing Define hooks that run before or after an action. Hooks reference other registered actions by service.action address. ```typescript import { Ok } from "slang-ts"; import { createAction, type Action } from "@nilejs/nile"; // A hook action that logs access export const logAccessAction: Action = createAction({ name: "logAccess", description: "Log action access for audit", handler: (data, context) => { console.log("Audit:", context?.hookContext?.actionName); return Ok(data); // Pass through payload }, }); // Main action with hooks export const createOrderAction: Action = createAction({ name: "createOrder", description: "Create a new order", hooks: { before: [ { service: "audit", action: "logAccess", isCritical: false }, { service: "inventory", action: "validateStock", isCritical: true }, ], after: [ { service: "notifications", action: "sendConfirmation", isCritical: false }, ], }, handler: (data) => Ok({ order: { ...data, status: "confirmed" } }), }); ``` ## Global Hooks - Cross-Cutting Concerns Add global before/after handlers that run on every action for authentication, logging, or transformation. ```typescript import { createNileServer } from "@nilejs/nile"; import { Ok, Err } from "slang-ts"; const server = createNileServer({ serverName: "my-app", services, onBeforeActionHandler: async ({ nileContext, action, payload }) => { // Runs before every action - auth checks, logging, etc. const session = nileContext.getSession("rest"); // Custom RBAC check const requiredRole = action.accessControl?.[0]; if (requiredRole && session?.role !== requiredRole) { return Err(`Requires role: ${requiredRole}`); } return Ok(payload); }, onAfterActionHandler: async ({ nileContext, action, payload, result }) => { // Runs after every action - transforms, auditing, etc. console.log(`Action ${action.name} completed:`, result.isOk); return result; }, }); ``` ## createModel - CRUD Model Factory for Drizzle Generate typed CRUD operations from Drizzle tables with auto-validation, error handling, and pagination. ```typescript // db/models/tasks.ts import { createModel } from "@nilejs/nile"; import { db } from "@/db/client"; import { tasks } from "@/db/schema"; export const taskModel = createModel(tasks, { db, name: "task" }); // Usage in action handlers: // taskModel.create({ data: { title: "New task" } }) // taskModel.findById("uuid-here") // taskModel.update({ id: "uuid", data: { title: "Updated" } }) // taskModel.delete("uuid-here") // taskModel.findAll() // taskModel.findPaginated({ limit: 20, offset: 0 }) // taskModel.findPaginated({ limit: 20, cursor: "last-id", cursorColumn: "created_at" }) ``` ## Database Schema with Drizzle ORM Define your database schema using Drizzle ORM for use with createModel. ```typescript // db/schema.ts import { pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core"; export const tasks = pgTable("tasks", { id: uuid("id").defaultRandom().primaryKey(), title: text("title").notNull(), description: text("description"), status: text("status", { enum: ["pending", "in-progress", "done"] }) .notNull() .default("pending"), created_at: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), updated_at: timestamp("updated_at", { withTimezone: true }) .notNull() .defaultNow() .$onUpdate(() => new Date()), }); // db/types.ts import type { tasks } from "./schema"; export type Task = typeof tasks.$inferSelect; export type NewTask = typeof tasks.$inferInsert; ``` ## handleError - Traceable Error Logging Use handleError for runtime errors that need logging. Returns an Err with a traceable log ID. ```typescript import { Ok } from "slang-ts"; import { createAction, handleError, type Action } from "@nilejs/nile"; export const createOrder: Action = createAction({ name: "createOrder", description: "Create a new order", handler: async (data, context) => { const db = context?.resources?.database; if (!db) { return handleError({ message: "Database not available" }); // Returns: Err("[log-id-xyz] Database not available") } const result = await saveOrder(db, data); if (!result) { return handleError({ message: "Failed to save order", data: { payload: data }, atFunction: "createOrder", }); } return Ok({ order: result }); }, }); ``` ## File Uploads with Multipart FormData Configure and handle file uploads through the same single POST endpoint with validation. ```typescript // Server configuration const server = createNileServer({ serverName: "my-app", services, rest: { baseUrl: "/api", uploads: { enforceContentType: true, limits: { maxFiles: 5, maxFileSize: 5 * 1024 * 1024, // 5MB per file minFileSize: 1, maxTotalSize: 20 * 1024 * 1024, // 20MB total maxFilenameLength: 128, }, allow: { mimeTypes: ["image/png", "image/jpeg", "application/pdf"], extensions: [".png", ".jpg", ".jpeg", ".pdf"], }, }, }, }); // Upload action handler export const uploadDocument: Action = createAction({ name: "upload", description: "Upload a document", isSpecial: { contentType: "multipart/form-data", uploadMode: "flat", }, handler: (data, context) => { const { fields, files } = data as { fields: Record<string, string | string[]>; files: Record<string, File | File[]>; }; const document = files.document as File; return Ok({ filename: document.name, size: document.size }); }, }); ``` ## Client File Upload Upload files using the Nile client with FormData support. ```typescript import { createNileClient } from "@nilejs/client"; const nile = createNileClient({ baseUrl: "http://localhost:8000/api" }); const { error, data } = await nile.upload({ service: "documents", action: "upload", files: { document: new File(["content"], "report.pdf", { type: "application/pdf" }), }, fields: { title: "Q4 Report", category: "finance", }, }); // Or using curl: // curl -X POST http://localhost:8000/api/services \ // -F "intent=execute" \ // -F "service=documents" \ // -F "action=upload" \ // -F "title=Q4 Report" \ // -F "document=@./report.pdf" ``` ## Custom Routes with Hono Add custom routes directly on the Hono app for webhooks, OAuth callbacks, or health checks. ```typescript import { createNileServer, getContext } from "@nilejs/nile"; const server = createNileServer({ serverName: "my-app", services, resources: { database: db }, rest: { baseUrl: "/api", allowedOrigins: ["http://localhost:3000"] }, }); const app = server.rest?.app; // Webhook endpoint app?.post("/webhooks/stripe", async (c) => { const ctx = getContext(); const payload = await c.req.json(); ctx.resources?.logger?.info({ atFunction: "stripeWebhook", message: "Payment webhook received", data: { eventType: payload.type }, }); return c.json({ received: true }); }); // OAuth callback app?.get("/auth/google/callback", async (c) => { const code = c.req.query("code"); // Exchange code, set session... return c.redirect("/dashboard"); }); // Custom health check app?.get("/health", async (c) => { const dbHealthy = await checkDatabaseConnection(); return c.json({ status: dbHealthy ? "ok" : "degraded" }, dbHealthy ? 200 : 503); }); ``` ## NileContext - Access Resources and Sessions Access shared resources, session data, and hook context from anywhere within a request. ```typescript import { Ok } from "slang-ts"; import { createAction, getContext, type Action } from "@nilejs/nile"; export const myAction: Action = createAction({ name: "myAction", description: "Example action using context", handler: (data, context) => { // Access logger context?.resources?.logger?.info({ atFunction: "myAction", message: "Action executed", data: { timestamp: Date.now() }, }); // Access database const db = context?.resources?.database; // Session management context?.setSession("rest", { userId: "123", role: "admin" }); const session = context?.getSession("rest"); // Or use getContext() from anywhere const ctx = getContext(); const user = ctx.get("currentUser"); return Ok({ session }); }, }); ``` ## CLI Commands - Generate Services and Actions Use the CLI to scaffold services, actions, and generate TypeScript types from your schemas. ```bash # Create a new service with demo action npx @nilejs/cli generate service users # Creates: src/services/users/sample.ts and src/services/users/index.ts # Add an action to existing service npx @nilejs/cli generate action users get-user # Creates: src/services/users/get-user.ts # Generate Zod schemas and TypeScript types from your services npx @nilejs/cli generate schema # Creates: src/generated/schemas.ts and src/generated/types.ts ``` ## Structured Logging Create and use structured loggers for production logging with time-based file chunking. ```typescript import { createLogger } from "@nilejs/nile"; // Create logger with monthly chunking const logger = createLogger("my-app", { mode: "prod", chunking: "monthly" }); // Use in handlers logger.info({ atFunction: "processOrder", message: "Order processed", data: { orderId: "123", amount: 99.99 }, }); logger.warn({ atFunction: "validateInput", message: "Missing optional field", }); logger.error({ atFunction: "paymentGateway", message: "Payment failed", data: { error: "Card declined" }, }); // Pass to server resources const server = createNileServer({ serverName: "my-app", services, resources: { logger }, }); ``` ## CORS Configuration Configure CORS with global defaults and per-route rules. ```typescript const server = createNileServer({ serverName: "my-app", services, rest: { baseUrl: "/api", allowedOrigins: ["http://localhost:3000", "https://myapp.com"], cors: { enabled: true, defaults: { credentials: true, allowHeaders: ["Content-Type", "Authorization"], allowMethods: ["POST", "GET", "OPTIONS"], maxAge: 600, }, addCors: [ { path: "/api/public/*", options: { origin: "*", credentials: false }, }, { path: "/api/partners/*", resolver: (origin, c, cors) => { if (partnerOrigins.includes(origin)) { cors.allowOrigin(origin); cors.addHeaders(["X-Partner-Id"]); } else { cors.deny(); } }, }, ], }, }, }); ``` ## Rate Limiting Configure rate limiting with custom client identification. ```typescript const server = createNileServer({ serverName: "my-app", services, rest: { baseUrl: "/api", rateLimiting: { windowMs: 900000, // 15 minutes limit: 100, // 100 requests per window standardHeaders: true, limitingHeader: "x-api-key", // Client key header }, }, }); ``` ## Static File Serving Serve static files from a local directory through the REST interface. ```typescript const server = createNileServer({ serverName: "my-app", runtime: "bun", // or "node" services, rest: { baseUrl: "/api", enableStatic: true, staticDir: "./assets", // defaults to "./assets" }, }); // Files in ./assets/ are served at /assets/* // GET /assets/logo.png // GET /assets/docs/readme.pdf ``` ## Project Structure Recommended project structure for Nile applications with services, database, and models. ``` my-app/ ├── src/ │ ├── index.ts # Server entry point │ ├── db/ │ │ ├── client.ts # DB connection setup │ │ ├── schema.ts # Drizzle table definitions │ │ ├── types.ts # Inferred types │ │ ├── index.ts # Barrel exports │ │ └── models/ │ │ ├── tasks.ts # createModel for tasks │ │ └── index.ts # Barrel exports │ └── services/ │ ├── services.config.ts # All service definitions │ └── tasks/ │ ├── create.ts # Action: create task │ ├── list.ts # Action: list tasks │ ├── get.ts # Action: get task by ID │ ├── update.ts # Action: update task │ └── delete.ts # Action: delete task ├── package.json ├── tsconfig.json └── drizzle.config.ts ``` ## Summary Nile provides a complete solution for building TypeScript backends with a service-action architecture that eliminates traditional routing boilerplate while maintaining type safety throughout the stack. The framework excels at internal APIs, dashboards, business logic applications, and AI-agent integration where the built-in discovery and schema endpoints provide everything an LLM needs to interact with your backend without custom adapters. Key integration patterns include: using createModel with Drizzle for zero-boilerplate CRUD operations, leveraging hooks for cross-cutting concerns like authentication and audit logging, deploying the @nilejs/client for type-safe frontend communication, and exposing the explore/schema intents for runtime API discovery. The Result pattern (Ok/Err) ensures consistent error handling from database operations through to client responses, while handleError provides traceable logging for production debugging.