0Pricing
R Academy · درس

استهلاك REST APIs في R

صادق باستخدام مفاتيح API، وقسّم النتائج إلى صفحات، وخزّن استجابات API

استهلاك REST APIs في R درس مجاني في R Academy على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في R Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة R Academy 4 دروس في المجموع.

مفاهيم REST API

تستخدم REST APIs أساليب HTTP مثل GET وPOST وPUT وDELETE على عناوين URL للموارد. وتكون الاستجابات عادةً بتنسيق JSON. وقد تتطلب واجهات API المصادقة، وتتعامل مع تقسيم الصفحات، وتفرض حدودًا لمعدل الطلبات. ويتولى عميل API جيد في R معالجة هذه الأمور الثلاثة.

library(httr2)

# REST API anatomy:
# Base URL:  https://api.example.com/v1
# Resources: /users, /products, /orders
# Methods:
#   GET    /users       -> list users
#   POST   /users       -> create user
#   GET    /users/42    -> get user 42
#   PUT    /users/42    -> update user 42
#   DELETE /users/42    -> delete user 42

# Query parameters for filtering/pagination:
# GET /users?page=2&size=20&sort=name

cat('REST = stateless + resource-based + HTTP methods')

المصادقة باستخدام رمز Bearer

تستخدم معظم واجهات API الحديثة رموز Bearer، المعتمدة على OAuth 2.0. خزّن الرمز في متغير بيئة باستخدام Sys.setenv() أو في ملف .Renviron. لا تضع الرموز بشكل ثابت في البرامج النصية أبدًا.

library(httr2)

# Store token securely in .Renviron:
# GITHUB_PAT=ghp_your_token_here

# Access at runtime:
token <- Sys.getenv('GITHUB_PAT')
if (nchar(token) == 0) token <- 'demo_token'

# Use in requests:
# resp <- request('https://api.github.com/user') |>
#   req_auth_bearer_token(token) |>
#   req_headers('Accept' = 'application/vnd.github.v3+json') |>
#   req_perform() |>
#   resp_check_status()

# result <- resp_body_json(resp)
# result$login  # your GitHub username
cat('Token from env:', if(nchar(token)>0) 'found' else 'missing')

إنشاء عميل API قابل لإعادة الاستخدام

ضمّن عنوان URL الأساسي والمصادقة ومعالجة الأخطاء في دالة مُنشئة. ويصبح كل endpoint من واجهة API طريقةً تستدعي هذه الدالة الأساسية؛ وهذا هو النمط القياسي لحزم API في R.

library(httr2)

# API client constructor
new_api_client <- function(base_url, token) {
  list(
    base_req = request(base_url) |>
      req_auth_bearer_token(token) |>
      req_headers('Accept' = 'application/json') |>
      req_retry(max_tries = 3)
  )
}

# Method: GET /users
get_users <- function(client, page = 1, size = 20) {
  resp <- client$base_req |>
    req_url_path_append('users') |>
    req_url_query(page = page, size = size) |>
    req_perform() |>
    resp_check_status()
  resp_body_json(resp, simplifyVector = TRUE)
}

# client <- new_api_client('https://api.example.com', token)
# users <- get_users(client, page = 1)
cat('Reusable client pattern: base request + methods')

تقسيم الصفحات باستخدام resp_link_url()

تستخدم العديد من واجهات API رؤوس Link لتقسيم الصفحات وفقًا لـ RFC 5988؛ إذ تتضمن الاستجابة رأسًا مثل Link: <url>; rel="next". وتستخرج resp_link_url(resp, 'next') عنوان URL للصفحة التالية تلقائيًا.

library(httr2)

# Generic paginator using Link headers
fetch_all_pages <- function(initial_url, token, max_pages = 50) {
  all_results <- list()
  next_url <- initial_url
  page <- 1
  while (!is.null(next_url) && page <= max_pages) {
    resp <- request(next_url) |>
      req_auth_bearer_token(token) |>
      req_perform() |>
      resp_check_status()
    all_results[[page]] <- resp_body_json(resp, simplifyVector = TRUE)
    # Follow Link: <url>; rel='next' header
    next_url <- tryCatch(
      resp_link_url(resp, 'next'),
      error = function(e) NULL
    )
    page <- page + 1
  }
  do.call(rbind, all_results)
}
cat('resp_link_url() follows RFC 5988 pagination')

تقسيم الصفحات القائم على المؤشر

تستخدم بعض واجهات API، مثل Twitter وSlack، مؤشرات بدلًا من أرقام الصفحات. وتتضمن الاستجابة حقلًا باسم next_cursor أو next_page_token. مرّر هذا الحقل كمعامل استعلام في الطلب التالي.

library(httr2)

