stuwest
stuwest

Reputation: 938

Functional way to make multiple API requests with Clojure

I'm working on a Clojure application that will interact with a web API to return a random result meeting a specific criterion. Because of the limitations of the API, I have to retrieve a number of results and use Clojure to filter out an appropriate one.

I would say 99% or more of the time, at least one of the results from the first request will meet my criterion. But because of that other 1%, I feel I have to build in functionality to make multiple requests. Obviously the odds are pretty good that the second request will be successful but I'm leery about writing a function that recurs endlessly until it gets the right result in case something goes wrong and I end up DDoSing the API.

My first thought was to build a counter into the function and limit it to, say, five requests. But that feels a bit procedural as an approach. What's the idiomatic Clojure way to go about this?

Here's what I have so far:

(ns randchar.api
  (:require [clj-http.client :as client]))

(defn api-request 
  [url]
  (get-in
    (client/request
      {:url url
       :method :get
       :content-type :json
       :as :json}) [:body :results]))

(defn filter-results
  "Returns the first result that meets the criterion."
  [results criterion]
  (take 1 
        (filter #(= (get-in % [:nested :sub-nested]) criterion) results)))

(defn build-url
  "Returns a url with randomized query string for the api request; not shown."
  []
  )

(defn return-result
  "Currently recurs endlessly if no results meet the criterion."
  [criterion]
  (let [x (filter-results (api-request (build-url)) criterion)]
    (if (not (empty? x))
      x
      (recur))))

Upvotes: 3

Views: 367

Answers (1)

Ankur
Ankur

Reputation: 33657

You can try something like:

(defn return-result
  [criterion count]
  (->> (repeatedly count #(filter-results (api-request build-url) criterion))
       (filter (complement empty?))
       first))

Upvotes: 3

Related Questions