Help us improve
Share bugs, ideas, or general feedback.
Create and manage backend servers for Obsidian plugins that need server-side processing
npx claudepluginhub jwplatta/agent-cubicle --plugin obsidian-plugin-builderHow this skill is triggered — by the user, by Claude, or both
Slash command
/obsidian-plugin-builder:plugin-backend-devThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
You are an expert in creating backend servers for Obsidian plugins.
Sets up Obsidian plugin dev environment: clones sample plugin, installs TypeScript/esbuild deps, configures manifest.json/versions.json, symlinks dev vault.
Teaches backend concepts: servers (Express.js), routes/endpoints with HTTP methods, REST APIs, request/response cycles, middleware, .env security, and CORS. Activates for server-side code, API routes, or Express/Fastify/Next.js handlers.
Creates p5.js generative art with seeded randomness, noise fields, and interactive parameter exploration. Use for algorithmic art, flow fields, or particle systems.
Share bugs, ideas, or general feedback.
You are an expert in creating backend servers for Obsidian plugins.
plugin-root/
├── plugin/ # Obsidian plugin
│ ├── main.ts
│ └── ...
└── server/ # Backend server
├── src/
│ ├── server.ts # Server entry point
│ ├── routes/
│ └── services/
├── package.json
├── tsconfig.json
└── .env
{
"name": "plugin-server",
"version": "1.0.0",
"main": "dist/server.js",
"scripts": {
"dev": "ts-node-dev --respawn src/server.ts",
"build": "tsc",
"start": "node dist/server.js"
},
"dependencies": {
"express": "^4.18.2",
"cors": "^2.8.5",
"dotenv": "^16.0.3"
},
"devDependencies": {
"@types/express": "^4.17.17",
"@types/cors": "^2.8.13",
"@types/node": "^18.15.0",
"ts-node-dev": "^2.0.0",
"typescript": "^5.0.0"
}
}
import express, { Request, Response } from 'express';
import cors from 'cors';
import dotenv from 'dotenv';
dotenv.config();
const app = express();
const PORT = process.env.PORT || 3000;
// Middleware
app.use(cors());
app.use(express.json());
// Health check
app.get('/health', (req: Request, res: Response) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() });
});
// Example API endpoint
app.post('/api/process', async (req: Request, res: Response) => {
try {
const { data } = req.body;
const result = await processData(data);
res.json({ success: true, result });
} catch (error) {
console.error('Error:', error);
res.status(500).json({
success: false,
error: error.message
});
}
});
async function processData(data: any): Promise<any> {
// Process data here
return data;
}
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
import Fastify from 'fastify';
import cors from '@fastify/cors';
const fastify = Fastify({ logger: true });
fastify.register(cors);
fastify.get('/health', async (request, reply) => {
return { status: 'ok' };
});
fastify.post('/api/process', async (request, reply) => {
const { data } = request.body as any;
const result = await processData(data);
return { success: true, result };
});
const start = async () => {
try {
await fastify.listen({ port: 3000 });
} catch (err) {
fastify.log.error(err);
process.exit(1);
}
};
start();
Flask==2.3.0
Flask-CORS==4.0.0
python-dotenv==1.0.0
from flask import Flask, request, jsonify
from flask_cors import CORS
import os
from dotenv import load_dotenv
load_dotenv()
app = Flask(__name__)
CORS(app)
@app.route('/health', methods=['GET'])
def health():
return jsonify({'status': 'ok'})
@app.route('/api/process', methods=['POST'])
def process():
try:
data = request.json.get('data')
result = process_data(data)
return jsonify({'success': True, 'result': result})
except Exception as e:
return jsonify({'success': False, 'error': str(e)}), 500
def process_data(data):
# Process data here
return data
if __name__ == '__main__':
port = int(os.getenv('PORT', 3000))
app.run(host='0.0.0.0', port=port, debug=True)
export class BackendService {
private baseUrl: string;
private isHealthy: boolean = false;
constructor(baseUrl: string = 'http://localhost:3000') {
this.baseUrl = baseUrl;
this.startHealthCheck();
}
async checkHealth(): Promise<boolean> {
try {
const response = await fetch(`${this.baseUrl}/health`, {
method: 'GET',
signal: AbortSignal.timeout(5000)
});
this.isHealthy = response.ok;
return this.isHealthy;
} catch {
this.isHealthy = false;
return false;
}
}
async processData(data: any): Promise<any> {
if (!this.isHealthy) {
throw new Error('Backend server is not available');
}
const response = await fetch(`${this.baseUrl}/api/process`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ data })
});
if (!response.ok) {
throw new Error(`Server error: ${response.statusText}`);
}
const result = await response.json();
if (!result.success) {
throw new Error(result.error || 'Unknown error');
}
return result.result;
}
private startHealthCheck(): void {
// Initial check
this.checkHealth();
// Periodic checks
setInterval(() => this.checkHealth(), 30000);
}
}
// Server endpoint
app.post('/api/process-file', async (req, res) => {
const { content, filename } = req.body;
const result = await processFile(content, filename);
res.json({ result });
});
// Plugin side
async processFile(file: TFile): Promise<any> {
const content = await this.app.vault.read(file);
return await this.backend.processData({
content,
filename: file.name
});
}
// Server with queue
import Queue from 'bull';
const processingQueue = new Queue('processing');
processingQueue.process(async (job) => {
return processData(job.data);
});
app.post('/api/batch', async (req, res) => {
const { items } = req.body;
const jobs = await Promise.all(
items.map(item => processingQueue.add(item))
);
res.json({ jobIds: jobs.map(j => j.id) });
});
// Server with streaming
app.get('/api/stream', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
const interval = setInterval(() => {
res.write(`data: ${JSON.stringify({ time: Date.now() })}\n\n`);
}, 1000);
req.on('close', () => {
clearInterval(interval);
});
});
cd server
npm run dev
curl http://localhost:3000/health
curl -X POST http://localhost:3000/api/process \
-H "Content-Type: application/json" \
-d '{"data": "test"}'
npm run build
npm start
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY dist ./dist
EXPOSE 3000
CMD ["node", "dist/server.js"]
version: '3.8'
services:
server:
build: ./server
ports:
- "3000:3000"
environment:
- PORT=3000
- NODE_ENV=production
restart: unless-stopped
When creating a backend: