đ Include Functionality Demo
What is include()?
The include() function allows you to embed one component inside another.
This is perfect for reusing code, creating modular components, and building complex pages from simple parts.
1. Basic Include - Simple Component
Included Component Output:
Simple Info Component
This is a reusable component!
It was included using include('/components/simple-info.html')
Rendered at: 27/12/2025, 4:44:15 am
Request path: /demo/components/simple-info.html
2. Include with Parameters
Included Component Output:
3. Multiple Includes - Different Parameters
You can include the same component multiple times with different parameters:
Included Components Output:
4. Include with Database Access
Included components have full access to $db, $cache, and all other global context:
Included Component Output:
StoneJS Framework
Database Integration Demo
This page demonstrates how to connect to PostgreSQL and query data using the $db object.
â Database Connected!
| Database: | ddata |
| Host: | pgsql-prod01.postgres.database.azure.com:5432 |
| Query Time: | 43ms |
| Total Records: | 400713 |
Query Results
Showing 41 - 50 of 400713 records
| RID | Operation | Table Name | User ID | Description | Created |
|---|---|---|---|---|---|
| 400678 | INSERT | chat_sessions | ddai_access_rw | 10/11/2025, 10:07:01 am | |
| 400677 | INSERT | chat_sessions | ddai_access_rw | 10/11/2025, 10:05:32 am | |
| 400676 | INSERT | chat_sessions | ddai_access_rw | 10/11/2025, 10:04:58 am | |
| 400675 | INSERT | chat_sessions | ddai_access_rw | 10/11/2025, 10:04:58 am | |
| 400674 | INSERT | chat_sessions | ddai_access_rw | 10/11/2025, 10:04:16 am | |
| 400673 | INSERT | chat_sessions | ddai_access_rw | 10/11/2025, 10:03:45 am | |
| 400672 | INSERT | chat_sessions | ddai_access_rw | 10/11/2025, 9:55:18 am | |
| 400671 | INSERT | chat_sessions | ddai_access_rw | 10/11/2025, 9:48:50 am | |
| 400670 | INSERT | chat_sessions | ddai_access_rw | 10/11/2025, 9:43:52 am | |
| 400669 | INSERT | chat_sessions | ddai_access_rw | 10/11/2025, 9:38:11 am |
Database API Reference
The $db object is available in all components and provides two main methods for database operations.
1. $db.query() - Standard Queries
Use for regular database operations without row-level security.
Single Query
Multiple Queries (Array Syntax)
Execute multiple queries on the same connection. Returns array of results.
Transactions
Wrap operations in BEGIN/COMMIT. All succeed or all fail together.
2. $db.secureQuery() - Row-Level Security
Use for tables with PostgreSQL RLS enabled. Automatically manages session variables in a transaction.
Single Session Variable
Multiple Session Variables
Multiple Queries with RLS
How secureQuery() Works
Automatic Session Variable Management:
- Gets a dedicated connection from the pool
- Starts a transaction (BEGIN)
- Sets session variable(s) with SET LOCAL
- Executes your query/queries
- Commits the transaction (automatically clears SET LOCAL variables)
- Releases the connection back to the pool
Why this matters: Session variables are scoped to the transaction, preventing variable leakage between requests.
Setting up Row-Level Security in PostgreSQL
â ī¸ Important: Regular $db.query() doesn't set session variables. Always use $db.secureQuery() for RLS-enabled tables, or RLS policies will block all rows.
Configuration
Database configuration is loaded from your .env file:
Return Value Format
Important: StoneJS $db.query() returns rows directly, not wrapped in a .rows property like the native pg library.
| Library | Return Format |
|---|---|
pg (native) |
result.rows |
$db.query() |
result (array directly) |
Best Practices
- Always use parameterized queries - Use
$1, $2placeholders to prevent SQL injection - Use secureQuery() for RLS tables - Automatically manages session variables
- Use array syntax for related queries - Ensures they run on the same connection
- Use transactions for atomic operations - Wrap in BEGIN/COMMIT
- Handle errors gracefully - Use try/catch blocks
- Combine with caching - Use
$cache.remember()for expensive queries
Example: Caching Database Results
Quick Reference Table
| Feature | $db.query() | $db.secureQuery() |
|---|---|---|
| Single query | â Yes | â Yes |
| Multiple queries | â Array syntax | â Array syntax |
| Transactions | â Manual BEGIN/COMMIT | â Automatic wrapper |
| Session variables | â No | â Automatic SET LOCAL |
| Use case | Standard queries | RLS-enabled tables |
| Connection handling | Automatic pool management | Dedicated connection per call |
Session Stats: You've viewed this page 1 time(s).
5. Include with Session Access
Included components can read and write session data:
Included Component Output:
Session Information Component
| Session ID: | S2gIfi4tB_ugkHD9gdLLKgbNPz6xfpLs |
| Visit Count: | 0 |
| User Name: | Not set |
âšī¸ This component has full access to the global $session object
6. đĨ NEW: Dhandler Resolution
Major Feature: The include() function now supports dhandler resolution, just like HTTP requests!
When you include a non-existent file path, StoneJS will search up the directory tree for a dhandler to handle it.
How It Works
Let's include a non-existent CSS file. StoneJS will find the /demo/css/dhandler and use it:
What happens:
- StoneJS looks for
/demo/css/imaginary-file-12345.cssâ not found - Searches up the tree for a dhandler â finds
/demo/css/dhandler - Executes the dhandler with
$context.dhandlerPath = 'imaginary-file-12345.css' - Returns the rendered output
Live Demo: CSS Dhandler
Including: /demo/css/test-1766771055940.css
This file doesn't exist, but the dhandler will handle it!
â Success! Received 21636 characters of CSS from the dhandler.
First 500 characters of output:
Comparison: HTTP vs Include
Both methods now work identically:
| Method | Example | Behavior |
|---|---|---|
| HTTP Request | GET /demo/css/abc123.css | â Dhandler handles it |
| Include Function | <%- await include('/demo/css/abc123.css') %> |
â Dhandler handles it |
Use Cases for Dhandler Includes
/css/style.<%= hash %>.css
/images/photo-400x300.jpg
/posts/<%= slug %>.html
/js/app.<%= version %>.js
â ī¸ Important: Dhandler Design for Includes
When creating dhandlers that will be used with include():
- â DO: Return content via EJS templates or plain text
- â DON'T: Call
$res.send()or$res.json()(these are for HTTP responses only)
7. Include Paths - Absolute vs Relative
Path Types:
Absolute Path: Starts with / and is relative to the pages root directory
Relative Path: Relative to the current component's directory
đ Key Points
How Include Works:
- â Always async: Must use
await include() - â Context sharing: Included components have access to $req, $res, $session, $db, $cache, $env
- â Parameters: Pass custom data as the second argument
- â Reusable: Same component can be included multiple times with different parameters
- â HTML output: Use
<%-(unescaped) to render included HTML - â Autohandlers: Included components execute with their own autohandlers
- đĨ Dhandler support: Non-existent paths are resolved through dhandlers, just like HTTP requests!
Best Practices:
- Create reusable components in a
/componentsdirectory - Design components to accept parameters for flexibility
- Keep components focused on one purpose
- Use descriptive component names
- Handle missing parameters gracefully with defaults
Common Use Cases:
- đ¨ Header, footer, navigation components
- đ Data display widgets (cards, tables, charts)
- đ Form elements and input groups
- đ Alert boxes and notifications
- đĻ Product cards, user profiles, etc.