Skip to content

Project structure

TypeScript projects keep sources under src/; JavaScript projects put the same directories at the project root. Both layouts are detected automatically — you never configure which one you are using.

src/
  api/          route handlers      -> HTTP endpoints under /api
  web/          page handlers       -> HTTP endpoints under /
  ws/           socket handlers     -> WebSocket endpoints
  bus/          broker connections  -> ctx.bus.<filename>
  consumers/    message handlers    -> subscriptions
  mcp/          tools, resources,
                prompts             -> MCP server
  di/           injectable values
  services/     injectable services
  middlewares/  request middlewares
  main.ts       bootstrap()
.clove/         generated types (gitignored)

The directories

DirectoryContainsBecomes
api/Modules whose default export is get(), post(), …HTTP routes under /api, path mirroring the file path
web/Same as api/The same, but mounted at the root / — for HTML pages
ws/Modules whose default export is ws()WebSocket endpoints under /ws/…
bus/Modules whose default export is bus()ctx.bus.<filename>, one broker connection each — see Message bus
consumers/Modules whose default export is consume()A subscription each. The path names the consumer; the channel is declared in the file
mcp/tools/, resources/ and prompts/ subdirectoriesAn MCP server at /mcp
services/Modules whose default export is service()ctx.<filename>, a singleton created at boot
di/Modules whose default export is di()ctx.<filename>, scoped per its declared lifetime
middlewares/Modules whose default export is middleware()Wrappers around every route, run in order

Files anywhere else are ignored by the scanner — put helpers, types and constants wherever you like.

Naming is the API

A file's name determines its key on ctx, and a route file's path determines its URL. services/auth.ts becomes ctx.auth; api/v1/login.post.ts becomes POST /api/v1/login.

This means renaming a file is a breaking change to your own code — which is the point. There is exactly one place a name is declared.

consumers/ is the deliberate exception: a channel is a contract shared with whoever publishes it, not a name this project owns, so it is declared in the file rather than derived from the path. See Message bus.

.clove/

clove dev, clove build and clove types write .clove/types.d.ts, which augments the Ctx interface with one entry per file in services/ and di/, and the BusRegistry interface with one per file in bus/. The scaffolded tsconfig.json includes it, and the scaffolded .gitignore excludes it — it is a build artefact, regenerated from the filesystem. See Typed context.

main.ts

The entry point. In the default layout it does one thing:

ts
import { bootstrap } from "clovejs"

bootstrap()

Everything else is discovered. See Bootstrap for the options it accepts, and Express interop if Clove is not going to own the process.

JavaScript layout

Identical, minus src/ and tsconfig.json:

api/
ws/
bus/
consumers/
di/
services/
middlewares/
main.js

Run clove scaffold --js to create it.

Released under the MIT License.