npm چیست؟ آموزش کامل npm، package.json و ساخت پکیج JavaScript برای API هوش مصنوعی
در این آموزش npm را از صفر تا سطح کاربردی یاد میگیرید؛ از نصب Package و مدیریت package.json تا package-lock، Scripts، SemVer و Workspaces. در پایان یک پکیج JavaScript قابلاستفاده مجدد برای API هوش مصنوعی درواره میسازیم.
اگر با JavaScript، Node.js، React، Next.js، Vue یا بسیاری از ابزارهای توسعه وب کار کرده باشید، احتمالاً دستورهایی مانند npm install، npm run dev و npm start را دیدهاید.
npm فقط ابزاری برای دانلود کتابخانه نیست. این ابزار در بخشهای مختلف چرخه توسعه JavaScript نقش دارد:
- ایجاد پروژه
- نصب و حذف Package
- مدیریت نسخه وابستگیها
- اجرای Scriptهای پروژه
- ساخت فایل Lock
- اجرای ابزارهای خط فرمان
- مدیریت Monorepo
- بستهبندی و انتشار Package
- نصب قابلتکرار در CI/CD
در این آموزش مفاهیم npm را از پایه یاد میگیریم و سپس یک پروژه واقعی میسازیم: یک Package قابلاستفاده مجدد JavaScript برای اتصال برنامههای Node.js به API هوش مصنوعی درواره.
npm چیست؟
npm مجموعهای از ابزارها و سرویسها برای مدیریت Packageهای JavaScript است.
طبق مستندات رسمی npm، npm از سه بخش اصلی تشکیل شده است:
- وبسایت npm
- ابزار خط فرمان npm CLI
- Registry یا مخزن Packageها
وبسایت npm برای جستوجو و بررسی Packageها استفاده میشود. Registry محل نگهداری Packageها و Metadata آنها است. npm CLI ابزاری است که از Terminal برای نصب، حذف، بهروزرسانی، اجرا و مدیریت Packageها استفاده میکنیم.
npm مخفف چیست؟
npm معمولاً با عبارت Node Package Manager شناخته میشود؛ زیرا در اکوسیستم Node.js برای مدیریت Packageها استفاده میشود.
بااینحال، در استفاده عملی مهمتر از نام کامل آن، سه نقش اصلی npm است:
Package Manager
Package Registry
Command-line Tool
Package چیست؟
Package مجموعهای از فایلها و Metadata است که یک قابلیت مشخص را ارائه میدهد.
یک Package میتواند شامل این موارد باشد:
- تابع JavaScript
- React Component
- ابزار Build
- کتابخانه اتصال به API
- ابزار خط فرمان
- Plugin
- Type Definition
- فایل CSS
- مجموعه تنظیمات
- Framework
نمونه Packageهای شناختهشده:
express
react
typescript
vite
dotenv
eslint
Package الزاماً نباید عمومی باشد. یک شرکت میتواند Packageهای داخلی و خصوصی برای اشتراک کد میان پروژههای خود داشته باشد.
Module چیست؟
در JavaScript، Module فایلی است که کد را Export میکند تا فایلهای دیگر بتوانند آن را Import کنند.
فایل math.js:
export function add(first, second) {
return first + second;
}
فایل app.js:
import { add } from "./math.js";
console.log(add(10, 5));
یک Package میتواند شامل یک یا چند Module باشد.
تفاوت مفهومی:
Module = واحد کد قابل Import
Package = مجموعه فایلها و Metadata قابل نصب
npm همراه Node.js نصب میشود؟
در بیشتر روشهای رسمی نصب Node.js، npm نیز همراه آن نصب میشود.
بررسی نسخه Node.js:
node --version
بررسی نسخه npm:
npm --version
اگر نسخه نمایش داده شود، npm نصب است.
برای نصب یا بهروزرسانی بهتر است روش مناسب سیستمعامل و مستندات نسخه فعلی Node.js و npm را بررسی کنید. در محیطهای توسعه حرفهای، استفاده از Version Manager میتواند مدیریت چند نسخه Node.js را سادهتر کند.
Registry در npm چیست؟
Registry یک پایگاه Package است. هنگام اجرای دستور زیر:
npm install express
npm اطلاعات Package و وابستگیهای آن را از Registry دریافت و در پروژه نصب میکند.
Registry عمومی پیشفرض:
https://registry.npmjs.org/
مشاهده Registry فعلی:
npm config get registry
شرکتها میتوانند Registry خصوصی نیز داشته باشند، اما برای بیشتر پروژههای عمومی همان Registry پیشفرض استفاده میشود.
ایجاد اولین پروژه npm
یک پوشه جدید بسازید:
mkdir npm-demo
cd npm-demo
سپس:
npm init
npm چند سؤال درباره نام، نسخه، توضیحات و Entry Point پروژه میپرسد.
برای ایجاد سریع با مقدارهای پیشفرض:
npm init -y
پس از اجرا، فایل package.json ایجاد میشود.
package.json چیست؟
package.json فایل اصلی Metadata و تنظیمات پروژه npm است.
نمونه:
{
"name": "npm-demo",
"version": "1.0.0",
"description": "A simple npm project",
"type": "module",
"main": "index.js",
"scripts": {
"start": "node index.js",
"test": "node --test"
},
"keywords": [
"javascript",
"nodejs"
],
"author": "",
"license": "MIT"
}
براساس مرجع package.json در مستندات npm، این فایل باید JSON معتبر باشد، نه JavaScript Object.
بنابراین موارد زیر در package.json مجاز نیستند:
- کامنت
- Comma اضافی
- تابع
- متغیر
- عبارت JavaScript
- کلید بدون Double Quote
این JSON نامعتبر است:
{
// comment is not allowed
"name": "my-project",
}
نسخه معتبر:
{
"name": "my-project"
}
مهمترین فیلدهای package.json
name
نام Package یا پروژه:
{
"name": "darvareh-client"
}
نام باید با قواعد npm سازگار باشد. برای Package عمومی باید یکتا باشد.
version
نسخه Package:
{
"version": "1.0.0"
}
نسخه معمولاً از Semantic Versioning پیروی میکند.
description
{
"description": "JavaScript client for an AI API"
}
private
اگر Package نباید تصادفی منتشر شود:
{
"private": true
}
این فیلد برای اپلیکیشنها و Packageهای داخلی مفید است.
type
برای استفاده از ECMAScript Modules:
{
"type": "module"
}
در این حالت میتوان نوشت:
import express from "express";
بدون تنظیم type: module، رفتار فایلهای .js ممکن است براساس CommonJS باشد:
const express = require("express");
main
Entry Point قدیمی و رایج Package:
{
"main": "./src/index.js"
}
برای Packageهای جدید میتوان از exports نیز استفاده کرد.
exports
مسیرهای عمومی Package را کنترل میکند:
{
"exports": {
".": "./src/index.js"
}
}
اگر Package چند ورودی داشته باشد:
{
"exports": {
".": "./src/index.js",
"./errors": "./src/errors.js"
}
}
files
مشخص میکند هنگام بستهبندی چه فایلهایی وارد Package شوند:
{
"files": [
"src",
"README.md"
]
}
scripts
Commandهای پروژه:
{
"scripts": {
"start": "node src/index.js",
"dev": "node --watch src/index.js",
"test": "node --test"
}
}
engines
نسخه Runtime مورد انتظار:
{
"engines": {
"node": ">=20"
}
}
این فیلد نیازمندی پروژه را اعلام میکند. سختگیرانهبودن اجرای آن میتواند به تنظیمات npm وابسته باشد.
license
مجوز Package:
{
"license": "MIT"
}
برای Package خصوصی سازمانی ممکن است از مقدار زیر استفاده شود:
{
"license": "UNLICENSED"
}
انتخاب License باید آگاهانه و متناسب با نحوه انتشار انجام شود.
dependencies چیست؟
Packageهایی که برنامه برای اجرا به آنها نیاز دارد در dependencies قرار میگیرند:
{
"dependencies": {
"express": "^5.0.0",
"dotenv": "^17.0.0"
}
}
نصب:
npm install express
npm بهصورت پیشفرض Package را در dependencies ثبت میکند.
devDependencies چیست؟
ابزارهایی که فقط برای توسعه، تست، Lint یا Build لازم هستند در devDependencies قرار میگیرند:
{
"devDependencies": {
"eslint": "^9.0.0",
"vitest": "^3.0.0"
}
}
نصب:
npm install --save-dev eslint
نسخه کوتاه:
npm install -D eslint
نمونه Dev Dependency:
- Linter
- Formatter
- Test Runner
- Bundler
- Type Checker
- ابزار Build
- ابزار Development Server
تفاوت dependencies و devDependencies
| موضوع | dependencies | devDependencies |
|---|---|---|
| نیاز در Runtime | بله | معمولاً خیر |
| ابزار تست | معمولاً خیر | بله |
| کتابخانه Backend | بله | خیر |
| Build Tool | بسته به معماری | معمولاً بله |
| ثبت در package.json | بله | بله |
برای یک Package کتابخانهای، Dependencyهای Runtime کاربران Package باید با دقت انتخاب شوند؛ زیرا به پروژه مصرفکننده اضافه خواهند شد.
peerDependencies چیست؟
peerDependencies اعلام میکند Package انتظار دارد پروژه مصرفکننده نسخهای سازگار از یک Package دیگر داشته باشد.
مثال یک Plugin برای React:
{
"peerDependencies": {
"react": ">=18"
}
}
این الگو زمانی مفید است که Package نباید نسخه جداگانه و مستقل از Dependency میزبان نصب کند.
موارد رایج:
- Plugin فریمورک
- React Component Library
- ESLint Plugin
- Adapter
- Extension یک کتابخانه
optionalDependencies چیست؟
Dependency اختیاری میتواند در صورت نصبنشدن، مانع کل نصب نشود:
{
"optionalDependencies": {
"some-optional-package": "^1.0.0"
}
}
برنامه باید نبودن این Dependency را مدیریت کند.
نصب Package با npm install
نصب تمام Dependencyهای ثبتشده:
npm install
نسخه کوتاه:
npm i
نصب Package:
npm install express
نصب نسخه مشخص:
npm install express@5.0.0
نصب Development Dependency:
npm install -D eslint
نصب Package محلی:
npm install ../my-local-package
نصب از فایل بستهبندیشده:
npm install ./my-package-1.0.0.tgz
جزئیات حالتهای نصب در مستندات npm install ارائه شده است.
node_modules چیست؟
npm Packageهای نصبشده را معمولاً داخل پوشه node_modules قرار میدهد:
project/
├── node_modules/
├── package.json
└── package-lock.json
این پوشه ممکن است تعداد زیادی فایل داشته باشد؛ زیرا Dependencyهای مستقیم شما نیز Dependencyهای دیگری دارند.
ساختار مفهومی:
پروژه شما
├── express
│ ├── dependency-a
│ └── dependency-b
└── another-package
└── dependency-c
آیا node_modules را وارد Git کنیم؟
معمولاً خیر.
فایل .gitignore:
node_modules/
.env
coverage/
dist/
دلایل:
- حجم زیاد
- قابلبازتولید بودن با npm
- تفاوتهای سیستمعامل
- تغییر دائمی فایلها
- وجود
package.jsonو Lock File
پس از Clone کردن پروژه:
npm install
یا در محیط CI:
npm ci
package-lock.json چیست؟
package-lock.json ساختار دقیق Dependencyهای نصبشده را ثبت میکند.
package.json ممکن است یک Range نسخه تعریف کند:
{
"dependencies": {
"express": "^5.0.0"
}
}
اما package-lock.json نسخه دقیق و Dependencyهای غیرمستقیم را ثبت میکند.
طبق مستندات package-lock.json، این فایل برای توصیف دقیق درخت Dependency تولید میشود تا نصبهای بعدی قابلتکرارتر باشند.
آیا package-lock.json را وارد Git کنیم؟
برای اپلیکیشنها معمولاً بله.
مزایا:
- نصب قابلپیشبینیتر
- هماهنگی محیط اعضای تیم
- نصب دقیقتر در CI
- مشاهده تغییر Dependencyها در Pull Request
- Debug سادهتر تفاوت نسخهها
حذف Lock File برای حل تصادفی مشکلات Dependency همیشه راهحل مناسبی نیست. ابتدا علت تعارض یا نصب ناموفق را بررسی کنید.
تفاوت npm install و npm ci
npm install
npm install
ویژگیها:
- برای توسعه روزمره مناسب است.
- میتواند Lock File را بهروزرسانی کند.
- Package جدید نصب میکند.
- تغییرات
package.jsonرا مدیریت میکند.
npm ci
npm ci
ویژگیهای عمومی:
- برای CI و نصب تمیز مناسب است.
- به Lock File نیاز دارد.
- نصب را براساس Dependencyهای Lockشده انجام میدهد.
- اگر
package.jsonو Lock File هماهنگ نباشند، خطا میدهد. - پوشه
node_modulesرا پیش از نصب تمیز بازسازی میکند.
قاعده عملی:
Development → npm install
CI/CD → npm ci
Semantic Versioning یا SemVer
نسخه Package معمولاً سه بخش دارد:
MAJOR.MINOR.PATCH
مثال:
2.4.7
معنای عمومی:
MAJOR: تغییر ناسازگارMINOR: قابلیت جدید سازگارPATCH: اصلاح سازگار
مثال:
1.0.0 → 1.0.1
اصلاح کوچک و سازگار.
1.0.0 → 1.1.0
قابلیت جدید سازگار.
1.0.0 → 2.0.0
تغییر ناسازگار با نسخه قبلی.
علامت ^ در نسخه npm
{
"express": "^5.0.0"
}
بهطور عمومی، ^ اجازه بهروزرسانی سازگار در محدوده Major را میدهد:
>=5.0.0 و <6.0.0
رفتار نسخههای قبل از 1.0.0 محدودتر است و باید با دقت بررسی شود.
علامت ~ در نسخه npm
{
"package-name": "~2.4.1"
}
معمولاً اجازه تغییر Patch را میدهد:
>=2.4.1 و <2.5.0
نسخه دقیق
{
"package-name": "2.4.1"
}
فقط همان نسخه در package.json درخواست شده است. Lock File همچنان Dependencyهای غیرمستقیم را ثبت میکند.
latest به چه معنا است؟
هنگام نصب:
npm install package-name@latest
latest یک Dist Tag است، نه الزاماً مفهومی مانند «جدیدترین نسخه قابلتصور». Maintainer مشخص میکند کدام نسخه با Tag برابر با latest ارائه شود.
Package میتواند Tagهای دیگری نیز داشته باشد:
latest
beta
next
canary
برای Production نباید بدون بررسی، نسخه آزمایشی را جایگزین نسخه پایدار کنید.
npm Scripts چیست؟
در package.json میتوان Commandهای پروژه را تعریف کرد:
{
"scripts": {
"dev": "node --watch src/server.js",
"start": "node src/server.js",
"test": "node --test",
"lint": "eslint ."
}
}
اجرا:
npm run dev
npm run lint
برای بعضی Scriptهای استاندارد میتوان run را حذف کرد:
npm start
npm test
طبق راهنمای رسمی npm Scripts، Scriptها از طریق Shell اجرا میشوند و Executableهای محلی پوشه node_modules/.bin در مسیر اجرای Script در دسترس قرار میگیرند.
به همین دلیل برای اجرای ESLint محلی لازم نیست مسیر کامل بنویسیم:
{
"scripts": {
"lint": "eslint ."
}
}
pre و post Script
npm بعضی Hookهای قبل و بعد از Script را پشتیبانی میکند:
{
"scripts": {
"pretest": "npm run lint",
"test": "node --test",
"posttest": "echo Tests completed"
}
}
با اجرای:
npm test
ترتیب مفهومی:
pretest
test
posttest
Scriptها میتوانند کد اجرا کنند؛ بنابراین Package و Scriptهای پروژه را آگاهانه بررسی کنید.
تفاوت npm و npx
npm
برای مدیریت Package و اجرای Scriptهای پروژه:
npm install
npm run build
npx
برای اجرای Binary یک Package:
npx eslint .
یا ایجاد پروژه:
npx create-vite
در نسخههای جدید npm، npx با سازوکار npm exec ارتباط نزدیکی دارد:
npm exec -- eslint .
برای ابزارهای تکرارشونده پروژه، نصب محلی و تعریف Script معمولاً قابلپیشبینیتر است:
npm install -D eslint
{
"scripts": {
"lint": "eslint ."
}
}
سپس:
npm run lint
نصب محلی و Global
نصب محلی
npm install package-name
Package داخل پروژه نصب میشود.
نصب Global
npm install --global package-name
نسخه کوتاه:
npm install -g package-name
نصب Global بیشتر برای ابزارهای CLI عمومی استفاده میشود. برای Build Tool و Linter پروژه، نصب محلی معمولاً بهتر است؛ زیرا نسخه ابزار همراه پروژه ثبت میشود.
حذف Package
npm uninstall express
نسخه کوتاه:
npm remove express
npm ورودی مربوط را از package.json و Lock File نیز بهروزرسانی میکند.
مشاهده Packageهای نصبشده
Dependencyهای مستقیم:
npm list --depth=0
Dependency خاص:
npm list express
علت نصب یک Package:
npm explain package-name
این دستور هنگام بررسی Dependency غیرمستقیم مفید است.
بررسی Packageهای قدیمی
npm outdated
این دستور نسخه فعلی، نسخه مورد انتظار و نسخه جدیدتر را نمایش میدهد.
بهروزرسانی در محدوده مجاز package.json:
npm update
برای ارتقای Major باید Release Note و تغییرات ناسازگار را بررسی کنید.
npm audit چیست؟
npm audit
این دستور Dependencyها را با اطلاعات آسیبپذیریهای شناختهشده بررسی میکند.
اصلاح خودکار سازگار در صورت امکان:
npm audit fix
از اجرای بدون بررسی گزینههایی که تغییرات ناسازگار اعمال میکنند اجتناب کنید. پس از هر بهروزرسانی:
- تستها را اجرا کنید.
- Build را بررسی کنید.
- رفتار برنامه را آزمایش کنید.
- تغییرات Lock File را مرور کنید.
گزارش Audit همیشه به معنای قابلاستفاده بودن مستقیم یک آسیبپذیری در معماری شما نیست، اما نباید بدون بررسی نادیده گرفته شود.
فایل .npmrc چیست؟
.npmrc تنظیمات npm را نگهداری میکند.
نمونه:
save-exact=true
یا تنظیم Registry:
registry=https://registry.npmjs.org/
فایل .npmrc میتواند در سطح پروژه یا کاربر وجود داشته باشد.
Token یا Credential واقعی را در .npmrc واردشده به Git قرار ندهید. در CI از Secretهای محیط اجرا استفاده کنید.
Environment Variable و فایل .env
npm بهتنهایی فایل .env را مانند کد JavaScript بارگذاری نمیکند. برای برنامه Node.js میتوان از روش Runtime یا Package مناسب استفاده کرد.
نمونه با dotenv:
npm install dotenv
import "dotenv/config";
const apiKey =
process.env.DARVAREH_API_KEY;
فایل .env:
DARVAREH_API_KEY=YOUR_DARVAREH_API_KEY
فایل .gitignore:
.env
node_modules/
API Key نباید داخل package.json قرار گیرد.
پروژه عملی: ساخت Package برای API درواره
در این پروژه Package کوچکی میسازیم که در برنامههای Node.js قابلاستفاده باشد.
قابلیتها:
- تنظیم API Key
- تنظیم Base URL
- انتخاب Model ID
- ارسال Chat Completion
- اعتبارسنجی پیامها
- مدیریت Timeout
- پشتیبانی از AbortSignal
- خطای ساختاریافته
- تست با Test Runner داخلی Node.js
- نصب محلی در یک پروژه نمونه
- بررسی محتویات Package پیش از انتشار
این Package برای استفاده سمت سرور طراحی میشود. نباید آن را همراه API Key در کد مرورگر استفاده کرد.
ساختار پروژه
darvareh-client/
├── src/
│ ├── client.js
│ ├── errors.js
│ └── index.js
├── test/
│ └── client.test.js
├── .gitignore
├── package.json
└── README.md
مرحله اول: ایجاد Package
mkdir darvareh-client
cd darvareh-client
npm init -y
مرحله دوم: تنظیم package.json
فایل package.json:
{
"name": "@your-scope/darvareh-client",
"version": "0.1.0",
"description": "A server-side JavaScript client for Darvareh AI API",
"private": true,
"type": "module",
"exports": {
".": "./src/index.js",
"./errors": "./src/errors.js"
},
"files": [
"src",
"README.md"
],
"scripts": {
"test": "node --test",
"test:watch": "node --test --watch",
"pack:check": "npm pack --dry-run"
},
"keywords": [
"artificial-intelligence",
"ai-api",
"javascript",
"nodejs",
"darvareh"
],
"engines": {
"node": ">=20"
},
"license": "UNLICENSED"
}
در این مثال:
private: trueاز انتشار تصادفی جلوگیری میکند.type: moduleاستفاده از ESM را فعال میکند.exportsمسیرهای عمومی را مشخص میکند.filesفایلهای قابلبستهبندی را محدود میکند.scriptsفرمانهای تست و بررسی Package را تعریف میکند.enginesنسخه مورد انتظار Node.js را اعلام میکند.
پیش از انتشار عمومی باید نام، Scope، License، README و سیاست نسخهبندی را مشخص کنید و private را فقط آگاهانه تغییر دهید.
مرحله سوم: ساخت Error اختصاصی
فایل src/errors.js:
export class DarvarehApiError extends Error {
constructor(
message,
{
status = null,
code = null,
details = null,
cause = undefined
} = {}
) {
super(message, {
cause
});
this.name = "DarvarehApiError";
this.status = status;
this.code = code;
this.details = details;
}
}
با Error اختصاصی، برنامه مصرفکننده میتواند خطاهای API را از سایر خطاها تشخیص دهد.
مرحله چهارم: ساخت Client
فایل src/client.js:
import {
DarvarehApiError
} from "./errors.js";
const DEFAULT_BASE_URL =
"https://api.darvareh.ir/v1";
function normalizeBaseUrl(value) {
return value.replace(/\/+$/, "");
}
function validateMessages(messages) {
if (
!Array.isArray(messages) ||
messages.length === 0
) {
throw new TypeError(
"messages must be a non-empty array"
);
}
const validRoles = new Set([
"system",
"user",
"assistant"
]);
for (const message of messages) {
const isValid =
message !== null &&
typeof message === "object" &&
validRoles.has(message.role) &&
typeof message.content === "string" &&
message.content.trim().length > 0;
if (!isValid) {
throw new TypeError(
"each message must contain a valid role and content"
);
}
}
}
async function parseJsonResponse(response) {
const rawBody = await response.text();
if (!rawBody) {
return null;
}
try {
return JSON.parse(rawBody);
} catch (error) {
throw new DarvarehApiError(
"API response is not valid JSON",
{
status: response.status,
cause: error
}
);
}
}
function getErrorMessage(data, status) {
const apiMessage =
data?.error?.message ??
data?.message ??
data?.error;
if (
typeof apiMessage === "string" &&
apiMessage.trim()
) {
return apiMessage;
}
return `API request failed with status ${status}`;
}
export class DarvarehClient {
#apiKey;
#baseUrl;
#defaultModel;
#timeoutMs;
#fetch;
constructor({
apiKey,
baseUrl = DEFAULT_BASE_URL,
defaultModel = null,
timeoutMs = 60000,
fetchImpl = globalThis.fetch
}) {
if (
typeof apiKey !== "string" ||
!apiKey.trim()
) {
throw new TypeError(
"apiKey is required"
);
}
if (typeof fetchImpl !== "function") {
throw new TypeError(
"A fetch implementation is required"
);
}
if (
!Number.isFinite(timeoutMs) ||
timeoutMs <= 0
) {
throw new TypeError(
"timeoutMs must be a positive number"
);
}
this.#apiKey = apiKey;
this.#baseUrl =
normalizeBaseUrl(baseUrl);
this.#defaultModel = defaultModel;
this.#timeoutMs = timeoutMs;
this.#fetch = fetchImpl;
}
async chat({
messages,
model = this.#defaultModel,
temperature = 0.4,
maxTokens = 1200,
signal
}) {
validateMessages(messages);
if (
typeof model !== "string" ||
!model.trim()
) {
throw new TypeError(
"model is required"
);
}
const timeoutController =
new AbortController();
const timeoutId = setTimeout(() => {
timeoutController.abort(
new Error("Request timeout")
);
}, this.#timeoutMs);
const combinedSignal =
signal
? AbortSignal.any([
signal,
timeoutController.signal
])
: timeoutController.signal;
try {
const response = await this.#fetch(
`${this.#baseUrl}/chat/completions`,
{
method: "POST",
headers: {
"Authorization":
`Bearer ${this.#apiKey}`,
"Content-Type":
"application/json"
},
body: JSON.stringify({
model,
messages,
temperature,
max_tokens: maxTokens
}),
signal: combinedSignal
}
);
const data =
await parseJsonResponse(response);
if (!response.ok) {
throw new DarvarehApiError(
getErrorMessage(
data,
response.status
),
{
status: response.status,
code:
data?.error?.code ??
data?.code ??
null,
details: data
}
);
}
const content =
data?.choices?.[0]?.message?.content;
if (
typeof content !== "string" ||
!content.trim()
) {
throw new DarvarehApiError(
"API response does not contain message content",
{
status: response.status,
details: data
}
);
}
return {
content,
usage: data?.usage ?? null,
model: data?.model ?? model,
raw: data
};
} catch (error) {
if (error instanceof DarvarehApiError) {
throw error;
}
if (
error.name === "AbortError" ||
combinedSignal.aborted
) {
throw new DarvarehApiError(
"The request was cancelled or timed out",
{
cause: error
}
);
}
throw new DarvarehApiError(
"Unable to connect to the API",
{
cause: error
}
);
} finally {
clearTimeout(timeoutId);
}
}
}
نکته درباره AbortSignal.any
در این نمونه از API جدیدتر AbortSignal.any استفاده شده است. به همین دلیل در engines نسخه جدیدی از Node.js اعلام کردیم.
اگر Runtime هدف شما از این قابلیت پشتیبانی نمیکند، میتوانید:
- حداقل نسخه Node.js را افزایش دهید.
- یک تابع ترکیب Signal پیادهسازی کنید.
- فقط Timeout داخلی را نگه دارید.
- سازگاری را پیش از انتشار آزمایش کنید.
فیلد engines بهتنهایی سازگاری را تضمین نمیکند؛ تست باید روی نسخههای اعلامشده اجرا شود.
مرحله پنجم: ساخت Entry Point
فایل src/index.js:
export {
DarvarehClient
} from "./client.js";
export {
DarvarehApiError
} from "./errors.js";
اکنون مصرفکننده میتواند بنویسد:
import {
DarvarehClient,
DarvarehApiError
} from "@your-scope/darvareh-client";
مرحله ششم: نوشتن تست
تست نباید درخواست واقعی ارسال کند یا هزینه API ایجاد کند. یک fetchImpl ساختگی تزریق میکنیم.
فایل test/client.test.js:
import test from "node:test";
import assert from "node:assert/strict";
import {
DarvarehApiError,
DarvarehClient
} from "../src/index.js";
test(
"returns assistant content",
async () => {
const calls = [];
const fakeFetch = async (
url,
options
) => {
calls.push({
url,
options
});
return new Response(
JSON.stringify({
model: "test-model",
choices: [
{
message: {
role: "assistant",
content: "پاسخ آزمایشی"
}
}
],
usage: {
prompt_tokens: 10,
completion_tokens: 5,
total_tokens: 15
}
}),
{
status: 200,
headers: {
"Content-Type":
"application/json"
}
}
);
};
const client = new DarvarehClient({
apiKey: "test-api-key",
defaultModel: "test-model",
fetchImpl: fakeFetch
});
const result = await client.chat({
messages: [
{
role: "user",
content: "سلام"
}
]
});
assert.equal(
result.content,
"پاسخ آزمایشی"
);
assert.equal(calls.length, 1);
assert.equal(
calls[0].url,
"https://api.darvareh.ir/v1/chat/completions"
);
const body = JSON.parse(
calls[0].options.body
);
assert.equal(
body.model,
"test-model"
);
}
);
test(
"throws for invalid messages",
async () => {
const client = new DarvarehClient({
apiKey: "test-api-key",
defaultModel: "test-model"
});
await assert.rejects(
() => client.chat({
messages: []
}),
TypeError
);
}
);
test(
"creates a structured API error",
async () => {
const fakeFetch = async () => {
return new Response(
JSON.stringify({
error: {
message:
"Request was not accepted",
code: "invalid_request"
}
}),
{
status: 400,
headers: {
"Content-Type":
"application/json"
}
}
);
};
const client = new DarvarehClient({
apiKey: "test-api-key",
defaultModel: "test-model",
fetchImpl: fakeFetch
});
await assert.rejects(
() => client.chat({
messages: [
{
role: "user",
content: "سلام"
}
]
}),
(error) => {
assert.ok(
error instanceof DarvarehApiError
);
assert.equal(
error.status,
400
);
assert.equal(
error.code,
"invalid_request"
);
return true;
}
);
}
);
اجرای تست:
npm test
حالت Watch:
npm run test:watch
مرحله هفتم: نوشتن README
فایل README.md:
# Darvareh JavaScript Client
یک Client سمت سرور برای اتصال برنامههای Node.js به API درواره.
## Requirements
- Node.js 20 یا جدیدتر
## Local installation
```bash
npm install ../darvareh-client
Usage
import {
DarvarehClient
} from "@your-scope/darvareh-client";
const client = new DarvarehClient({
apiKey: process.env.DARVAREH_API_KEY,
defaultModel: process.env.DARVAREH_MODEL_ID
});
const response = await client.chat({
messages: [
{
role: "user",
content: "JavaScript چیست؟"
}
]
});
console.log(response.content);
API Key را فقط در محیط امن سمت سرور نگهداری کنید.
وجود README برای Package اهمیت زیادی دارد. README باید حداقل شامل این موارد باشد:
- هدف Package
- روش نصب
- نمونه استفاده
- نسخه Runtime
- API عمومی
- مدیریت خطا
- محدودیتها
- License
## مرحله هشتم: بررسی محتویات Package
قبل از انتشار یا انتقال Package:
```bash
npm run pack:check
یا:
npm pack --dry-run
این دستور نشان میدهد چه فایلهایی وارد Package خواهند شد.
مواردی که نباید وارد Package عمومی شوند:
.env- API Key
- فایل Test غیرضروری
- Log
- Credential
- فایل حجیم توسعه
- تنظیمات خصوصی
- Dataset داخلی
- خروجی موقت
مرحله نهم: ساخت فایل tgz
npm pack
خروجی مشابه:
your-scope-darvareh-client-0.1.0.tgz
این فایل را میتوان در یک پروژه دیگر نصب کرد:
npm install ../darvareh-client/your-scope-darvareh-client-0.1.0.tgz
تا زمانی که private: true فعال است، Package را عمومی منتشر نمیکنیم؛ اما میتوانیم آن را محلی تست کنیم.
استفاده از Package در پروژه نمونه
پوشه جدید:
mkdir darvareh-client-demo
cd darvareh-client-demo
npm init -y
تنظیم ESM:
npm pkg set type=module
نصب dotenv:
npm install dotenv
نصب Package محلی:
npm install ../darvareh-client
فایل .env:
DARVAREH_API_KEY=YOUR_DARVAREH_API_KEY
DARVAREH_MODEL_ID=YOUR_MODEL_ID
فایل index.js:
import "dotenv/config";
import {
DarvarehApiError,
DarvarehClient
} from "@your-scope/darvareh-client";
const client = new DarvarehClient({
apiKey:
process.env.DARVAREH_API_KEY,
defaultModel:
process.env.DARVAREH_MODEL_ID,
timeoutMs: 60000
});
try {
const result = await client.chat({
messages: [
{
role: "system",
content:
"شما یک دستیار فارسی و دقیق هستید."
},
{
role: "user",
content:
"npm و npx چه تفاوتی دارند؟"
}
]
});
console.log(result.content);
if (result.usage) {
console.log(
"Usage:",
result.usage
);
}
} catch (error) {
if (error instanceof DarvarehApiError) {
console.error(
"API request failed:",
{
message: error.message,
status: error.status,
code: error.code
}
);
process.exitCode = 1;
} else {
throw error;
}
}
اجرا:
node index.js
برای دریافت API Key میتوانید در درواره ثبتنام کنید. برای انتخاب Model ID و مشاهده اطلاعات بهروز قیمت مدلها به صفحه مدلهای درواره مراجعه کنید.
چرا این Package را در مرورگر استفاده نمیکنیم؟
Package برای Node.js و محیط امن سمت سرور طراحی شده است.
این کد در React سمت مرورگر مناسب نیست:
const client = new DarvarehClient({
apiKey: "YOUR_DARVAREH_API_KEY"
});
اگر API Key وارد Bundle مرورگر شود، کاربر میتواند آن را مشاهده کند.
معماری مناسب:
React یا مرورگر
↓
Backend شما
↓
Package داخلی درواره
↓
API درواره
ایجاد Command با npm pkg
npm میتواند بعضی فیلدهای package.json را بدون ویرایش دستی تغییر دهد:
npm pkg set type=module
افزودن Script:
npm pkg set scripts.test="node --test"
خواندن نام Package:
npm pkg get name
این دستورات برای Automation و Scriptهای Setup مفید هستند.
npm Workspaces چیست؟
Workspaces امکان مدیریت چند Package مرتبط را در یک Repository فراهم میکند.
ساختار:
ai-platform/
├── apps/
│ ├── dashboard/
│ └── api/
├── packages/
│ ├── darvareh-client/
│ └── shared-config/
├── package.json
└── package-lock.json
فایل ریشه:
{
"name": "ai-platform",
"private": true,
"workspaces": [
"apps/*",
"packages/*"
]
}
هر Workspace میتواند package.json خود را داشته باشد.
مزایا:
- مدیریت چند پروژه در یک Repository
- اشتراک Package داخلی
- یک Lock File مرکزی
- اجرای Script در Workspace مشخص
- نصب Dependency برای Package خاص
- توسعه همزمان Frontend و Backend
اجرای Script در Workspace
npm run dev --workspace=apps/dashboard
اجرای Script در تمام Workspaces:
npm run test --workspaces
نصب Dependency در Workspace:
npm install express --workspace=apps/api
Workspaces برای پروژهای شامل Frontend، Backend، SDK و Package مشترک بسیار مفید هستند.
ساخت CLI با فیلد bin
یک Package میتواند Command خط فرمان ارائه دهد.
فایل src/cli.js:
#!/usr/bin/env node
console.log(
"Darvareh client is ready"
);
در package.json:
{
"bin": {
"darvareh-client":
"./src/cli.js"
}
}
پس از نصب Package، Command مربوط در دسترس قرار میگیرد. برای CLI واقعی باید ورودی، خروجی، کد Exit و مدیریت خطا بهدرستی طراحی شوند.
هیچ API Key را بهصورت پیشفرض در Argument خط فرمان قرار ندهید؛ زیرا ممکن است در History یا فهرست Processها دیده شود. استفاده از Environment Variable مناسبتر است.
انتشار Package در npm
پیش از انتشار عمومی باید این موارد آماده باشند:
- نام یکتا
- نسخه صحیح
- README کامل
- License
- فایلهای محدودشده با
files - تست موفق
- Build موفق
- عدم وجود Secret
- حساب npm
- سیاست انتشار
- دسترسی مناسب Package
- روش احراز هویت امن
ورود:
npm login
مشاهده کاربر:
npm whoami
بررسی محتویات:
npm pack --dry-run
انتشار:
npm publish
برای Scoped Package عمومی ممکن است تنظیم Access لازم باشد:
npm publish --access public
در پروژه این مقاله عمداً private: true گذاشتهایم تا انتشار تصادفی انجام نشود. تا زمانی که Package واقعاً برای انتشار آماده نشده، این فیلد را حذف نکنید.
افزایش نسخه Package
Patch:
npm version patch
Minor:
npm version minor
Major:
npm version major
این دستورها میتوانند نسخه را در package.json تغییر دهند و بسته به تنظیمات، عملیات مرتبط با Git را نیز انجام دهند. قبل از اجرا وضعیت Repository و رفتار نسخه npm خود را بررسی کنید.
قاعده عمومی انتشار:
Bug Fix سازگار → Patch
Feature سازگار → Minor
Breaking Change → Major
انتخاب Package مناسب
قبل از نصب یک Package این موارد را بررسی کنید:
- آیا واقعاً به Dependency نیاز دارید؟
- مستندات روشن دارد؟
- Repository و Issue Tracker مشخص است؟
- نسخههای جدید منظم منتشر میشوند؟
- API آن پایدار است؟
- License برای پروژه مناسب است؟
- تعداد Dependencyهای غیرمستقیم چقدر است؟
- Bundle Size برای Frontend مناسب است؟
- Type Definition دارد؟
- با Runtime هدف سازگار است؟
- جایگزین سادهتر داخلی وجود دارد؟
- Package منسوخ نشده است؟
تعداد Download بهتنهایی کیفیت یا تناسب Package با پروژه شما را تضمین نمیکند.
مدیریت Dependency در پروژه هوش مصنوعی
در پروژههای متصل به مدلهای هوش مصنوعی، Dependencyها معمولاً در چند دسته قرار میگیرند:
| دسته | نمونه کاربرد |
|---|---|
| HTTP Client | ارتباط با API |
| Validation | بررسی ورودی و خروجی |
| Web Framework | ساخت Backend |
| Environment | خواندن تنظیمات |
| Database | ذخیره مکالمه |
| Queue | پردازش Async |
| Logging | مشاهدهپذیری |
| Testing | تست Client و API |
برای هر Dependency جدید از خود بپرسید:
آیا این قابلیت با API داخلی Runtime قابلپیادهسازی است؟
آیا هزینه نگهداری Package از ارزش آن کمتر است؟
آیا نسخه و License آن مناسب است؟
آیا در Production واقعاً استفاده میشود؟
در Package نمونه، برای درخواست HTTP از fetch داخلی Node.js استفاده کردیم و Dependency Runtime جدیدی اضافه نکردیم.
خطاهای رایج npm
دستور npm شناخته نمیشود
خطا:
npm: command not found
یا در ویندوز:
'npm' is not recognized
بررسی کنید:
- Node.js نصب است؟
- Terminal پس از نصب دوباره باز شده؟
- PATH درست تنظیم شده؟
- Version Manager فعال است؟
- Command از Shell صحیح اجرا میشود؟
خطای EACCES
این خطا معمولاً به Permission هنگام نصب Global مربوط است. تغییر Permissionهای سیستمی بدون شناخت میتواند مشکل بیشتری ایجاد کند. استفاده از Version Manager یا تنظیم صحیح مسیر Global معمولاً روش مناسبتری است.
تعارض Dependency
ممکن است Packageها نسخههای ناسازگار Peer Dependency درخواست کنند.
راهکار:
- متن کامل خطا را بخوانید.
- نسخه Packageها را بررسی کنید.
- Release Note را ببینید.
- Dependency مستقیم و Peer را هماهنگ کنید.
- از Force کردن نصب بدون بررسی اجتناب کنید.
package.json نامعتبر
بررسی JSON:
- Double Quote
- نبود Comment
- نبود Comma اضافی
- بستهشدن
{}و[]
Script پیدا نمیشود
خطا:
Missing script: "dev"
فهرست Scriptها:
npm run
فایل package.json را بررسی کنید:
{
"scripts": {
"dev": "node --watch index.js"
}
}
Package نصب است اما Import نمیشود
بررسی کنید:
- Package در
dependenciesوجود دارد؟ - نام Import صحیح است؟
- ESM یا CommonJS درست تنظیم شده؟
- فیلد
exportsمسیر را اجازه میدهد؟ - نسخه Node.js سازگار است؟
- Package فقط Type است یا Runtime Code دارد؟
تغییر Package اعمال نمیشود
در Package محلی ممکن است نیاز باشد:
- Package دوباره نصب شود.
- نسخه افزایش یابد.
- Tarball جدید ساخته شود.
- Cache ابزار Build پاک شود.
- Dev Server Restart شود.
آیا پاککردن node_modules راهحل خوبی است؟
گاهی بازسازی نصب مشکل محیطی را حل میکند:
rm -rf node_modules
npm ci
در PowerShell:
Remove-Item node_modules -Recurse -Force
npm ci
اما پیش از حذف بررسی کنید:
- تغییر محلی داخل
node_modulesندارید؟ - Lock File معتبر است؟
package.jsonبا Lock File هماهنگ است؟- مشکل از نسخه Node.js نیست؟
- خطای Registry یا شبکه وجود ندارد؟
بهصورت عادی نباید فایلهای داخل node_modules را مستقیم ویرایش کنید.
بهترین روشهای استفاده از npm
package-lock.jsonرا برای اپلیکیشن وارد Git کنید.- در CI از
npm ciاستفاده کنید. node_modulesرا وارد Git نکنید.- Scriptهای اصلی را در
package.jsonثبت کنید. - برای ابزار پروژه از نصب محلی استفاده کنید.
- Dependency غیرضروری اضافه نکنید.
- Release Note نسخههای Major را بخوانید.
- Packageهای قدیمی را دورهای بررسی کنید.
- تست را پس از Update اجرا کنید.
.envرا وارد Git نکنید.- Token را در فایل عمومی
.npmrcنگذارید. - پیش از Publish از
npm pack --dry-runاستفاده کنید. - Package داخلی را با
private: trueمحافظت کنید. - Runtime موردنیاز را در
enginesاعلام کنید. - API عمومی Package را با
exportsمحدود کنید. - نسخه Package را براساس SemVer تغییر دهید.
- API Key در Package Hardcode نکنید.
چکلیست package.json
nameصحیح و معتبر است.versionاز SemVer پیروی میکند.privateبرای اپلیکیشن تنظیم شده است.typeبا ESM یا CommonJS هماهنگ است.scriptsقابلاجرا هستند.- Dependencyهای Runtime درست دستهبندی شدهاند.
- ابزارهای توسعه در
devDependenciesهستند. exportsفقط مسیرهای عمومی را باز میکند.filesمحتویات Package را محدود میکند.enginesRuntime موردنیاز را اعلام میکند.- License مشخص است.
- Secret در فایل وجود ندارد.
- JSON معتبر است.
نقشه راه یادگیری npm
مرحله اول: دستورات اصلی
npm init
npm install
npm uninstall
npm run
npm list
مرحله دوم: فایلهای پروژه
package.json
package-lock.json
node_modules
.npmrc
.gitignore
مرحله سوم: مدیریت نسخه
Semantic Versioning
Version Range
Dist Tag
npm outdated
npm update
مرحله چهارم: Workflow تیمی
npm ci
Scripts
Testing
Lint
Build
CI/CD
مرحله پنجم: ساخت Package
exports
files
engines
README
npm pack
npm publish
مرحله ششم: پروژه بزرگ
Workspaces
Monorepo
Package داخلی
Shared Config
Versioning
Release Process
سؤالهای متداول
npm چیست؟
npm ابزار مدیریت Packageهای JavaScript و Node.js است و شامل CLI، Registry و وبسایت جستوجوی Packageها میشود.
آیا npm همراه Node.js نصب میشود؟
در بیشتر روشهای نصب Node.js، npm نیز نصب میشود. با دستور npm --version میتوانید آن را بررسی کنید.
npm install چه کاری انجام میدهد؟
این دستور Dependencyها را نصب میکند، پوشه node_modules را میسازد و در صورت نیاز package.json و package-lock.json را بهروزرسانی میکند.
package.json چیست؟
فایل Metadata و تنظیمات پروژه npm است و اطلاعاتی مانند نام، نسخه، Scriptها و Dependencyها را نگهداری میکند.
package-lock.json چیست؟
این فایل نسخه دقیق Dependencyهای نصبشده و ساختار درخت وابستگی را ثبت میکند تا نصب قابلتکرارتر باشد.
node_modules چیست؟
پوشهای است که Packageهای نصبشده پروژه و Dependencyهای آنها را نگهداری میکند.
آیا node_modules را در GitHub قرار دهیم؟
معمولاً خیر. package.json و package-lock.json را ثبت کنید و node_modules را در .gitignore قرار دهید.
تفاوت npm install و npm ci چیست؟
npm install برای توسعه و تغییر Dependencyها مناسب است. npm ci برای نصب تمیز و قابلتکرار براساس Lock File در CI/CD استفاده میشود.
تفاوت npm و npx چیست؟
npm برای نصب و مدیریت Package و اجرای Scriptها است. npx یا npm exec برای اجرای Binary یک Package استفاده میشود.
تفاوت dependencies و devDependencies چیست؟
dependencies برای Packageهای موردنیاز Runtime است. devDependencies برای ابزارهای توسعه، تست، Lint و Build استفاده میشود.
peerDependencies چیست؟
این فیلد اعلام میکند Package مصرفکننده باید نسخه سازگاری از Dependency میزبان، مانند React، داشته باشد.
علامت ^ در package.json چیست؟
بهطور عمومی اجازه نصب نسخههای سازگار جدیدتر در محدوده Major را میدهد. رفتار نسخههای قبل از 1.0.0 محدودتر است.
آیا package-lock.json را حذف کنیم؟
حذف آن نباید اولین راهحل مشکلات نصب باشد. ابتدا تعارض Dependency، نسخه Node.js و هماهنگی package.json را بررسی کنید.
npm audit چیست؟
دستور بررسی Dependencyها در برابر اطلاعات آسیبپذیریهای شناختهشده است. نتیجه باید همراه با معماری واقعی پروژه بررسی شود.
npm run dev چیست؟
dev یک Script تعریفشده توسط پروژه است. npm بهصورت ذاتی نمیداند dev چه کاری انجام دهد؛ Command آن در package.json قرار دارد.
آیا میتوان Package خصوصی ساخت؟
بله. میتوان Package را فقط داخل Repository، Workspace، Registry خصوصی یا سازمان استفاده کرد. برای جلوگیری از انتشار تصادفی از private: true استفاده کنید.
چگونه یک Package npm بسازیم؟
پروژه را با npm init ایجاد کنید، Entry Point و exports را تعریف کنید، تست و README بنویسید و با npm pack --dry-run محتویات بسته را بررسی کنید.
آیا API Key را میتوان داخل Package قرار داد؟
خیر. Package باید API Key را در Runtime از برنامه مصرفکننده دریافت کند. کلید واقعی نباید Hardcode یا منتشر شود.
آیا Client ساختهشده در این مقاله برای React مناسب است؟
این Client برای محیط امن سمت سرور طراحی شده است. استفاده از آن همراه کلید API در مرورگر باعث افشای کلید میشود. React باید به Backend برنامه متصل شود.
جمعبندی
npm یکی از اجزای اصلی اکوسیستم JavaScript است. این ابزار فقط Package نصب نمیکند؛ بلکه ساختار پروژه، نسخه Dependencyها، Scriptها، Lock File، Workspaces و فرایند بستهبندی را مدیریت میکند.
مهمترین مفاهیمی که باید یاد بگیرید عبارتاند از:
- Package و Module
- npm CLI و Registry
package.jsonpackage-lock.jsonnode_modulesdependenciesdevDependencies- Semantic Versioning
- npm Scripts
- تفاوت npm و npx
- تفاوت
npm installوnpm ci - Workspaces
- ساخت و بستهبندی Package
در پروژه عملی این مقاله یک Client سمت سرور برای API درواره ساختیم. Package دارای Entry Point مشخص، Error اختصاصی، Timeout، اعتبارسنجی پیام، تست بدون درخواست واقعی و Script بررسی محتویات بسته بود.
این الگو را میتوان برای ساخت SDK داخلی، کتابخانه مشترک سازمانی، ابزار CLI یا Package قابلاستفاده در چند Backend توسعه داد.
برای دریافت API Key و شروع استفاده از مدلهای هوش مصنوعی میتوانید در درواره ثبتنام کنید. برای انتخاب Model ID، بررسی قابلیت مدلها و مشاهده اطلاعات بهروز قیمت نیز صفحه مدلهای درواره را ببینید.
منابع تکمیلی
- معرفی npm در مستندات رسمی
- مرجع package.json
- مرجع package-lock.json
- مستندات npm install
- راهنمای npm Scripts
- مستندات npm Workspaces
مقالات مرتبط
- هوش مصنوعی با Node.js؛ ساخت اپلیکیشن با Express و API درواره
- JSON چیست؟ آموزش کامل JSON در Python، JavaScript و API
- ساخت چتبات هوش مصنوعی با Next.js، React و API درواره
- ساخت اپلیکیشن دسکتاپ هوش مصنوعی با Electron و JavaScript
- ساخت افزونه VS Code با هوش مصنوعی، TypeScript و فایل VSIX
- ساخت افزونه Chrome هوش مصنوعی با JavaScript
- ساخت SDK و API Client هوش مصنوعی از OpenAPI
- هوش مصنوعی با NestJS؛ ساخت API با TypeScript و درواره
- HTTP چیست؟ آموزش Request، Response، Method و Status Code
- REST API چیست؟ راهنمای کامل طراحی RESTful API
برای مطالعه شرایط استفاده و محدودیتهای مسئولیت، صفحه «سلب مسئولیت» را مشاهده کنید.