Apidog

Platform Pengembangan API Kolaboratif All-in-one

Desain API

Dokumentasi API

Debug API

Mocking API

Pengujian Otomatis

Cara Menggunakan Spectral dengan TypeScript

Mikael Svenson

Mikael Svenson

Updated on May 19, 2025

Menjaga konsistensi, kualitas, dan kepatuhan terhadap standar desain sangat penting bagi tim yang menggunakan API. Spesifikasi API seperti OpenAPI dan AsyncAPI menyediakan cetak biru (blueprint), tetapi memastikan cetak biru ini diikuti dengan benar di berbagai layanan dan tim bisa menjadi tantangan yang menakutkan. Di sinilah alat linting API berperan, dan Spectral menonjol sebagai opsi sumber terbuka yang fleksibel dan kuat. Ketika dikombinasikan dengan TypeScript, Spectral memberdayakan pengembang untuk membuat aturan kustom yang tangguh dan aman tipe (type-safe), meningkatkan tata kelola API ke tingkat yang baru.

Tutorial ini akan memandu Anda melalui proses memanfaatkan Spectral dengan TypeScript, mulai dari pengaturan awal hingga membuat logika validasi kustom yang canggih. Kita akan menjelajahi bagaimana TypeScript meningkatkan pengembangan aturan Spectral, menghasilkan solusi linting API yang lebih mudah dipelihara dan lebih andal.

💡
Ingin alat Pengujian API hebat yang menghasilkan Dokumentasi API yang indah?

Ingin platform Terintegrasi, All-in-One untuk Tim Pengembang Anda bekerja sama dengan produktivitas maksimum?

Apidog memenuhi semua permintaan Anda, dan menggantikan Postman dengan harga yang jauh lebih terjangkau!
button

Memahami Spectral: Penjaga Spesifikasi API Anda

Sebelum masuk ke integrasi TypeScript, mari kita pahami apa itu Spectral dan mengapa ini adalah alat yang berharga dalam toolkit pengembangan API Anda.

Spectral adalah linter JSON/YAML sumber terbuka dengan fokus utama pada format deskripsi API seperti OpenAPI (v2 dan v3) dan AsyncAPI. Tujuannya adalah untuk membantu menegakkan pedoman desain API, mendeteksi kesalahan umum, dan memastikan konsistensi di seluruh lanskap API Anda. Anggap saja sebagai ESLint atau TSLint, tetapi khusus untuk kontrak API Anda.

Manfaat Utama Menggunakan Spectral:

  • Konsistensi: Menegakkan desain API yang seragam di seluruh tim dan proyek.
  • Jaminan Kualitas: Menangkap kesalahan dan praktik buruk sejak dini dalam siklus pengembangan.
  • Peningkatan Kolaborasi: Memberikan pemahaman bersama tentang standar API.
  • Otomasi: Terintegrasi dengan mulus ke dalam pipeline CI/CD untuk validasi otomatis.
  • Ekstensibilitas: Memungkinkan pembuatan aturan kustom yang disesuaikan dengan kebutuhan organisasi tertentu.
  • Inti yang Agnostik Format: Meskipun unggul dalam OpenAPI/AsyncAPI, intinya dapat melakukan linting pada struktur JSON/YAML apa pun.

Spectral beroperasi berdasarkan ruleset. Ruleset adalah kumpulan aturan, di mana setiap aturan menargetkan bagian tertentu dari dokumen API Anda (menggunakan ekspresi JSONPath) dan menerapkan logika validasi. Spectral dilengkapi dengan ruleset bawaan (misalnya, spectral:oas untuk standar OpenAPI), tetapi kekuatan sebenarnya terletak pada kemampuan untuk menentukan ruleset kustom.

Mengapa TypeScript untuk Aturan Kustom Spectral?

