📚 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)

Deployment → API Documentation Live API Example

Need more help?

View All Demos Getting Started GitHub Repository