Create and manage backend servers for Obsidian plugins that need server-side processing
From obsidian-plugin-buildernpx claudepluginhub jwplatta/agent-cubicle --plugin obsidian-plugin-builderThis skill uses the workspace's default tool permissions.
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Migrates code, prompts, and API calls from Claude Sonnet 4.0/4.5 or Opus 4.1 to Opus 4.5, updating model strings on Anthropic, AWS, GCP, Azure platforms.
Analyzes BMad project state from catalog CSV, configs, artifacts, and query to recommend next skills or answer questions. Useful for help requests, 'what next', or starting BMad.
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: