API dokumentatsiya — Swagger bilan

~20 daq · Express · API Arxitekturasi

Muammodan boshlaylik

Backend tayyor. Frontend dasturchi sizga yozadi:

"Qaysi endpointlar bor? Qanday parametrlar yuborish kerak? Javob qanday ko'rinadi? Token qayerga qo'yiladi?"

Siz har safar tushuntirasiz. Yoki Notion da yozasiz — kod o'zgarganda Notion eskirib qoladi. Yoki README yozasiz — uni hech kim o'qimaydi.

Swagger — bu muammoning yechimi. Kod yonida yashaydi, har doim yangi, o'zi interaktiv interfeys yaratadi. Frontend dasturchi brauzerdan endpointni sinab ko'ra oladi.


Swagger nima?

Swagger (OpenAPI) — API ni tasvirlash uchun standart format. swagger-ui-express esa bu tavsifni chiroyli interaktiv sahifaga aylantiradi.

npm install swagger-ui-express swagger-jsdoc

Arxitektura

project/
├── config/
│   └── swagger.js       ← Swagger sozlamalari
├── routes/
│   └── products.js      ← endpointlar (JSDoc izohlar shu yerda)
└── app.js

1-qadam: Swagger sozlamasi

// config/swagger.js
const swaggerJsdoc = require('swagger-jsdoc');

const options = {
  definition: {
    openapi: '3.0.0',
    info: {
      title: 'Mening API',
      version: '1.0.0',
      description: 'Express API dokumentatsiyasi',
    },
    servers: [
      { url: 'http://localhost:3000/api/v1' },
    ],
    components: {
      securitySchemes: {
        bearerAuth: {                         // ← JWT token uchun
          type: 'http',
          scheme: 'bearer',
          bearerFormat: 'JWT',
        },
      },
    },
  },
  apis: ['./routes/*.js'],                    // ← izohlar qayerda
};

module.exports = swaggerJsdoc(options);

2-qadam: app.js ga ulash

// app.js
const swaggerUi   = require('swagger-ui-express');
const swaggerSpec = require('./config/swagger');

app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
// → http://localhost:3000/api-docs — dokumentatsiya sahifasi

3-qadam: Route izohlar

Izohlar route faylida, endpoint ustida yoziladi. /** ... */ formatida:

// routes/products.js

/**
 * @swagger
 * /products:
 *   get:
 *     summary: Barcha mahsulotlar
 *     description: Filtr va sort bilan mahsulotlar ro'yxati
 *     tags: [Mahsulotlar]
 *     parameters:
 *       - in: query
 *         name: category
 *         schema:
 *           type: string
 *         description: Kategoriya nomi
 *       - in: query
 *         name: minPrice
 *         schema:
 *           type: number
 *         description: Minimal narx
 *       - in: query
 *         name: page
 *         schema:
 *           type: integer
 *           default: 1
 *         description: Sahifa raqami
 *     responses:
 *       200:
 *         description: Muvaffaqiyatli
 *         content:
 *           application/json:
 *             example:
 *               success: true
 *               data:
 *                 - id: "64f1a2b3"
 *                   name: "iPhone 15"
 *                   price: 1200000
 *               meta:
 *                 page: 1
 *                 total: 50
 */
router.get('/', async (req, res, next) => {
  // ...
});
/**
 * @swagger
 * /products/{id}:
 *   get:
 *     summary: Bitta mahsulot
 *     tags: [Mahsulotlar]
 *     parameters:
 *       - in: path
 *         name: id
 *         required: true
 *         schema:
 *           type: string
 *         description: Mahsulot ID si
 *     responses:
 *       200:
 *         description: Mahsulot topildi
 *         content:
 *           application/json:
 *             example:
 *               success: true
 *               data:
 *                 id: "64f1a2b3"
 *                 name: "iPhone 15"
 *                 price: 1200000
 *       404:
 *         description: Topilmadi
 *         content:
 *           application/json:
 *             example:
 *               success: false
 *               error:
 *                 code: NOT_FOUND
 *                 message: "Mahsulot topilmadi"
 */
