npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@x-app/nuxt-api

v0.1.1

Published

Configurable API composable for Nuxt 3 and Nuxt 4

Downloads

392

Readme

@x-app/nuxt-api

وحدة Nuxt توفر composable موحدًا للتعامل مع API عبر $fetch. تدعم GET وPOST وPUT وPATCH وDELETE، ورؤوس الطلبات والمصادقة، ومعالجة النجاح والفشل، ومنع تكرار الطلبات وتنظيمها في طابور.

التثبيت والإعداد

ثبّت الحزمة:

npm install @x-app/nuxt-api

أضفها إلى modules في nuxt.config.ts واضبط رابط API في runtimeConfig.public.apiBaseUrl:

export default defineNuxtConfig({
  modules: ["@x-app/nuxt-api"],
  runtimeConfig: {
    public: {
      apiBaseUrl: process.env.NUXT_PUBLIC_API_BASE_URL,
    },
  },
});

مثلًا، عيّن متغير البيئة:

NUXT_PUBLIC_API_BASE_URL=https://api.example.com

إذا لم يُضبط apiBaseUrl، يرمي الطلب خطأً برسالة لم يتم ضبط رابط خدمة API.

يمكن تحديد عنوان افتراضي لمجموعة طلبات عند إنشاء useApi عبر baseUrl، أو تحديد عنوان مختلف لطلب بعينه عبر opts.baseUrl. يفيد ذلك عند الاتصال بخدمات مستقلة:

const api = useApi();
await api.post("/auth/login", { username, password }, {
  baseUrl: useRuntimeConfig().public.xAppAuthApiBaseUrl,
});

تكون الأولوية لـopts.baseUrl على العنوان الافتراضي في useApi({ baseUrl })، ثم يُستخدم runtimeConfig.public.apiBaseUrl عند عدم تحديد أي منهما. ويظل الاسم القديم AuthBaseUrl مقبولًا للتوافق.

يسجل الموديول useApi() للاستيراد التلقائي. يجب استدعاؤه داخل سياق Nuxt مثل <script setup> أو composable أو plugin. ويمكن استيراده يدويًا:

import { useApi } from "@x-app/nuxt-api/composables/useApi";

وضع الإعدادات الافتراضية مركزيًا

ضع عنوان الخدمة مرة واحدة في nuxt.config.ts كما في الإعداد أعلاه. ولتوحيد خيارات الطلب والـ hooks في التطبيق، أنشئ wrapper محليًا في مجلد composables في Nuxt 3 أو app/composables في Nuxt 4:

export function useAppApi() {
  return useApi({
    dedupe: true,
    queue: false,
    headers: {
      "X-Application": "web",
      "Accept-Language": "ar",
    },
    onFailure: ({ error, status, url, method }) => {
      console.error("API request failed", { method, url, status, error });
    },
  });
}

استخدم wrapper التطبيق بدل useApi() مباشرةً، فتُطبق هذه الإعدادات افتراضيًا على دواله:

const api = useAppApi();
const users = await api.get<User[]>("/users");

يمكن تجاوز dedupe أو queue في طلب بعينه عبر opts؛ مثلًا لتعطيل منع التكرار لهذا الاستدعاء فقط:

await useAppApi().get<User[]>("/users/latest", {}, { dedupe: false });

الرؤوس الممررة إلى useApi({ headers }) تُضاف افتراضيًا لكل طلب. إذا مررت الرؤوس نفسها في opts.headers لطلب محدد، تتقدم قيم الطلب الفردي، بينما تبقى بقية الرؤوس الافتراضية. ترويسة Authorization الصريحة من الرؤوس الافتراضية أو الفردية تتقدم على الرمز التلقائي من cookie.

بهذا الأسلوب تبقى قيمة عنوان الخدمة في runtimeConfig، بينما تحفظ الرؤوس والـ callbacks داخل composable التطبيق لأن hooks دوال JavaScript وليست قيم إعدادات قابلة للتسلسل.

أول طلب

كل دالة طلب تقبل النوع المتوقع للبيانات كـ generic، وتعيد Promise يحتوي بيانات الاستجابة:

<script setup lang="ts">
interface User {
  id: number;
  name: string;
  email: string;
}

const api = useApi();
const users = await api.get<User[]>("/users");
</script>

يُضاف apiBaseUrl تلقائيًا إلى بداية المسار. يمكن كتابة المسار بشرطة مائلة في أوله أو دونها:

