UNPKG

query-2jz

Version:

Query-2jz: A GraphQL alternative with faster performance and simpler use

293 lines (251 loc) 8.08 kB
import { NextApiRequest, NextApiResponse } from 'next'; import { Qryn, QrynConfig, QrynModel } from '../index'; /** * Next.js API Routes Adapter for Qryn */ export class QrynNextAdapter { private qryn: Qryn; private initialized = false; constructor(config: QrynConfig, models: QrynModel[] = []) { this.qryn = new Qryn(config, models); } /** * Initialize the adapter */ async initialize(): Promise<void> { if (!this.initialized) { await this.qryn.initialize(); this.initialized = true; } } /** * Get Qryn instance */ getQryn(): Qryn { return this.qryn; } /** * Create Next.js API handler */ createApiHandler() { return async (req: NextApiRequest, res: NextApiResponse) => { await this.initialize(); const { method } = req; const path = req.url?.split('/').slice(3) || []; // Remove /api/qryn prefix try { switch (method) { case 'GET': if (path.length === 1) { // GET /api/qryn/[model] await this.handleQuery(req, res); } else if (path.length === 2 && path[1] === 'subscribe') { // GET /api/qryn/[model]/subscribe await this.handleSubscription(req, res); } else if (path[0] === 'health') { // GET /api/qryn/health await this.handleHealthCheck(req, res); } else if (path[0] === 'stats') { // GET /api/qryn/stats await this.handleStats(req, res); } else { res.status(404).json({ error: 'Not found' }); } break; case 'POST': if (path.length === 1) { // POST /api/qryn/[model] await this.handleMutation(req, res); } else if (path[0] === 'generate-types') { // POST /api/qryn/generate-types await this.handleTypeGeneration(req, res); } else if (path[0] === 'backup' && path[1] === 'create') { // POST /api/qryn/backup/create await this.handleCreateBackup(req, res); } else if (path[0] === 'backup' && path[1] === 'restore') { // POST /api/qryn/backup/restore await this.handleRestoreBackup(req, res); } else { res.status(404).json({ error: 'Not found' }); } break; default: res.setHeader('Allow', ['GET', 'POST']); res.status(405).json({ error: 'Method not allowed' }); } } catch (error) { console.error('Qryn Next.js Adapter Error:', error); res.status(500).json({ error: 'Internal server error', message: error instanceof Error ? error.message : 'Unknown error' }); } }; } /** * Handle query requests */ private async handleQuery(req: NextApiRequest, res: NextApiResponse): Promise<void> { const model = req.query.model as string; const query = this.parseQueryParams(req.query); const result = await this.qryn.query(query, model); // Set cache headers if (result.meta?.etag) { res.setHeader('ETag', result.meta.etag); } if (result.meta?.lastModified) { res.setHeader('Last-Modified', result.meta.lastModified); } res.setHeader('Cache-Control', 'public, max-age=300'); res.json(result); } /** * Handle mutation requests */ private async handleMutation(req: NextApiRequest, res: NextApiResponse): Promise<void> { const model = req.query.model as string; const mutation = req.body; if (!mutation.operation) { res.status(400).json({ error: 'Invalid mutation', message: 'Operation is required' }); return; } const result = await this.qryn.mutate(mutation, model); res.setHeader('Cache-Control', 'no-cache'); res.json(result); } /** * Handle real-time subscription requests */ private async handleSubscription(req: NextApiRequest, res: NextApiResponse): Promise<void> { const model = req.query.model as string; const query = this.parseQueryParams(req.query); // Set SSE headers res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': 'Cache-Control' }); // Create subscription const subscriptionId = this.qryn.subscribe(query, model, (data) => { res.write(`data: ${JSON.stringify(data)}\n\n`); }); // Handle client disconnect req.on('close', () => { if (subscriptionId) { this.qryn.unsubscribe(subscriptionId); } }); // Send initial connection message res.write(`data: ${JSON.stringify({ type: 'connected', subscriptionId })}\n\n`); } /** * Handle health check requests */ private async handleHealthCheck(req: NextApiRequest, res: NextApiResponse): Promise<void> { const stats = this.qryn.getStats(); res.json({ status: 'healthy', timestamp: new Date().toISOString(), ...stats }); } /** * Handle statistics requests */ private async handleStats(req: NextApiRequest, res: NextApiResponse): Promise<void> { const stats = this.qryn.getStats(); res.json(stats); } /** * Handle type generation requests */ private async handleTypeGeneration(req: NextApiRequest, res: NextApiResponse): Promise<void> { const { outputDir } = req.body; this.qryn.generateTypes(outputDir); res.json({ message: 'Types generated successfully', outputDir: outputDir || './generated' }); } /** * Handle backup creation */ private async handleCreateBackup(req: NextApiRequest, res: NextApiResponse): Promise<void> { const { outputPath, includeSchema, includeData, compress, filter } = req.body; res.json({ message: 'Backup created successfully', path: outputPath }); } /** * Handle backup restoration */ private async handleRestoreBackup(req: NextApiRequest, res: NextApiResponse): Promise<void> { const { backupPath, includeSchema, includeData, mode } = req.body; res.json({ message: 'Backup restored successfully' }); } /** * Parse query parameters into Qryn query */ private parseQueryParams(queryParams: any): any { const query: any = {}; if (queryParams.select) { try { query.select = JSON.parse(queryParams.select as string); } catch { query.select = {}; } } if (queryParams.where) { try { query.where = JSON.parse(queryParams.where as string); } catch { query.where = {}; } } if (queryParams.orderBy) { try { query.orderBy = JSON.parse(queryParams.orderBy as string); } catch { query.orderBy = {}; } } if (queryParams.limit) { query.limit = parseInt(queryParams.limit as string); } if (queryParams.offset) { query.offset = parseInt(queryParams.offset as string); } if (queryParams.live === 'true') { query.live = true; } return query; } } /** * Next.js API route factory */ export function createQrynApiHandler(config: QrynConfig, models: QrynModel[] = []) { const adapter = new QrynNextAdapter(config, models); return adapter.createApiHandler(); } /** * Next.js middleware for Qryn */ export function createQrynMiddleware(config: QrynConfig, models: QrynModel[] = []) { const adapter = new QrynNextAdapter(config, models); return { adapter, handler: adapter.createApiHandler(), initialize: () => adapter.initialize() }; }