📚 StoneJS Developer Guide
Complete reference for building modern web applications with the StoneJS Framework
🔌 REST APIs
Building APIs with Dhandlers
Dhandlers provide a powerful way to build RESTful APIs in StoneJS. They handle routing, HTTP methods, and can return JSON responses.
REST API HTTP Methods
| Method | Purpose | Example Usage | Status Code |
|---|---|---|---|
GET |
Retrieve data | List users, get user by ID | 200 |
POST |
Create new resource | Create new user | 201 |
PUT |
Update entire resource | Update user details | 200 |
PATCH |
Partial update | Update user email only | 200 |
DELETE |
Remove resource | Delete user | 200 |
Complete REST API Example
A full CRUD (Create, Read, Update, Delete) API implementation:
// pages/api/users/dhandler
module.exports = async (context) => {
const { req, res, db } = context;
try {
// GET /api/users - List all users
if (req.method === 'GET' && req.path === '/api/users') {
const users = await db.query('SELECT id, name, email FROM users');
return res.json({ success: true, data: users });
}
// GET /api/users/123 - Get specific user
const match = req.path.match(/^\/api\/users\/(\d+)$/);
if (req.method === 'GET' && match) {
const userId = match[1];
const user = await db.query(
'SELECT id, name, email FROM users WHERE id = $1',
[userId]
);
if (user.length === 0) {
return res.status(404).json({ error: 'User not found' });
}
return res.json({ success: true, data: user[0] });
}
// POST /api/users - Create user
if (req.method === 'POST' && req.path === '/api/users') {
const { name, email } = req.body;
const result = await db.query(
'INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id',
[name, email]
);
return res.status(201).json({
success: true,
data: { id: result[0].id, name, email }
});
}
// PUT /api/users/123 - Update user
if (req.method === 'PUT' && match) {
const userId = match[1];
const { name, email } = req.body;
await db.query(
'UPDATE users SET name = $1, email = $2 WHERE id = $3',
[name, email, userId]
);
return res.json({ success: true });
}
// DELETE /api/users/123 - Delete user
if (req.method === 'DELETE' && match) {
const userId = match[1];
await db.query('DELETE FROM users WHERE id = $1', [userId]);
return res.json({ success: true });
}
// 404 - Route not found
return res.status(404).json({ error: 'Endpoint not found' });
} catch (error) {
console.error('API Error:', error);
return res.status(500).json({ error: 'Internal server error' });
}
};
Query Parameters
// GET /api/users?role=admin&limit=10
module.exports = async (context) => {
const { req, res, db } = context;
const role = req.query.role;
const limit = parseInt(req.query.limit) || 50;
let query = 'SELECT * FROM users';
const params = [];
if (role) {
query += ' WHERE role = $1';
params.push(role);
}
query += ` LIMIT ${limit}`;
const users = await db.query(query, params);
return res.json({ data: users });
};
Authentication Middleware
// pages/api/autohandler
module.exports = async (context, next) => {
const { req, res, session } = context;
// Check authentication
if (!session.userId) {
return res.status(401).json({ error: 'Unauthorized' });
}
// Check API key
const apiKey = req.headers['x-api-key'];
if (!apiKey || !isValidApiKey(apiKey)) {
return res.status(403).json({ error: 'Forbidden' });
}
// Continue to next handler
return await next();
};
CORS Support
module.exports = async (context) => {
const { req, res } = context;
// Set CORS headers
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
// Handle preflight
if (req.method === 'OPTIONS') {
return res.status(200).end();
}
// Handle actual request
// ...
};
HTTP Status Codes
Use appropriate status codes to communicate response status to clients:
| Code | Meaning | When to Use |
|---|---|---|
200 |
OK | Successful GET, PUT, PATCH, DELETE |
201 |
Created | Successful POST that creates a resource |
400 |
Bad Request | Invalid input or validation error |
401 |
Unauthorized | Authentication required or failed |
403 |
Forbidden | Authenticated but not authorized |
404 |
Not Found | Resource or endpoint doesn't exist |
500 |
Server Error | Unexpected server error |
Response Formats
Maintain consistent JSON response structures across your API:
Success Response
res.json({
success: true,
data: { id: 1, name: 'Alice', email: '[email protected]' }
});
Error Response
res.status(400).json({
success: false,
error: 'Invalid input',
details: { field: 'email', message: 'Email is required' }
});
Paginated Response
res.json({
success: true,
data: items,
pagination: {
page: 1,
limit: 50,
total: 250,
pages: 5
}
});
💡 Pro Tip: Include a success boolean in all responses to make client-side error handling easier.
Best Practices
- Use proper HTTP status codes (200, 201, 400, 404, 500)
- Validate input data before processing
- Use try/catch for error handling
- Return consistent JSON structure
- Implement authentication/authorization
- Add rate limiting for production APIs
- Log API requests and errors
- Version your API (e.g., /api/v1/users)
Need more help?