await api.get<User[]>("users");
await api.get<User[]>("/users");

كلا المثالين يطلبان https://api.example.com/users إذا كان هذا هو apiBaseUrl.

دوال HTTP والمعاملات

GET

المعامل الثاني في get يتحول إلى query string. القيم null وundefined تُهمل، والقيمة المصفوفية تتحول إلى عدة قيم بالاسم نفسه:

const result = await api.get<User[]>("/users", {
  page: 2,
  active: true,
  role: ["admin", "editor"],
  ignored: null,
});
// GET /users?page=2&active=true&role=admin&role=editor

POST وPUT وPATCH وDELETE

المعامل الثاني هو جسم الطلب. يرسل useApi الجسم كـ JSON ويضيف Content-Type: application/json تلقائيًا:

const created = await api.post<User>("/users", {
  name: "Sara",
  email: "[email protected]",
});

const replaced = await api.put<User>(`/users/${created.id}`, {
  name: "Sara Ali",
  email: "[email protected]",
});

const updated = await api.patch<User>(`/users/${created.id}`, {
  name: "Sara Ahmed",
});

await api.delete<void>(`/users/${created.id}`);

إذا احتاج endpoint إلى بادئة إضافية، مرّر url_layer ضمن خيارات الطلب:

await api.get("/users", {}, { url_layer: "/api/v1/" });
// https://api.example.com/api/v1/users

تُزال الشرطات الزائدة من أطراف url_layer عند بناء الرابط.

خيارات الطلب

المعامل الأخير في كل دالة هو opts. يقبل خيارات $fetch المدعومة من Nuxt/ofetch، بالإضافة إلى خيارات useApi الخاصة:

const users = await api.get<User[]>("/users", { page: 1 }, {
  timeout: 5_000,
  headers: { "X-Client": "web" },
});

const response = await api.post<User>("/users", { name: "Sara" }, {
  retry: 1,
  timeout: 10_000,
});

من الخيارات الخاصة:

  • url_layer: بادئة تضاف إلى مسار الطلب.
  • baseUrl: عنوان الأساس لهذا الطلب، وله الأولوية على العنوان الافتراضي لـuseApi وعلى runtimeConfig.public.apiBaseUrl.
  • error_message: افتراضيًا true. عند true تُرمى رسالة مختارة من استجابة API أو errorFallback. عند false يُعاد رمي خطأ $fetch الأصلي.
  • errorFallback: رسالة بديلة عند تعذّر استخراج رسالة مفيدة من استجابة الخطأ. يحاول useApi قراءة message أو error أو أول عنصر من errors. الافتراضي فشل تنفيذ الطلب.
  • success_message: افتراضيًا false. عند true تُستخرج message أو error من بيانات الاستجابة وتُطبع عبر console.info إذا وُجدت.
  • service وendpoint: قيم وصفية تُمرر إلى onFailure لمساعدتك في تصنيف الخطأ.
  • dedupe وqueue: تفعيل منع التكرار أو وضع الطلب في طابور؛ تفاصيلهما في القسم التالي.

المصادقة والرؤوس

يقرأ useApi الرمز من cookie باسم access_token. عند وجود قيمة، يضيف:

Authorization: Bearer <token>

تُرسل هذه الترويسة تلقائيًا في الطلبات. إذا مررت Authorization بنفسك، تتقدم القيمة اليدوية على الرمز الموجود في cookie. أسماء الرؤوس غير حساسة لحالة الأحرف.

await api.get<User>("/profile", {}, {
  headers: {
    "X-Client": "web",
    Authorization: "Bearer custom-token",
  },
});

يمكن استخدام headers عادية أو Headers أو أي صيغة يقبلها $fetch. وتبقى الرؤوس الافتراضية Accept: application/json ونوع محتوى JSON للطلبات غير GET ما لم تستبدلها بترويسة مخصصة.

يمكن تعيين headers مشتركة لكل الدوال عند إنشاء useApi، كما في مثال useAppApi أعلاه. headers الطلب الفردي تدمج فوقها؛ فإذا أعاد الطلب ترويسة بالاسم نفسه، تُستخدم قيمة الطلب الفردي.

Hooks للنجاح والفشل

يمكن تمرير onSuccess وonFailure عند إنشاء useApi. يعمل كل منهما مع جميع دوال HTTP التابعة لذلك الاستدعاء:

