Filter OpenAPI Script
filter-openapi.mjs adalah Node.js script yang digunakan untuk mengambil OpenAPI specification dari remote source, memfilternya berdasarkan tags yang ditentukan, dan menyimpan hasilnya ke file lokal.
📋 Overview
🔧 Fungsi Utama
Script ini melakukan proses berikut:
- Fetch OpenAPI specification dari remote URL
- Filter API endpoints berdasarkan tags yang diizinkan
- Resolve dependencies dari filtered endpoints
- Save hasil filter ke file JSON lokal
🎯 Tujuan Penggunaan
- Reduce Bundle Size: Menghindari generate code untuk endpoint yang tidak digunakan
- Faster Generation: Kubb lebih cepat memproses OpenAPI spec yang lebih kecil
- Organized Development: Fokus hanya pada API yang relevan untuk project
⚙️ Konfigurasi
Environment Variables
| Variable | Default | Description |
|---|---|---|
SWAGGER_URL | https://dev-sisappra.tsgitdev.com/api/v1/docs.json | Source OpenAPI URL |
FILTERED_PATH | ./sisappra.filtered.json | Output file path |
Tags yang Diizinkan
const KEEP_TAGS = new Set([
'Auth - Pengguna',
'Keuangan - Inventarisasi Aset',
'Keuangan - Pengelolaan Gudang Induk',
'Master Keuangan - Parameter',
'Master Keuangan - Jenis Barang',
'Master Keuangan - Kondisi Aset',
'Master Keuangan - Status Pengadaan Aset',
'Master Keuangan - Ruangan',
'Master Parameter',
'Meta Data Pelanggar',
'Laporan Satu Data Pelanggar - Surat',
]);
🔄 Alur Kerja
1. Fetch OpenAPI Specification
async function fetchSwaggerJson(url) {
console.log(`Fetching OpenAPI spec from: ${url}`);
const response = await fetch(url, {
redirect: 'follow', // Follow HTTP redirects
headers: {
'User-Agent': 'filter-openapi/1.0.0'
}
});
if (!response.ok) {
throw new Error(`Failed to fetch: ${response.status} ${response.statusText}`);
}
return await response.json();
}
2. Filter Paths by Tags
function filterOpenApiByTags(swagger, keepTags) {
const filteredPaths = {};
for (const [path, pathItem] of Object.entries(swagger.paths)) {
const filteredOperations = {};
for (const [method, operation] of Object.entries(pathItem)) {
// Skip non-operation items (parameters, etc.)
if (typeof operation !== 'object' || !operation.tags) continue;
// Check if any tag matches keepTags
if (operation.tags.some(tag => keepTags.has(tag))) {
filteredOperations[method] = operation;
}
}
// Keep path if it has filtered operations
if (Object.keys(filteredOperations).length > 0) {
filteredPaths[path] = filteredOperations;
}
}
return { ...swagger, paths: filteredPaths };
}
3. Resolve Dependencies
function resolveRefs(swagger, refsToResolve = new Set()) {
const allRefs = new Set(refsToResolve);
let changed = true;
// Iteratively resolve all transitive dependencies
while (changed) {
changed = false;
// Scan document for new references
const newRefs = extractRefs(swagger);
for (const ref of newRefs) {
if (!allRefs.has(ref)) {
allRefs.add(ref);
changed = true;
}
}
}
return allRefs;
}
4. Build Filtered Document
function buildFilteredDoc(original, filteredPaths, requiredRefs) {
const filtered = {
...original,
paths: filteredPaths
};
// Only keep required schemas
if (original.components?.schemas) {
filtered.components = {
...original.components,
schemas: {}
};
for (const ref of requiredRefs) {
const schemaName = extractSchemaName(ref);
if (original.components.schemas[schemaName]) {
filtered.components.schemas[schemaName] = original.components.schemas[schemaName];
}
}
}
return filtered;
}
🚀 Penggunaan
Basic Usage
# Run dengan default settings
node scripts/filter-openapi.mjs
# Output: ./sisappra.filtered.json
Custom Environment
# Custom OpenAPI source
export SWAGGER_URL="https://api.example.com/docs.json"
# Custom output path
export FILTERED_PATH="./custom-filtered.json"
# Run script
node scripts/filter-openapi.mjs
Integration dengan Kubb
# Complete workflow
node scripts/filter-openapi.mjs && npx kubb build --config kubb.config.ts
Package Scripts
{
"scripts": {
"api:filter": "node scripts/filter-openapi.mjs",
"api:generate": "npm run api:filter && npx kubb build",
"api:watch": "npx kubb build --watch",
"api:clean": "rm -rf sisappra.filtered.json gen && npm run api:generate"
}
}
📊 Output Structure
Generated File Format
{
"openapi": "3.0.0",
"info": {
"title": "SISAPPRA API",
"version": "1.0.0"
},
"paths": {
"/api/v1/auth/pengguna": {
"get": {
"tags": ["Auth - Pengguna"],
"summary": "Get pengguna list",
"responses": { ... }
}
}
},
"components": {
"schemas": {
"Pengguna": { ... },
"PenggunaRequest": { ... }
}
}
}
Reduction Statistics
Script ini biasanya mengurangi ukuran OpenAPI spec hingga 80-90%:
Original spec: 2.5MB (1,200+ endpoints)
Filtered spec: 350KB (150+ endpoints)
Reduction: 86% smaller
🔧 Advanced Usage
Custom Tags Filtering
Untuk menambah atau mengubah tags yang diizinkan:
const KEEP_TAGS = new Set([
// Existing tags...
'New API Group',
'Another Feature'
]);
Multiple Environment Support
// Script untuk multiple environments
const environments = {
development: 'https://dev-api.example.com/docs.json',
staging: 'https://staging-api.example.com/docs.json',
production: 'https://api.example.com/docs.json'
};
const env = process.env.NODE_ENV || 'development';
const swaggerUrl = environments[env];
Validation Script
// Tambahkan validasi setelah filter
function validateFilteredDoc(filtered, original) {
const filteredPaths = Object.keys(filtered.paths || {});
const originalTags = new Set();
// Extract all tags from original
Object.values(original.paths || {}).forEach(pathItem => {
Object.values(pathItem).forEach(operation => {
if (operation.tags) {
operation.tags.forEach(tag => originalTags.add(tag));
}
});
});
console.log(`Filtered ${filteredPaths.length} paths`);
console.log(`From ${originalTags.size} tag groups`);
return filteredPaths.length > 0;
}
🚨 Error Handling
Common Errors
Network Error:
Error: Failed to fetch: 404 Not Found
Solution: Check SWAGGER_URL environment variable
Invalid JSON:
Error: Unexpected token < in JSON at position 0
Solution: API endpoint mungkin return HTML error page
No Matching Tags:
Warning: No paths matched the keepTags
Solution: Check if tags in KEEP_TAGS match API spec
Debug Mode
// Tambahkan debug logging
function debugLog(message, data) {
if (process.env.DEBUG) {
console.log(`[DEBUG] ${message}`, data);
}
}
Error Recovery
// Retry mechanism for network failures
async function fetchWithRetry(url, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fetchSwaggerJson(url);
} catch (error) {
if (i === maxRetries - 1) throw error;
console.log(`Retry ${i + 1}/${maxRetries}...`);
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
}
}
}
🔍 Monitoring & Logging
Progress Logging
# Sample output
Fetching OpenAPI spec from: https://dev-sisappra.tsgitdev.com/api/v1/docs.json
Original spec has 1,247 paths
Filtered to 156 paths (12.5% of original)
Found 89 unique schema references
Required 45 schemas after dependency resolution
Written filtered spec to: ./sisappra.filtered.json
Statistics Collection
function collectStats(original, filtered) {
return {
originalPaths: Object.keys(original.paths || {}).length,
filteredPaths: Object.keys(filtered.paths || {}).length,
originalSchemas: Object.keys(original.components?.schemas || {}).length,
filteredSchemas: Object.keys(filtered.components?.schemas || {}).length,
reduction: Math.round(
(1 - Object.keys(filtered.paths || {}).length / Object.keys(original.paths || {}).length) * 100
)
};
}
📚 Best Practices
1. Version Control
# Add .gitignore entry
echo "sisappra.filtered.json" >> .gitignore
echo "gen/" >> .gitignore
2. CI/CD Integration
# GitHub Actions example
- name: Generate API client
run: |
npm run api:generate
git add gen/
git commit -m "chore: update generated API client"
3. Team Coordination
- Communication saat ada perubahan tags
- Documentation untuk new API endpoints
- Regular updates untuk OpenAPI spec
4. Performance Optimization
- Cache filtered spec untuk development
- Use conditional generation di CI/CD
- Monitor generation time
🔗 Integrations
Dengan Development Workflow
# Pre-commit hook
#!/bin/sh
# .git/hooks/pre-commit
npm run api:generate
git add gen/
Dengan CI/CD Pipeline
# Azure DevOps / GitHub Actions
steps:
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Generate API client
run: npm run api:generate
- name: Build application
run: npm run build
Dengan Monitoring
// Health check untuk API generation
async function healthCheck() {
try {
const spec = await fetchSwaggerJson(process.env.SWAGGER_URL);
const filtered = filterOpenApiByTags(spec, KEEP_TAGS);
if (Object.keys(filtered.paths).length === 0) {
throw new Error('No matching paths found');
}
return { status: 'healthy', paths: Object.keys(filtered.paths).length };
} catch (error) {
return { status: 'unhealthy', error: error.message };
}
}