# Cursor-based pagination pattern
fetch_cursor_pages <- function(base_url, token, max_pages = 100) {
  all_data <- list()
  cursor <- NULL
  page <- 1
  repeat {
    req <- request(base_url) |>
      req_auth_bearer_token(token)
    if (!is.null(cursor))
      req <- req |> req_url_query(cursor = cursor)
    resp <- req |> req_perform() |> resp_check_status()
    body <- resp_body_json(resp)
    all_data[[page]] <- body$results
    cursor <- body$next_cursor  # NULL if last page
    if (is.null(cursor) || page >= max_pages) break
    page <- page + 1
  }
  do.call(c, all_data)
}
cat('Cursor pagination: safer for large/changing datasets')

معالجة الأخطاء بطريقة سليمة

يفصل عميل API المخصص لبيئة الإنتاج بين أخطاء HTTP وإخفاقات الشبكة عند التقاطها. استخدم tryCatch() حول req_perform() وافحص شرط الخطأ httr2_http_* لمعالجة الأخطاء حسب رمز الحالة.

library(httr2)

safe_api_call <- function(req) {
  tryCatch(
    req |> req_perform() |> resp_check_status(),
    httr2_http_401 = function(e) {
      stop('Authentication failed. Check your token.')
    },
    httr2_http_403 = function(e) {
      stop('Forbidden. Insufficient permissions.')
    },
    httr2_http_404 = function(e) {
      message('Resource not found, returning NULL')
      return(NULL)
    },
    httr2_http_429 = function(e) {
      stop('Rate limit exceeded. Try again later.')
    },
    error = function(e) {
      stop(paste('Request failed:', conditionMessage(e)))
    }
  )
}
cat('Match on specific httr2_http_NNN conditions')

تخزين استجابات API مؤقتًا

خزّن استجابات API مؤقتًا لتجنب الطلبات غير الضرورية أثناء التطوير. تعمل req_cache() في httr2 على تخزين الاستجابات مؤقتًا على القرص مع احترام رؤوس Cache-Control. ويمكن استخدام التخزين المؤقت اليدوي مع أي واجهة API.

library(httr2)

# httr2 built-in disk cache
# resp <- request('https://api.example.com/static-data') |>
#   req_cache(tempdir(), max_age = 3600) |>  # 1 hour TTL
#   req_perform()

# Manual cache pattern
cached_api_call <- function(url, cache_file, max_age = 3600) {
  if (file.exists(cache_file)) {
    age <- as.numeric(Sys.time() - file.mtime(cache_file))
    if (age < max_age) {
      cat('Cache hit\n')
      return(readRDS(cache_file))
    }
  }
  cat('Cache miss, fetching...\n')
  # result <- resp_body_json(request(url) |> req_perform())
  # saveRDS(result, cache_file)
  # result
}
cached_api_call('https://example.com/api', 'cache.rds')

الطلبات المتوازية باستخدام req_perform_parallel()

ترسل req_perform_parallel() طلبات متعددة بالتزامن، مما يقلل إجمالي الوقت اللازم للعمليات الدفعية بدرجة كبيرة. ادمجها مع req_throttle() للالتزام بحدود معدل الطلبات أثناء التنفيذ المتوازي.

library(httr2)

# Build a list of requests
item_ids <- 1:5
reqs <- lapply(item_ids, function(id) {
  request(paste0('https://jsonplaceholder.typicode.com/todos/', id))
})

# Execute all in parallel (requires internet)
# resps <- req_perform_parallel(
#   reqs,
#   on_error = 'continue',  # skip failures
#   progress = TRUE
# )
# results <- lapply(resps, resp_body_json)
# titles <- sapply(results, function(r) r$title)
# print(titles)

# on_error options:
# 'stop'     -> abort on first failure
# 'continue' -> collect errors, keep going
cat('Parallel: much faster for many independent calls')

التعامل مع حدود معدل الطلبات

تبلّغ واجهات API عن حالة حدود معدل الطلبات عبر رؤوس الاستجابة: X-RateLimit-Remaining وX-RateLimit-Reset وRetry-After. اقرأ هذه الرؤوس لتنفيذ تراجع تدريجي ذكي.

library(httr2)

respect_rate_limit <- function(resp) {
  # Check remaining calls
  remaining <- resp_header(resp, 'x-ratelimit-remaining')
  if (!is.na(remaining) && as.integer(remaining) < 5) {
    reset_at <- as.integer(
      resp_header(resp, 'x-ratelimit-reset')
    )
    wait_secs <- max(0, reset_at - as.integer(Sys.time()))
    cat(sprintf('Rate limit nearly exhausted. Waiting %ds\n', wait_secs))
    # Sys.sleep(wait_secs)
  }
  # Handle 429 Retry-After header
  if (resp_status(resp) == 429) {
    retry_after <- resp_header(resp, 'retry-after')
    cat(sprintf('Rate limited. Retry after %s seconds\n', retry_after))
  }
  resp
}
cat('Always respect X-RateLimit-* headers')