router.get('/:id', async (req, res, next) => {
  // ...
});
/**
 * @swagger
 * /products:
 *   post:
 *     summary: Yangi mahsulot yaratish
 *     tags: [Mahsulotlar]
 *     security:
 *       - bearerAuth: []          ← token kerak
 *     requestBody:
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             type: object
 *             required: [name, price]
 *             properties:
 *               name:
 *                 type: string
 *                 example: "iPhone 15"
 *               price:
 *                 type: number
 *                 example: 1200000
 *               category:
 *                 type: string
 *                 example: "elektronika"
 *     responses:
 *       201:
 *         description: Yaratildi
 *         content:
 *           application/json:
 *             example:
 *               success: true
 *               data:
 *                 id: "64f1a2b3"
 *                 name: "iPhone 15"
 *                 price: 1200000
 *       401:
 *         description: Token kerak
 *       422:
 *         description: Validatsiya xatosi
 */
router.post('/', auth, async (req, res, next) => {
  // ...
});

Schema qayta ishlatish (takrorlanishni kamaytirish)

Bir xil obyekt tuzilishi ko'p joyda ishlatilsa — alohida schema yarating:

// routes/products.js yuqorisida

/**
 * @swagger
 * components:
 *   schemas:
 *     Product:
 *       type: object
 *       properties:
 *         id:
 *           type: string
 *           example: "64f1a2b3"
 *         name:
 *           type: string
 *           example: "iPhone 15"
 *         price:
 *           type: number
 *           example: 1200000
 *         category:
 *           type: string
 *           example: "elektronika"
 */

Keyin response da qayta ishlating:

/**
 * @swagger
 * /products/{id}:
 *   get:
 *     responses:
 *       200:
 *         content:
 *           application/json:
 *             schema:
 *               properties:
 *                 data:
 *                   $ref: '#/components/schemas/Product'  ← qayta ishlatish
 */

Natija

http://localhost:3000/api-docs da shunday ko'rinadi:

┌─────────────────────────────────────┐
│  Mening API  v1.0.0                 │
├─────────────────────────────────────┤
│  ▼ Mahsulotlar                      │
│  ┌─────────────────────────────┐    │
│  │ GET  /products    [Try it]  │    │
│  │ POST /products    [Try it]  │    │
│  │ GET  /products/{id} [Try]   │    │
│  └─────────────────────────────┘    │
└─────────────────────────────────────┘

"Try it out" tugmasini bosib, to'g'ridan-to'g'RI brauzerdan so'rov yuborish mumkin. Token ham kiritiladi — hamma narsa interaktiv.


Tez-tez uchraydigan xatolar

1. YAML chekinishiga e'tibor bermaslik

// ❌ — YAML da chekinish (indentation) muhim
/**
 * @swagger
 * /products:
 *  get:          ← 1 bo'shliq — noto'g'ri
 *   summary: ...
 */

// ✅ — 2 yoki 4 bo'shliq, izchil
/**
 * @swagger
 * /products:
 *   get:         ← 2 bo'shliq — to'g'ri
 *     summary: ...
 */

Sababi: Swagger YAML formatini ishlatadi. Chekinish noto'g'ri bo'lsa — parse xatosi, dokumentatsiya ko'rinmaydi.

2. apis yo'li noto'g'ri

// ❌ — fayl topilmasa izohlar o'qilmaydi
apis: ['./route/*.js']  // ← "route" emas, "routes"
// ✅
apis: ['./routes/*.js', './routes/**/*.js'], // ← ichki papkalar ham

3. Dokumentatsiyani production da ochiq qoldirish

// ❌ — production da hamma ko'ra oladi
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
// ✅ — faqat development da
if (process.env.NODE_ENV !== 'production') {
  app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
}

Sababi: API tuzilishi, endpoint nomlari, parametrlar — bularni hamma ko'rishi xavfsizlik xatari.


Xulosa

  • Swagger — API ni interaktiv dokumentatsiya sifatida ko'rsatadi
  • swagger-jsdoc izohlarni o'qiydi, swagger-ui-express sahifa ko'rsatadi
  • Izohlar route faylida, endpoint ustida /** @swagger */ formatida
  • $ref — schemani qayta ishlatish, takrorlanishni kamaytiradi
  • Production da /api-docs ni yoping

Keyingi dars: 41-dars: Webhook — qabul qilish va yuborish