Meskipun ruleset Spectral dapat didefinisikan dalam YAML atau JavaScript (file .js), menggunakan TypeScript untuk mengembangkan fungsi kustom menawarkan keuntungan signifikan:

  • Keamanan Tipe (Type Safety): Pengetikan statis TypeScript menangkap kesalahan pada waktu kompilasi, mengurangi kejutan runtime dalam logika linting kustom Anda. Ini sangat penting untuk aturan yang kompleks.
  • Pengalaman Pengembang yang Lebih Baik: Pelengkapan otomatis, kemampuan refactoring, dan navigasi kode yang lebih baik di IDE membuat penulisan dan pemeliharaan fungsi kustom menjadi lebih mudah.
  • Keterbacaan dan Kemudahan Pemeliharaan yang Ditingkatkan: Tipe eksplisit membuat tujuan dan struktur fungsi kustom Anda lebih jelas, terutama untuk tim.
  • Fitur JavaScript Modern: Manfaatkan fitur ES modern dengan percaya diri, karena TypeScript dikompilasi menjadi JavaScript yang kompatibel.
  • Kemampuan Uji yang Lebih Baik: Pengetikan memudahkan penulisan unit test yang tangguh untuk fungsi Spectral kustom Anda.

Dengan menulis fungsi Spectral kustom Anda dalam TypeScript, Anda membawa ketelitian dan manfaat perkakas yang sama ke kode tata kelola API Anda seperti yang Anda lakukan pada kode aplikasi Anda.

Menyiapkan Lingkungan Spectral dan TypeScript Anda

Mari kita mulai dan siapkan alat yang diperlukan.

Prasyarat:

  • Node.js dan npm (atau yarn): Spectral adalah aplikasi Node.js. Pastikan Anda telah menginstal Node.js (versi LTS direkomendasikan) dan npm (atau yarn).
  • Proyek TypeScript: Anda memerlukan proyek TypeScript atau bersedia menyiapkannya.

Langkah-langkah Instalasi:

Pertama, Anda memerlukan CLI Spectral untuk menjalankan operasi linting dan menguji aturan Anda. Seringkali berguna untuk menginstalnya secara global atau menggunakan npx.Bash

npm install -g @stoplight/spectral-cli
# atau
yarn global add @stoplight/spectral-cli

Untuk mengembangkan aturan kustom secara terprogram dan menggunakan pustaka inti Spectral dalam proyek TypeScript, instal paket Spectral yang diperlukan:Bashnpm install @stoplight/spectral-core @stoplight/spectral-functions @stoplight/spectral-rulesets typescript ts-node --save-dev # atau yarn add @stoplight/spectral-core @stoplight/spectral-functions @stoplight/spectral-rulesets typescript ts-node --dev

Mari kita uraikan paket-paket ini:

  • @stoplight/spectral-core: Inti dari Spectral, berisi mesin linting.
  • @stoplight/spectral-functions: Menyediakan kumpulan fungsi bawaan yang dapat digunakan aturan Anda (misalnya, alphabetical, defined, pattern, truthy, xor).
  • @stoplight/spectral-rulesets: Menawarkan ruleset yang telah ditentukan sebelumnya seperti spectral:oas (untuk OpenAPI) dan spectral:asyncapi.
  • typescript: Kompilator TypeScript.
  • ts-node: Memungkinkan Anda menjalankan file TypeScript secara langsung tanpa pra-kompilasi, berguna untuk pengembangan.

Konfigurasi TypeScript:

Buat file tsconfig.json di root proyek Anda jika Anda belum memilikinya. Konfigurasi dasar mungkin terlihat seperti ini:JSON

{
  "compilerOptions": {
    "target": "es2020", // Atau versi yang lebih baru
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "outDir": "./dist", // Direktori output untuk JavaScript yang dikompilasi
    "rootDir": "./src", // Direktori sumber untuk file TypeScript Anda
    "resolveJsonModule": true // Memungkinkan mengimpor file JSON
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "**/*.spec.ts"]
}

Sesuaikan outDir dan rootDir sesuai dengan struktur proyek Anda. Kita akan mengasumsikan fungsi TypeScript kustom Anda akan berada di direktori src.