عميل API متكامل: مثال GitHub

بتجميع كل ما سبق، ننشئ عميل GitHub API متكاملًا يُجري المصادقة، ويجلب قوائم المستودعات المقسمة إلى صفحات، ويتعامل مع الأخطاء، موضحًا جميع الأنماط التي تعلمتها حتى الآن.

library(httr2)

# GitHub API client
github_repos <- function(username, token = NULL, n_pages = 3) {
  base <- 'https://api.github.com'
  req_base <- request(base) |>
    req_headers(
      'Accept'     = 'application/vnd.github.v3+json',
      'User-Agent' = 'R-API-Client/1.0'
    )
  if (!is.null(token))
    req_base <- req_base |> req_auth_bearer_token(token)

  all_repos <- list()
  for (page in seq_len(n_pages)) {
    # resp <- req_base |>
    #   req_url_path_append('users', username, 'repos') |>
    #   req_url_query(page = page, per_page = 30, sort = 'updated') |>
    #   req_perform() |> resp_check_status()
    # repos <- resp_body_json(resp, simplifyVector = TRUE)
    # if (length(repos) == 0) break
    # all_repos[[page]] <- repos
    cat(sprintf('Would fetch page %d for %s\n', page, username))
  }
  do.call(rbind, all_repos)
}
github_repos('hadley')

بيانات اعتماد عميل OAuth 2.0

تتطلب بعض واجهات API تدفق بيانات اعتماد عميل OAuth 2.0، إذ تُبادَل قيمتا client_id وclient_secret برمز وصول. وتتولى oauth_client() وreq_oauth_client_credentials() في httr2 ذلك تلقائيًا.

library(httr2)

# OAuth 2.0 Client Credentials flow:
# client <- oauth_client(
#   id     = Sys.getenv('CLIENT_ID'),
#   secret = Sys.getenv('CLIENT_SECRET'),
#   token_url = 'https://auth.example.com/oauth/token'
# )

# Automatic token management:
# resp <- request('https://api.example.com/data') |>
#   req_oauth_client_credentials(client) |>
#   req_perform()
# httr2 automatically:
# 1. Gets access token using client credentials
# 2. Adds 'Authorization: Bearer <token>' header
# 3. Refreshes token when expired

cat('req_oauth_client_credentials() handles token lifecycle')
cat('Token is cached in memory automatically')

اختبار سريع

اختبر فهمك لإنشاء عملاء REST API في R باستخدام httr2.

مراجعة: استهلاك REST APIs

أهم النقاط: أنشئ عملاء قابلين لإعادة الاستخدام عبر تضمين عنوان URL الأساسي والمصادقة وإعادة المحاولة في دالة مُنشئة. استخدم req_auth_bearer_token() للمصادقة، وخزّن الرموز في متغيرات البيئة. عالج تقسيم الصفحات عبر رؤوس Link باستخدام resp_link_url() أو الأنماط القائمة على المؤشرات. تعامل دائمًا مع أخطاء HTTP باستخدام resp_check_status() وtryCatch(). استخدم req_perform_parallel() للطلبات الدفعية. وخزّن النتائج مؤقتًا أثناء التطوير.

library(httr2)

# REST API client template:
make_api_call <- function(url, token,
                          method = 'GET',
                          body = NULL,
                          query = list()) {
  req <- request(url) |>
    req_method(method) |>
    req_auth_bearer_token(token) |>
    req_headers('Accept' = 'application/json') |>
    req_retry(max_tries = 3) |>
    req_throttle(rate = 10/60)
  if (length(query) > 0)
    req <- do.call(req_url_query, c(list(req), query))
  if (!is.null(body))
    req <- req |> req_body_json(body)
  req |> req_perform() |> resp_check_status()
}
cat('Template: auth + retry + throttle + error check')

الأسئلة الشائعة

هل درس «استهلاك REST APIs في R» مجاني؟

نعم — نص درس «استهلاك REST APIs في R» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة R Academy، انتقل إلى CoddyKit PRO. تتضمن دورة R Academy 4 دروس في المجموع.

ماذا ستتعلم في «استهلاك REST APIs في R»؟

صادق باستخدام مفاتيح API، وقسّم النتائج إلى صفحات، وخزّن استجابات API تتمرن على R Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ R Academy؟

لا تُشترط خبرة سابقة. R Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.

كم من الوقت يستغرق درس «استهلاك REST APIs في R»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس R Academy هذا؟

نعم. كل درس في R Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. تحليل JSON باستخدام jsonlite
  2. إجراء طلبات HTTP باستخدام httr2
  3. استهلاك REST APIs في R
  4. التعامل مع بنى JSON المتداخلة
← العودة إلى R Academy