---
title: Project structure
description: A walkthrough of every file and folder in a Nooh project and what each one does.
---

A Nooh project is a regular TypeScript project with a few special directories. Nooh discovers your routes and dependencies by reading the filesystem, so the layout of those directories is what drives your API.

## Typical layout

<FileTree>

- nooh.config.ts
- src/
  - index.ts
  - middleware/
    - logger.ts
  - deps/
    - db.ts
  - routes/
    - index.get.ts
    - users/
      - $.ts
      - index.get.ts
      - [id].get.ts
- .nooh/
  - app.ts
  - router/
    - index.ts
    - users/
      - index.ts
      - [id].ts

</FileTree>

## File-by-file

### `nooh.config.ts`

The project configuration. This is the only file Nooh requires. It tells the compiler where your routes and dependency containers live:

```ts
import { config } from "@nooh-ts/nooh";

export default config({
  routes: "src/routes",       // directory where Nooh looks for routes
  dependencies: "src/deps",   // directory where Nooh looks for containers
});
```

You can also pass a Hono `Env` type parameter to propagate environment types across the entire generated application:

```ts
import { config } from "@nooh-ts/nooh";

export interface App {
  Variables: {
    user: User;
  };
}

export default config<App>({
  routes: "src/routes",
  dependencies: "src/deps",
});
```

### Route files

Every `.ts` file inside your `routes` directory is a route. The filename encodes the HTTP method and the path:

| File | Route |
|------|-------|
| `routes/index.get.ts` | `GET /` |
| `routes/users/index.get.ts` | `GET /users` |
| `routes/users/[id].get.ts` | `GET /users/:id` |
| `routes/users/[id].post.ts` | `POST /users/:id` |

A route file exports a default value produced by the generated handler factory:

```ts
// src/routes/users/[id].get.ts
import { get } from "@router/users/[id]";

export default get((c) => {
  const id = c.req.param("id");
  return c.json({ id });
});
```

### Group files (`$.ts`)

A `$.ts` file inside a directory applies middleware and configuration to all routes in that directory and its subdirectories:

```ts
// src/routes/users/$.ts
import logger from "@middleware/logger";
import { group } from "@nooh-ts/nooh";

export default group({
  middleware: [logger],
});
```

### Dependency containers

Files inside your `dependencies` directory export containers — objects that declare services and their scopes:

```ts
// src/deps/db.ts
import { container, singleton } from "@nooh-ts/nooh";

export default container({
  connection: singleton(() => new Database(process.env.DATABASE_URL)),
});
```

### Entry point (`src/index.ts`)

Your Hono entry point. Nooh generates a `.nooh/app.ts` that is a standard Hono `app`; you import and re-export it:

```ts
// src/index.ts
import app from "../.nooh/app";

export default app;
```

### `.nooh/` (generated)

The output directory. This is generated by the compiler — do not edit it by hand, and add it to `.gitignore` if you prefer a clean repository. Every file inside it is plain TypeScript that you can inspect.

<Tree>
  <Tree.Folder name=".nooh" defaultOpen>
    <Tree.File name="app.ts" />
    <Tree.File name="types.ts" />
    <Tree.File name="error.ts" />
    <Tree.Folder name="router" defaultOpen>
      <Tree.File name="index.ts" />
      <Tree.File name="di.ts" />
      <Tree.Folder name="users">
        <Tree.File name="index.ts" />
        <Tree.File name="[id].ts" />
      </Tree.Folder>
    </Tree.Folder>
  </Tree.Folder>
</Tree>

| File | Purpose |
|------|---------|
| `app.ts` | The root Hono application — import this from your entry point. |
| `types.ts` | Re-exports your `App` type so all generated modules stay in sync. |
| `error.ts` | Runtime helpers for response validation. |
| `router/*.ts` | One typed handler factory per route path. |
| `router/di.ts` | Dependency injection runtime helpers. |