Konsep Inti Spectral: Aturan, Ruleset, dan Fungsi

Sebelum menulis fungsi TypeScript, mari kita perkuat pemahaman kita tentang komponen utama Spectral.

Aturan:

Sebuah aturan mendefinisikan pemeriksaan spesifik yang akan dilakukan. Properti utama dari sebuah aturan meliputi:

  • description: Penjelasan aturan yang dapat dibaca manusia.
  • message: Pesan kesalahan atau peringatan yang ditampilkan jika aturan dilanggar. Dapat menyertakan placeholder seperti {{error}}, {{path}}, {{value}}.
  • severity: Mendefinisikan dampak pelanggaran aturan. Dapat berupa error, warn, info, atau hint.
  • given: Ekspresi JSONPath (atau array darinya) yang menentukan bagian mana dari dokumen yang berlaku untuk aturan.
  • then: Mendefinisikan tindakan yang akan diambil pada nilai yang ditargetkan. Ini biasanya melibatkan penerapan satu atau lebih fungsi.
  • function: Nama fungsi bawaan atau kustom yang akan dieksekusi.
  • functionOptions: Opsi untuk diteruskan ke fungsi.
  • formats: Array yang menentukan format dokumen mana yang berlaku untuk aturan ini (misalnya, oas3, oas2, asyncapi2).

Ruleset:

Ruleset adalah file YAML atau JavaScript (misalnya, .spectral.yaml, .spectral.js, atau .spectral.ts ketika dikompilasi) yang mengelompokkan aturan. Ia juga dapat:

  • extends: Mewarisi aturan dari ruleset lain (misalnya, ruleset Spectral bawaan atau ruleset organisasi bersama).
  • rules: Objek yang berisi definisi aturan kustom Anda.
  • functionsDir: Menentukan direktori tempat file fungsi JavaScript kustom berada.
  • functions: Array fungsi kustom (kurang umum saat menggunakan functionsDir atau pengaturan terprogram).

Fungsi:

Fungsi adalah unit logika inti yang melakukan validasi sebenarnya. Spectral menyediakan banyak fungsi bawaan seperti:

  • truthy: Memeriksa apakah suatu nilai adalah truthy.
  • falsy: Memeriksa apakah suatu nilai adalah falsy.
  • defined: Memeriksa apakah suatu properti didefinisikan.
  • undefined: Memeriksa apakah suatu properti tidak didefinisikan.
  • pattern: Memeriksa apakah sebuah string cocok dengan regex.
  • alphabetical: Memeriksa apakah elemen array atau kunci objek dalam urutan abjad.
  • length: Memeriksa panjang string atau array.
  • schema: Memvalidasi nilai terhadap Skema JSON.

Kekuatan sebenarnya muncul ketika ini tidak cukup, dan Anda perlu menulis fungsi kustom Anda sendiri – di sinilah TypeScript bersinar.

Membuat Fungsi Spectral Kustom Pertama Anda di TypeScript

Mari kita buat fungsi kustom sederhana. Bayangkan kita ingin menegakkan aturan bahwa semua ringkasan operasi API harus dalam format Title Case dan tidak melebihi 70 karakter.

Langkah 1: Definisikan Fungsi Kustom di TypeScript

Buat file, misalnya src/customFunctions.ts:TypeScript

import type { IFunction, IFunctionResult } from '@stoplight/spectral-core';

interface TitleCaseLengthOptions {
  maxLength: number;
}

