📚 StoneJS Developer Guide
Complete reference for building modern web applications with the StoneJS Framework
🛣️ Routing & Pages
File-Based Routing
StoneJS uses file-based routing. The file structure in pages/ directly maps to URLs:
| File Path | URL |
|---|---|
pages/index.html |
/ or /index.html |
pages/about.html |
/about.html |
pages/blog/index.html |
/blog/ or /blog/index.html |
pages/blog/post.html |
/blog/post.html |
pages/admin/users.html |
/admin/users.html |
Dynamic Routing with Dhandlers
For dynamic routes, API endpoints, or custom routing logic, use dhandlers:
// pages/blog/dhandler
module.exports = async (context) => {
const { req, res, db } = context;
// Extract path after /blog/
const path = req.path.replace('/blog/', '');
// Handle different routes
if (path === '' || path === 'index.html') {
// List all blog posts
const posts = await db.query('SELECT * FROM posts ORDER BY created_at DESC');
return res.render('blog/list', { posts });
}
// Match /blog/post-slug
const post = await db.query('SELECT * FROM posts WHERE slug = $1', [path]);
if (post.length > 0) {
return res.render('blog/single', { post: post[0] });
}
// 404
return res.status(404).send('Post not found');
};
Route Parameters
Access route parameters, query strings, and request data via $req:
| Property | Example URL/Request | Access Method |
|---|---|---|
| Query parameters | /users.html?id=123&role=admin |
$req.query.id → "123" |
| POST body (JSON) | {"name": "Alice", "email": "[email protected]"} |
$req.body.name → "Alice" |
| URL path | /blog/posts.html |
$req.path → "/blog/posts.html" |
| HTTP method | GET, POST, PUT, DELETE | $req.method → "GET" |
| Headers | Authorization: Bearer token |
$req.headers.authorization |
| Cookies | theme=dark |
$req.cookies.theme → "dark" |
Example Usage
<%%js>
// URL: /users.html?id=123&role=admin
const userId = $req.query.id; // "123"
const userRole = $req.query.role; // "admin"
// POST body parameters
const name = $req.body.name;
const email = $req.body.email;
// Check HTTP method
if ($req.method === 'POST') {
// Handle form submission
}
Autohandlers (Layouts)
Autohandlers wrap pages with shared HTML structure:
<!-- pages/autohandler (root layout) -->
<!DOCTYPE html>
<html>
<head>
<title><%= $context.get('pageTitle') || 'My Site' %></title>
</head>
<body>
<header>
<!-- Site header -->
</header>
<main>
<%- await next() <!-- Child content injected here -->
</main>
<footer>
<!-- Site footer -->
</footer>
</body>
</html>
Nested Autohandlers
Autohandlers can be nested for section-specific layouts:
pages/
├── autohandler # Root layout (all pages)
├── index.html
└── admin/
├── autohandler # Admin layout (only admin pages)
└── dashboard.html
The admin/dashboard.html page will be wrapped by BOTH autohandlers:
- pages/autohandler (outer)
- pages/admin/autohandler (inner)
- pages/admin/dashboard.html (content)
Pro Tip: Use autohandlers for authentication checks, authorization, and shared layouts!
Need more help?