Getting Started with Nile

Nile is a functional-first, type-safe backend framework built on Hono. It works with Bun, Node.js, and Deno, uses Zod for validation, and returns Results from all action handlers using the Ok / Err pattern from slang-ts.

1. Installation

The CLI creates a working project with services, database setup, and dev tooling:

npx @nilejs/cli new my-app
cd my-app && bun install && bun run dev

The generated project includes a tasks service, PGLite database, Drizzle ORM, and a running server. You can add more services and actions with npx @nilejs/cli generate service <name> and npx @nilejs/cli generate action <service> <name>. To extract Zod schemas and TypeScript types from your actions, run npx @nilejs/cli generate schema.

Manual install

:::tabs

@tab Bun

bun add @nilejs/nile zod slang-ts

@tab npm

npm install @nilejs/nile zod slang-ts

@tab pnpm

pnpm add @nilejs/nile zod slang-ts

:::

2. Quick Start

2.1 Create Actions

Actions are the core building blocks. Each action has a name, optional Zod validation schema, and a handler that returns a Result.

// services/todos/create.ts
import { Ok } from "slang-ts";
import z from "zod";
import { createAction, type Action } from "@nilejs/nile";

const createTodoSchema = z.object({
  title: z.string().min(1, "Title is required"),
  completed: z.boolean().default(false),
});

const createTodoHandler = (data: Record<string, unknown>) => {
  const todo = {
    id: crypto.randomUUID(),
    title: data.title as string,
    completed: (data.completed as boolean) ?? false,
  };
  return Ok({ todo });
};

export const createTodoAction: Action = createAction({
  name: "create",
  description: "Create a new todo",
  validation: createTodoSchema,
  handler: createTodoHandler,
});

2.2 Group Actions into a Service

// services/todos/list.ts
import { Ok } from "slang-ts";
import { createAction, type Action } from "@nilejs/nile";

const listTodoHandler = () => {
  return Ok({
    todos: [
      { id: "1", title: "Learn Nile", completed: false },
      { id: "2", title: "Build an API", completed: true },
    ],
  });
};

export const listTodoAction: Action = createAction({
  name: "list",
  description: "List all todos",
  handler: listTodoHandler,
});
// services/todos.ts
import { createServices, type Services } from "@nilejs/nile";
import { createTodoAction } from "./create";
import { listTodoAction } from "./list";

export const services: Services = createServices([
  {
    name: "todos",
    description: "Todo list management",
    actions: [createTodoAction, listTodoAction],
  },
]);

2.3 Create and Start the Server

// server.ts
import { createNileServer } from "@nilejs/nile";
import { services } from "./services/todos";

const server = createNileServer({
  serverName: "my-app",
  services,
  rest: {
    baseUrl: "/api",
    port: 8000,
  },
});

if (server.rest) {
  const { fetch } = server.rest.app;
  Bun.serve({ fetch, port: 8000 });
  console.log("Server running at http://localhost:8000");
}

Run with Bun:

bun run server.ts

2.4 Invoke Your Actions

Nile uses a single POST endpoint with an intent-driven payload:

# List todos
curl -X POST http://localhost:8000/api/services \
  -H "Content-Type: application/json" \
  -d '{
    "intent": "execute",
    "service": "todos",
    "action": "list",
    "payload": {}
  }'

# Create a todo
curl -X POST http://localhost:8000/api/services \
  -H "Content-Type: application/json" \
  -d '{
    "intent": "execute",
    "service": "todos",
    "action": "create",
    "payload": { "title": "Ship Nile", "completed": false }
  }'

3. Project Structure

my-api/
├── server.ts                  # Entry point
├── services/
│   ├── todos.ts               # Service definition
│   └── todos/
│       ├── create.ts          # Action: create todo
│       └── list.ts            # Action: list todos
└── package.json

4. Next Steps

This documentation reflects the current implementation and is subject to evolution.