// Fungsi kustom untuk memeriksa apakah string dalam format title-cased dan dalam panjang maksimum
export const titleCaseAndLength: IFunction<string, TitleCaseLengthOptions> = (
  targetVal,
  options,
  context
): IFunctionResult[] | void => {
  const results: IFunctionResult[] = [];

  if (typeof targetVal !== 'string') {
    // Seharusnya tidak terjadi jika path 'given' menunjuk ke string, tetapi praktik yang baik
    return [{ message: `Nilai pada path '${context.path.join('.')}' harus berupa string.` }];
  }

  // Periksa Title Case (pemeriksaan sederhana: huruf pertama setiap kata adalah huruf besar)
  const words = targetVal.split(' ');
  const isTitleCase = words.every(word => word.length === 0 || (word[0] === word[0].toUpperCase() && (word.length === 1 || word.substring(1) === word.substring(1).toLowerCase())));

  if (!isTitleCase) {
    results.push({
      message: `Ringkasan "${targetVal}" pada path '${context.path.join('.')}' harus dalam Title Case.`,
      path: [...context.path], // Path ke elemen yang melanggar
    });
  }

  // Periksa panjang
  const maxLength = options?.maxLength || 70; // Default ke 70 jika tidak disediakan
  if (targetVal.length > maxLength) {
    results.push({
      message: `Ringkasan "${targetVal}" pada path '${context.path.join('.')}' melebihi panjang maksimum ${maxLength} karakter. Panjang saat ini: ${targetVal.length}.`,
      path: [...context.path],
    });
  }

  return results;
};

Penjelasan:

  • Kita mengimpor IFunction dan IFunctionResult dari @stoplight/spectral-core untuk keamanan tipe. IFunction<T = unknown, O = unknown> mengambil dua argumen generik: T untuk tipe targetVal (nilai yang sedang dilinting) dan O untuk tipe options yang diteruskan ke fungsi.
  • targetVal: Nilai sebenarnya dari dokumen API yang ditunjuk oleh JSONPath given. Kita telah mengetiknya sebagai string.
  • options: Objek yang berisi opsi yang diteruskan dari definisi aturan (misalnya, { "maxLength": 70 }). Kita membuat antarmuka TitleCaseLengthOptions untuk opsi ini.
  • context: Menyediakan informasi kontekstual tentang proses linting, termasuk:
  • path: Array segmen path yang mengarah ke targetVal.
  • document: Seluruh dokumen yang di-parse.
  • rule: Aturan saat ini yang sedang diproses.
  • Fungsi mengembalikan array objek IFunctionResult jika ada pelanggaran, atau void/undefined/array kosong jika tidak ada masalah. Setiap IFunctionResult harus memiliki message dan secara opsional path (jika berbeda dari context.path).
  • Logika kita memeriksa format title case sederhana dan panjang maksimum.

Langkah 2: Kompilasi Fungsi TypeScript

Jika Anda berencana menggunakan fungsi ini dengan ruleset .spectral.yaml atau .spectral.js yang menunjuk ke functionsDir, Anda perlu mengkompilasi TypeScript Anda ke JavaScript.

Tambahkan skrip build ke package.json Anda:JSON

{
  "scripts": {
    "build": "tsc"
  }
}

Jalankan npm run build atau yarn build. Ini akan mengkompilasi src/customFunctions.ts ke dist/customFunctions.js (berdasarkan tsconfig.json kita).

Langkah 3: Buat File Ruleset

Mari kita buat ruleset, misalnya, .spectral.js (atau .spectral.ts jika Anda lebih suka pengaturan yang sepenuhnya berbasis TypeScript, lihat bagian selanjutnya).

Jika menggunakan file ruleset JavaScript yang langsung mereferensikan fungsi yang dikompilasi:JavaScript

// .spectral.js
const { titleCaseAndLength } = require('./dist/customFunctions'); // Path ke fungsi JS yang dikompilasi

module.exports = {
  extends: [['@stoplight/spectral-rulesets/dist/rulesets/oas', 'recommended']],
  rules: {
    'operation-summary-title-case-length': {
      description: 'Ringkasan operasi harus dalam format title-cased dan tidak melebihi 70 karakter.',
      message: '{{error}}', // Pesan akan berasal dari fungsi kustom
      given: '$.paths[*][*].summary', // Menargetkan semua ringkasan operasi
      severity: 'warn',
      formats: ["oas3"], // Terapkan hanya untuk dokumen OpenAPI v3
      then: {
        function: titleCaseAndLength, // Langsung mereferensikan fungsi yang diimpor
        functionOptions: {
          maxLength: 70,
        },
      },
    },
    // Anda dapat menambahkan aturan lain di sini
  },
};

