Folder Structure
Folder structure, file naming conventions, and how Legonode maps files to routes, middleware, events, and schedules.
Folder Structure
This guide explains how Legonode organizes your appDir directory: which folders and file names are special, how URL paths are derived, and how to name files for routes, middleware, events, and scheduled tasks.
App folder structure
All application code that Legonode loads lives under a single appDir (by default ./src, configurable via legonode.config.ts). The layout looks like this:
- Routes — Route files live under
router/; the folder path underrouter/becomes the URL path. - Middleware —
router/**/middleware.ts(or.js/.mts/.mjs) applies to that directory and all subpaths (prefix is relative torouter/). - Events — Only files inside
appDir/events/are loaded as event handlers. - Schedules — Only files inside
appDir/cron/are loaded as scheduled (cron) tasks.
Route file naming
Legonode treats two kinds of files as route handlers:
1. Single file for all methods: route.ts
- Names:
route.ts,route.js,route.mts,route.mjs - Meaning: This path handles every HTTP method you implement (e.g.
GET,POST). - Exports: Export a function per method:
GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS, and/ordefault(used when the request method doesn't have its own export).
Example: router/api/posts/route.ts → serves /api/posts for any method you export.
2. One file per method: get.ts, post.route.ts, etc.
- Names:
get.ts,post.ts,get.route.ts,post.route.ts, etc. (same forput,patch,delete,head,options). - Extensions:
.ts,.js,.mts,.mjs - Meaning: That path handles only that HTTP method. You can mix: e.g.
route.tsfor GET andpost.route.tsfor POST on the same path. - Exports: Export the handler as the method name (e.g.
export function GET) or asdefault.
You cannot have two files that both handle the same method for the same path (e.g. two files for GET on /api/users); the loader will throw.
How URL paths are built
- The pathname of a route is the directory path to the folder that contains the route file. The filename does not appear in the URL.
- Route groups — A folder whose name is in parentheses is omitted from the URL:
router/api/(user)/users/route.ts→ pathname is/api/users(not/api/(user)/users).
- Dynamic segments — A folder whose name is in square brackets becomes a path parameter:
router/api/posts/[postId]/route.ts→ pathname/api/posts/:postId; the matched value is inctx.params.postId.
- Optional catch-all —
[[...param]]matches the rest of the path (including zero segments). The param name (e.g.all) receives the remainder as a single string.
Examples:
| File path | URL path |
|---|---|
router/route.ts | / |
router/api/hello/route.ts | /api/hello |
router/api/(user)/users/get.route.ts | GET /api/users |
router/api/(user)/posts/[postId]/route.ts | /api/posts/:postId |
router/api/(user)/posts/[postId]/author/route.ts | /api/posts/:postId/author |
Middleware file naming
- Names:
middleware.ts,middleware.js,middleware.mts,middleware.mjs - Placement: Only under
router/. The directory path underrouter/is the path prefix for which the middleware runs. Middleware in a folder runs for that path and all subpaths. - Order: Middleware is resolved from the root toward the request path (e.g.
router/middleware.tsthenrouter/api/middleware.tsfor a request to/api/hello).
Event handlers: appDir/events/
- Directory: Only
appDir/events/is scanned for event handlers. - File names:
*.event.ts,*.event.js,*.event.mts,*.event.mjs - Event name: Derived from the filename (before
.event.*) by converting camelCase to dot.case:userCreatedEmail.event.ts→ event nameuser.created.email
- Export: The file must export a default function; that function is registered as the handler for that event name. You emit events via
ctx.events.emit('user.created.email', payload)(or the event bus API).
Scheduled tasks: appDir/cron/
- Directory: Only
appDir/cron/is scanned for scheduled tasks. - File names:
*.cron.ts,*.cron.js,*.cron.mts,*.cron.mjs - Task name: Derived from the filename (before
.cron.*) by converting camelCase to dot.case:intervalExample.cron.ts→ schedule nameinterval.example
- Exports: The file must export
schedule(cron expression or schedule definition) andrun(function). You can invoke the same run logic by name viactx.schedule('interval.example', payload)(or the schedule runner API).
Config and extensions summary
| Kind | Location | File pattern | Notes |
|---|---|---|---|
| Routes | Any folder under router/ | route.ts, get.ts, post.route.ts, etc. | Folder path under router/ = URL path; (group) omitted, [id] = param |
| Middleware | Any folder under router/ | middleware.ts | Path prefix = directory path under router/ |
| Events | appDir/events/ only | *.event.ts | Event name = camelCase → dot.case |
| Schedules | appDir/cron/ only | *.cron.ts | Task name = camelCase → dot.case |
Supported extensions for routes and middleware: .ts, .js, .mts, .mjs. Use the same extensions for events and cron files.
Next steps
- Get Started — Set up a project and run your first route.
- Routing — Dynamic segments, route groups, and method handlers in more detail.
- Middleware — Order,
next(), and error handling.