REST API
Create custom REST API endpoints for your plugins.
Registering Endpoints
sdk.rest.register(method, path, handler)
Create a new API endpoint.
// GET endpoint
sdk.rest.register('GET', '/api/my-plugin/items', async (req, res) => {
const items = await sdk.storage.get('items') || [];
return res.json({
success: true,
data: items
});
});
// POST endpoint
sdk.rest.register('POST', '/api/my-plugin/items', async (req, res) => {
const { name, value } = req.body;
const items = await sdk.storage.get('items') || [];
const newItem = { id: Date.now(), name, value };
items.push(newItem);
await sdk.storage.set('items', items);
return res.json({
success: true,
data: newItem
});
});
// PUT endpoint
sdk.rest.register('PUT', '/api/my-plugin/items/:id', async (req, res) => {
const { id } = req.params;
const { name, value } = req.body;
const items = await sdk.storage.get('items') || [];
const index = items.findIndex(i => i.id === parseInt(id));
if (index === -1) {
return res.status(404).json({
success: false,
error: 'Item not found'
});
}
items[index] = { ...items[index], name, value };
await sdk.storage.set('items', items);
return res.json({
success: true,
data: items[index]
});
});
// DELETE endpoint
sdk.rest.register('DELETE', '/api/my-plugin/items/:id', async (req, res) => {
const { id } = req.params;
let items = await sdk.storage.get('items') || [];
items = items.filter(i => i.id !== parseInt(id));
await sdk.storage.set('items', items);
return res.json({
success: true
});
});Request Object
The request object contains:
sdk.rest.register('POST', '/api/my-endpoint', async (req, res) => {
// URL parameters
req.params.id // From /api/items/:id
// Query string
req.query.page // From ?page=2
req.query.search // From ?search=hello
// Request body (for POST/PUT)
req.body.name
req.body.data
// Headers
req.headers['content-type']
req.headers.authorization
// Cookies
req.cookies.sessionId
// User (if authenticated)
req.user // { id, email, role, ... }
// Files (for multipart uploads)
req.files // Array of uploaded files
});Response Methods
sdk.rest.register('GET', '/api/example', async (req, res) => {
// JSON response
return res.json({ success: true, data: {} });
// With status code
return res.status(201).json({ created: true });
// Error responses
return res.status(400).json({ error: 'Invalid input' });
return res.status(401).json({ error: 'Unauthorized' });
return res.status(404).json({ error: 'Not found' });
return res.status(500).json({ error: 'Server error' });
// Plain text
return res.send('Hello World');
// HTML
return res.send('<h1>Hello</h1>');
// Redirect
return res.redirect('/new-location');
// Set headers
res.setHeader('X-Custom-Header', 'value');
return res.json({ data: true });
// Set cookie
res.cookie('name', 'value', { maxAge: 86400000 });
return res.json({ success: true });
});Authentication
Require Authentication
sdk.rest.register('GET', '/api/my-plugin/private', async (req, res) => {
// Check if user is logged in
if (!req.user) {
return res.status(401).json({ error: 'Authentication required' });
}
// User is authenticated
return res.json({ user: req.user });
}, {
auth: true // Require authentication
});Require Admin Role
sdk.rest.register('POST', '/api/my-plugin/admin-action', async (req, res) => {
// Check admin role
if (req.user.role !== 'admin') {
return res.status(403).json({ error: 'Admin access required' });
}
// Perform admin action
return res.json({ success: true });
}, {
auth: true,
roles: ['admin']
});Validation
sdk.rest.register('POST', '/api/contact', async (req, res) => {
const { name, email, message } = req.body;
// Manual validation
const errors = [];
if (!name || name.trim().length < 2) {
errors.push({ field: 'name', message: 'Name is required' });
}
if (!email || !email.includes('@')) {
errors.push({ field: 'email', message: 'Valid email is required' });
}
if (!message || message.trim().length < 10) {
errors.push({ field: 'message', message: 'Message must be at least 10 characters' });
}
if (errors.length > 0) {
return res.status(400).json({
success: false,
errors
});
}
// Process valid data
// ...
return res.json({ success: true });
});File Uploads
sdk.rest.register('POST', '/api/my-plugin/upload', async (req, res) => {
if (!req.files || req.files.length === 0) {
return res.status(400).json({ error: 'No file uploaded' });
}
const file = req.files[0];
// file.originalname - original filename
// file.mimetype - MIME type
// file.size - file size in bytes
// file.buffer - file content
// Validate file type
const allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
if (!allowedTypes.includes(file.mimetype)) {
return res.status(400).json({ error: 'Invalid file type' });
}
// Validate file size (5MB limit)
if (file.size > 5 * 1024 * 1024) {
return res.status(400).json({ error: 'File too large' });
}
// Save using Media API
const result = await sdk.media.upload(file, {
folder: 'my-plugin/uploads'
});
return res.json({
success: true,
file: result
});
});Rate Limiting
sdk.rest.register('POST', '/api/my-plugin/send', async (req, res) => {
// ... handler
}, {
rateLimit: {
max: 10, // Maximum requests
window: 60000 // Per minute (ms)
}
});Example: Complete CRUD API
module.exports = async function(sdk) {
const COLLECTION = 'bookmarks';
// List all bookmarks
sdk.rest.register('GET', '/api/bookmarks', async (req, res) => {
const { page = 1, limit = 20, search = '' } = req.query;
let bookmarks = await sdk.storage.get(COLLECTION) || [];
// Search filter
if (search) {
const searchLower = search.toLowerCase();
bookmarks = bookmarks.filter(b =>
b.title.toLowerCase().includes(searchLower) ||
b.url.toLowerCase().includes(searchLower)
);
}
// Pagination
const total = bookmarks.length;
const start = (page - 1) * limit;
const items = bookmarks.slice(start, start + limit);
return res.json({
success: true,
data: {
items,
total,
page: parseInt(page),
pages: Math.ceil(total / limit)
}
});
});
// Get single bookmark
sdk.rest.register('GET', '/api/bookmarks/:id', async (req, res) => {
const bookmarks = await sdk.storage.get(COLLECTION) || [];
const bookmark = bookmarks.find(b => b.id === req.params.id);
if (!bookmark) {
return res.status(404).json({
success: false,
error: 'Bookmark not found'
});
}
return res.json({
success: true,
data: bookmark
});
});
// Create bookmark
sdk.rest.register('POST', '/api/bookmarks', async (req, res) => {
const { title, url, tags = [] } = req.body;
// Validation
if (!title || !url) {
return res.status(400).json({
success: false,
error: 'Title and URL are required'
});
}
const bookmarks = await sdk.storage.get(COLLECTION) || [];
const newBookmark = {
id: sdk.utils.generateId(),
title,
url,
tags,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString()
};
bookmarks.push(newBookmark);
await sdk.storage.set(COLLECTION, bookmarks);
return res.status(201).json({
success: true,
data: newBookmark
});
}, { auth: true });
// Update bookmark
sdk.rest.register('PUT', '/api/bookmarks/:id', async (req, res) => {
const { title, url, tags } = req.body;
const bookmarks = await sdk.storage.get(COLLECTION) || [];
const index = bookmarks.findIndex(b => b.id === req.params.id);
if (index === -1) {
return res.status(404).json({
success: false,
error: 'Bookmark not found'
});
}
bookmarks[index] = {
...bookmarks[index],
...(title && { title }),
...(url && { url }),
...(tags && { tags }),
updatedAt: new Date().toISOString()
};
await sdk.storage.set(COLLECTION, bookmarks);
return res.json({
success: true,
data: bookmarks[index]
});
}, { auth: true });
// Delete bookmark
sdk.rest.register('DELETE', '/api/bookmarks/:id', async (req, res) => {
let bookmarks = await sdk.storage.get(COLLECTION) || [];
const initialLength = bookmarks.length;
bookmarks = bookmarks.filter(b => b.id !== req.params.id);
if (bookmarks.length === initialLength) {
return res.status(404).json({
success: false,
error: 'Bookmark not found'
});
}
await sdk.storage.set(COLLECTION, bookmarks);
return res.json({
success: true
});
}, { auth: true, roles: ['admin'] });
return {};
};Using from Frontend
// Fetch bookmarks
const response = await fetch('/api/bookmarks?page=1&limit=10');
const data = await response.json();
// Create bookmark
await fetch('/api/bookmarks', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
title: 'My Bookmark',
url: 'https://example.com'
})
});
// Delete bookmark
await fetch('/api/bookmarks/abc123', {
method: 'DELETE'
});