Atau, jika menggunakan .spectral.yaml dan functionsDir:

Pertama, pastikan direktori dist Anda berisi index.js yang mengekspor fungsi Anda, atau customFunctions.js Anda langsung mengekspornya. Misalnya, jika dist/customFunctions.js memiliki exports.titleCaseAndLength = ...;, Anda bisa melakukan:YAML

# .spectral.yaml
extends:
  - ["@stoplight/spectral-rulesets/dist/rulesets/oas", "recommended"]
functionsDir: "./dist" # Direktori yang berisi fungsi JS kustom yang dikompilasi
rules:
  operation-summary-title-case-length:
    description: "Ringkasan operasi harus dalam format title-cased dan tidak melebihi 70 karakter."
    message: "{{error}}"
    given: "$.paths[*][*].summary"
    severity: "warn"
    formats: ["oas3"]
    then:
      function: customFunctions#titleCaseAndLength # Mengasumsikan fungsi diekspor dari customFunctions.js
      functionOptions:
        maxLength: 70

Di sini, customFunctions#titleCaseAndLength memberitahu Spectral untuk mencari customFunctions.js (atau customFunctions/index.js) di functionsDir dan menggunakan fungsi titleCaseAndLength yang diekspor.

Langkah 4: Buat Dokumen OpenAPI Sampel

Mari kita buat file openapi.yaml sederhana untuk menguji aturan kita:YAML

# openapi.yaml
openapi: 3.0.0
info:
  title: Sample API
  version: 1.0.0
paths:
  /items:
    get:
      summary: retrieves all items from the store # Salah: bukan title case
      responses:
        '200':
          description: Daftar item.
    post:
      summary: Adds A New Item To The Ever Expanding Collection Of Items In The Store # Salah: terlalu panjang
      responses:
        '201':
          description: Item dibuat.
  /users:
    get:
      summary: Get User Details # Benar
      responses:
        '200':
          description: Detail pengguna.

Langkah 5: Jalankan Spectral

Sekarang, jalankan Spectral CLI terhadap dokumen OpenAPI Anda:Bash

spectral lint openapi.yaml --ruleset .spectral.js
# atau jika menggunakan ruleset YAML (seringkali terdeteksi otomatis jika bernama .spectral.yaml)
spectral lint openapi.yaml

Output yang Diharapkan:

Anda akan melihat peringatan yang mirip dengan ini:

openapi.yaml
 2:10  warning  operation-summary-title-case-length  Summary "retrieves all items from the store" at path 'paths./items.get.summary' must be in Title Case.   paths./items.get.summary
 6:10  warning  operation-summary-title-case-length  Summary "Adds A New Item To The Ever Expanding Collection Of Items In The Store" at path 'paths./items.post.summary' exceeds maximum length of 70 characters. Current length: 78.  paths./items.post.summary

✖ 2 problems (0 errors, 2 warnings, 0 infos, 0 hints)

Output ini mengkonfirmasi bahwa fungsi TypeScript kustom kita, yang dikompilasi ke JavaScript, dengan benar mengidentifikasi pelanggaran.

Penggunaan Spectral Secara Terprogram dengan Ruleset TypeScript

Untuk skenario yang lebih kompleks atau integrasi yang lebih erat ke dalam aplikasi, Anda mungkin ingin menggunakan Spectral secara terprogram dan mendefinisikan ruleset Anda sepenuhnya dalam TypeScript. Ini melewati kebutuhan akan file .spectral.yaml atau .spectral.js terpisah jika diinginkan, dan memungkinkan konstruksi ruleset yang dinamis.

Buat file TypeScript untuk linter Anda, misalnya src/linter.ts:TypeScript

