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.