const api = useApi({
  onSuccess: ({ data, status, url, method }) => {
    console.info(`${method} ${url} completed with ${status}`, data);
  },
  onFailure: ({ error, status, data, url, method, service, endpoint }) => {
    console.error(`${service}/${endpoint}`, method, url, status, data, error);
  },
});

onSuccess يستقبل كائنًا يحتوي data وurl وmethod وstatus عند توفره. يُستدعى بعد نجاح $fetch ورسالة success_message. إذا رمى خطأً، يصل ذلك الخطأ إلى مستدعي الطلب، ولا يُستدعى onFailure بسببه.

onFailure يستقبل error الأصلي، وurl، وmethod، وstatus وبيانات الاستجابة عند توفرهما، إضافةً إلى service وendpoint. يُستدعى عند أخطاء HTTP أو الشبكة. إذا رمى hook نفسه خطأً، يُتجاهل ذلك الخطأ حتى لا يحجب خطأ الطلب الأصلي.

يمكن أن يكون كلا الـ hookين غير متزامن:

const api = useApi({
  onFailure: async ({ status }) => {
    if (status === 401) {
      await navigateTo("/login");
    }
  },
});

لا يوجد logger خاص بالتطبيق داخل الحزمة؛ استخدم onFailure لتسجيل الخطأ في logger مشروعك أو لتنفيذ معالجة عامة.

منع تكرار الطلبات وتنظيمها في طابور

الخياران dedupe وqueue معطلان افتراضيًا. يمكن تفعيلهما افتراضيًا عند إنشاء useApi، ثم تجاوزهما لكل طلب عبر opts:

const api = useApi({ dedupe: true, queue: true });

// يعيد استخدام نتيجة الطلب المطابق إذا كان ما يزال جاريًا.
const usersRequest = api.get<User[]>("/users", { page: 1 });

// يتجاوز الإعداد العام لهذا الطلب.
const health = await api.get<{ ok: boolean }>("/health", {}, {
  dedupe: false,
  queue: false,
});

dedupe

إذا كان dedupe: true، لا يرسل useApi طلب شبكة آخر عند وجود طلب مطابق ما يزال جاريًا أو منتظرًا؛ بل يشارك مستدعوه النتيجة أو الخطأ نفسه. تُقارن طريقة HTTP والرابط النهائي والجسم والرؤوس وخيارات $fetch. لا تدخل callbacks ولا signal في مفتاح المطابقة. بعد اكتمال الطلب، تُزال حالته؛ فالطلبات اللاحقة لا تشارك نتيجة قديمة.

عند مشاركة طلب مكرر، تُنفذ hooks ورسالة success_message مرة واحدة وفق خيارات الاستدعاء الذي بدأ طلب الشبكة؛ أما الاستدعاءات التي انضمت إليه فتستلم النتيجة أو الخطأ نفسه.

queue

إذا كان queue: true، تُنفذ طلبات endpoint نفسه بالتتابع حسب ترتيب وصولها. يجمع الطابور الطلبات إلى المسار نفسه بعد إضافة url_layer، ويتجاهل query string عند تحديد المسار. يمكن أن تختلف الطرق والمعاملات والأجسام، لكنها تنتظر حتى يكتمل الطلب السابق. يبدأ الطلب التالي سواء نجح السابق أو فشل.

إذا فُعّل الخياران معًا، تُشارك الطلبات المطابقة النتيجة أولًا، ثم تنتظر الطلبات المختلفة للمسار نفسه دورها في الطابور. حالة الطوابير ومنع التكرار مشتركة بين استدعاءات useApi داخل تطبيق Nuxt ومعزولة بين سياقات SSR.

معالجة الأخطاء في الاستدعاء

يمكن أيضًا معالجة الخطأ عند موضع الاستدعاء باستخدام try/catch:

try {
  const user = await api.get<User>("/users/42");
  console.log(user.name);
} catch (error) {
  // افتراضيًا يكون error Error برسالة API أو errorFallback.
  console.error(error);
}

استخدم error_message: false إذا كنت تحتاج إلى كائن خطأ $fetch الأصلي بدل رسالة Error مبسطة:

try {
  await api.get("/users/42", {}, { error_message: false });
} catch (error) {
  // افحص تفاصيل الخطأ الأصلي هنا.
}

البناء والنشر

npm install
npm run build
npm pack --dry-run
npm publish

ينفذ prepack البناء تلقائيًا قبل إنشاء حزمة npm.