import { Spectral, Document } from '@stoplight/spectral-core';
import { oas } from '@stoplight/spectral-rulesets';
import { truthy } from '@stoplight/spectral-functions'; // Contoh fungsi bawaan
import { titleCaseAndLength } from './customFunctions'; // Fungsi TS kustom Anda
import type { ISpectralDiagnostic } from '@stoplight/spectral-core';
import * as fs from 'fs/promises';
import * as path from 'path';

// Definisikan instance Spectral
const spectral = new Spectral();

// Muat ruleset dan fungsi bawaan
spectral.setRuleset({
  extends: [[oas, 'recommended']], // Memperluas aturan OpenAPI yang direkomendasikan
  rules: {
    'operation-summary-title-case-length': {
      description: 'Ringkasan operasi harus dalam format title-cased dan tidak melebihi 70 karakter.',
      message: '{{error}}',
      given: '$.paths[*][*].summary',
      severity: 'warn',
      formats: ["oas3"],
      then: {
        function: titleCaseAndLength, // Langsung menggunakan fungsi TypeScript
        functionOptions: {
          maxLength: 70,
        },
      },
    },
    'info-contact-defined': { // Contoh menggunakan fungsi bawaan
        description: 'Objek info harus memiliki objek kontak.',
        message: 'Informasi kontak API hilang.',
        given: '$.info',
        severity: 'warn',
        formats: ["oas3"],
        then: {
          field: 'contact',
          function: truthy, // Menggunakan fungsi bawaan
        },
      },
  },
});

// Fungsi untuk melakukan linting pada dokumen
export async function lintDocument(filePath: string): Promise<ISpectralDiagnostic[]> {
  try {
    const absolutePath = path.resolve(filePath);
    const fileContent = await fs.readFile(absolutePath, 'utf-8');
    
    // Buat objek Dokumen Spectral
    // Argumen kedua (URI) penting untuk menyelesaikan $refs relatif jika ada
    const document = new Document(fileContent, undefined, absolutePath); 
    
    const results = await spectral.run(document);
    return results;
  } catch (error) {
    console.error('Error linting dokumen:', error);
    return [];
  }
}

// Contoh penggunaan (misalnya, dalam skrip atau modul lain)
async function main() {
  const diagnostics = await lintDocument('openapi.yaml'); // Path ke spesifikasi API Anda
  if (diagnostics.length > 0) {
    console.log('Ditemukan Masalah Linting API:');
    diagnostics.forEach(issue => {
      console.log(
        `- [${issue.severity === 0 ? 'Error' : issue.severity === 1 ? 'Warning' : issue.severity === 2 ? 'Info' : 'Hint'}] ${issue.code} (${issue.message}) pada ${issue.path.join('.')}`
      );
    });
  } else {
    console.log('Tidak ada masalah linting API ditemukan. Kerja bagus!');
  }
}

// Jika Anda ingin menjalankan file ini langsung dengan ts-node
if (require.main === module) {
  main().catch(console.error);
}

Untuk menjalankan ini:Bash

npx ts-node src/linter.ts

Pendekatan ini memberikan fleksibilitas maksimum:

  • Tidak Ada Langkah Kompilasi untuk Definisi Ruleset: Ruleset itu sendiri didefinisikan dalam TypeScript. Fungsi kustom Anda masih perlu diimpor (baik sebagai TS jika ts-node digunakan, atau JS yang dikompilasi jika menjalankan Node murni).
  • Aturan Dinamis: Anda dapat secara terprogram membuat atau memodifikasi ruleset berdasarkan kondisi runtime.
  • Penggunaan Tipe Langsung: Anda langsung menggunakan fungsi TypeScript yang diimpor dalam definisi ruleset, meningkatkan keamanan tipe dan integrasi IDE.

Teknik Fungsi Kustom Tingkat Lanjut

Mari kita jelajahi beberapa aspek yang lebih canggih dalam membuat fungsi kustom.

Fungsi Kustom Asinkron:

Jika fungsi kustom Anda perlu melakukan operasi asinkron (misalnya, mengambil sumber daya eksternal untuk divalidasi, meskipun gunakan dengan hati-hati untuk kinerja), Anda dapat mendefinisikannya sebagai fungsi async.TypeScript

import type { IFunction, IFunctionResult } from '@stoplight/spectral-core';

export const checkExternalResource: IFunction<string, { url: string }> = 
  async (targetVal, options, context): Promise<IFunctionResult[] | void> => {
  try {
    const response = await fetch(`${options.url}/${targetVal}`);
    if (!response.ok) {
      return [{ message: `Sumber daya '${targetVal}' tidak ditemukan di ${options.url}. Status: ${response.status}` }];
    }
  } catch (error: any) {
    return [{ message: `Error mengambil sumber daya '${targetVal}': ${error.message}` }];
  }
};

Spectral akan dengan benar await fungsi asinkron Anda.

Mengakses Dokumen Lengkap dan Nilai yang Diselesaikan:

Objek context.document memberi Anda akses ke seluruh dokumen yang di-parse. Lebih kuat lagi, context.document.resolved menyediakan versi dokumen yang sepenuhnya di-dereference, yang penting saat berurusan dengan pointer $ref.TypeScript

import type { IFunction, IFunctionResult } from '@stoplight/spectral-core';
import {isPlainObject} from '@stoplight/json';

// Contoh: Pastikan skema yang direferensikan memiliki properti spesifik
export const referencedSchemaHasProperty: IFunction<{$ref: string}, { propertyName: string }> = (
  targetVal, // Ini akan menjadi objek seperti { $ref: '#/components/schemas/MySchema' }
  options,
  context
): IFunctionResult[] | void => {
  if (!targetVal.$ref) return;

  // Metode `context.document.resolveAnchor` dapat menemukan nilai yang diselesaikan untuk $ref
  const resolvedValue = context.document.resolveAnchor(targetVal.$ref);

  if (!resolvedValue || !isPlainObject(resolvedValue.value)) {
    return [{ message: `Tidak dapat menyelesaikan $ref: ${targetVal.$ref}` }];
  }
  
  const schemaProperties = resolvedValue.value.properties as Record<string, unknown> | undefined;

  if (!schemaProperties || schemaProperties[options.propertyName] === undefined) {
    return [{
      message: `Skema yang direferensikan oleh "${targetVal.$ref}" pada path '${context.path.join('.')}' harus memiliki properti "${options.propertyName}".`,
      path: [...context.path, '$ref'] // Arahkan ke $ref itu sendiri
    }];
  }
};

Fungsi ini akan digunakan dengan path given yang menargetkan objek yang berisi $ref, misalnya: $.paths[*].*.responses.*.content.*.schema.

Bekerja dengan format:

Pastikan aturan kustom Anda menentukan format yang berlaku (misalnya, oas2, oas3, asyncapi2). Ini mencegah aturan berjalan pada tipe dokumen yang tidak kompatibel. Anda dapat mengakses format yang terdeteksi dalam fungsi Anda melalui context.document.formats.

Mengorganisir dan Memperluas Ruleset

Seiring bertambahnya koleksi aturan kustom Anda, organisasi menjadi kunci.

  • Ruleset Modular: Pecah ruleset besar menjadi file yang lebih kecil dan terfokus. Anda dapat menggunakan properti extends untuk menggabungkannya. JavaScript
// .spectral.js (ruleset utama)
module.exports = {
  extends: ['./rulesets/common-rules.js', './rulesets/security-rules.js'],
  // ... aturan atau override lainnya
};
  • Ruleset Bersama: Publikasikan ruleset sebagai paket npm untuk membagikannya di berbagai proyek atau tim. Properti extends kemudian dapat mereferensikan paket-paket ini.
  • functionsDir untuk Kesederhanaan: Jika Anda memiliki banyak fungsi JS kustom (dikompilasi dari TS), functionsDir dalam ruleset .spectral.yaml bisa lebih sederhana daripada mendaftarkan semuanya atau menggunakan pengaturan yang sepenuhnya terprogram. Pastikan saja file JS yang dikompilasi mengekspor fungsi dengan benar.

Mengintegrasikan Spectral ke dalam Alur Kerja Anda

Kekuatan sebenarnya dari linting API terwujud ketika diotomatiskan.

  • Pipeline CI/CD: Integrasikan perintah spectral lint ke dalam GitHub Actions, GitLab CI, Jenkins, atau pipeline CI/CD lainnya. Gagalkan build jika terdeteksi kesalahan kritis. YAML
# Contoh langkah GitHub Action
- name: Lint API Specification
  run: spectral lint ./path/to/your/api-spec.yaml --ruleset ./.spectral.js --fail-severity=error
  • Git Hooks: Gunakan alat seperti Husky untuk menjalankan Spectral pada hook pre-commit atau pre-push, menangkap masalah bahkan sebelum mencapai repositori.
  • Integrasi IDE: Cari ekstensi Spectral untuk IDE Anda (misalnya, VS Code) untuk mendapatkan umpan balik waktu nyata saat Anda menulis spesifikasi API Anda.

Praktik Terbaik untuk Spectral dan Aturan Kustom TypeScript

  • Deskripsi dan Pesan yang Jelas: Tulis properti description dan message yang bermakna untuk aturan Anda. Pesan harus memandu pengguna tentang cara memperbaiki masalah.
  • Tingkat Keparahan yang Sesuai: Gunakan error untuk pelanggaran kritis, warn untuk saran penting, info untuk pemeriksaan informasional, dan hint untuk saran kecil.
  • Path given yang Tepat: Buat ekspresi JSONPath Anda sespesifik mungkin untuk hanya menargetkan node yang dimaksud. Ini meningkatkan kinerja dan mengurangi false positive.
  • Fungsi Idempoten: Fungsi kustom harus murni jika memungkinkan – dengan input yang sama, mereka harus menghasilkan output yang sama tanpa efek samping.
  • Uji Aturan Kustom Anda: Tulis unit test untuk fungsi TypeScript kustom Anda untuk memastikan mereka berperilaku seperti yang diharapkan dengan berbagai input.
  • Pertimbangan Kinerja: Perhatikan komputasi kompleks atau banyak panggilan fungsi schema dalam aturan yang sensitif terhadap kinerja, terutama untuk dokumen yang sangat besar. Uji kinerja ruleset Anda.
  • Perbarui Tipe TypeScript: Pastikan fungsi kustom Anda mengetik dengan benar targetVal dan options yang mereka harapkan. Ini penting untuk kemudahan pemeliharaan.

Kesimpulan: Tingkatkan Tata Kelola API Anda dengan Spectral dan TypeScript

Spectral menawarkan kerangka kerja yang tangguh untuk linting API, dan dengan memanfaatkan TypeScript untuk pengembangan aturan kustom, Anda membawa peningkatan keamanan tipe, pengalaman pengembang, dan kemudahan pemeliharaan ke strategi tata kelola API Anda. Baik Anda menegakkan praktik terbaik OpenAPI, konvensi penamaan khusus perusahaan, atau logika bisnis kompleks dalam desain API Anda, kombinasi mesin aturan Spectral yang fleksibel dan sistem pengetikan yang kuat dari TypeScript menyediakan solusi yang ampuh.

Dengan mengintegrasikan Spectral ke dalam siklus pengembangan Anda, mulai dari umpan balik IDE hingga pipeline CI/CD, Anda dapat memastikan API Anda konsisten, berkualitas tinggi, dan mematuhi standar yang Anda definisikan, yang pada akhirnya menghasilkan API yang lebih andal dan lebih mudah dikonsumsi. Mulailah dari yang kecil, ulangi ruleset Anda, dan berdayakan tim Anda dengan alat untuk membangun API yang lebih baik.