A Clojure library for interacting with the Shopify platform.
shopify.resources
: functions for interacting with a shop's resources.shopify.friend
: provides a friend workflow to authenticate ring apps with Shopify shops using OAuth2.
There is documentation and annotated source.
Shopify artifacts are released to Clojars.
Leiningen is the way to go for managing dependencies in Clojure. Add the following dependency to your project.clj
:
:dependencies [[shopify "0.1.0"]]
First, you'll need to get some API credentials and a development shop.
The easiest way of accessing an individual shop is by creating a private app, but this is only really useful for experimentation. To use OAuth2, you need to create an app through your Partner account.
Private apps are the quickest way of getting started. Just get some credentials and put them in a map:
(def auth {:shop "my-dev-shop.myshopify.com"
:api-key "6dd96e9b19a0a7b3792f354eaf2a982b"
:password "195b5baab7ce4581a861e925e930301d"})
Fire up a REPL and define an auth map for a private app as shown above. Now bring in the shopify.resources
namespace:
(require '[shopify.resources :as shop])
And try out a few simple requests:
(shop/get-shop auth)
; {:country "CA", :longitude "-79.385324", ...}
(shop/get-list :pages {:limit 2 :fields "id,title"} auth)
; [{:shopify.resources/type :page, :id 9855484, :title "About Us"}
; {:shopify.resources/type :page, :id 9855482, :title "Welcome"}]
(shop/get-one :page {:id 9855484} auth)
; {:published-at "2013-03-10T23:49:41-04:00", :handle "about-us", ...}
(shop/get-count :pages {} auth)
; 2
The first map argument is always assumed to be the params or attributes of the request, which is why an empty map needed to be passed before auth
in the get-count
request (get-shop
never takes any params).
If you don't want to bother passing in the same auth map with every request, you can wrap your requests with the with-opts
macro:
(shop/with-opts auth
{:page-count (shop/get-count :pages)
:first-title (-> (shop/get-list :pages {:limit 1}) first :title)})
; {:page-count 2, :first-title "About Us"}
You can create and update with save!
:
(shop/save! :page {:title "New page!" :body_html "<p>Hello!</p>"} auth)
; {:published-at "2013-03-11T00:11:00-04:00", :handle "new-page", :id 9855576, ...}
(shop/with-opts auth
(-> (shop/get-one :page {:id 9855576})
(update-in [:title] clojure.string/upper-case)
shop/save!
(select-keys [:id :title])))
; {:title "NEW PAGE!", :id 9855576}
The call to save!
in the second example just had a single altered attribute map as its only argument (via the thread-first macro). Instead of figuring out the resource type from the usual first keyword argument, it extracted the type from a :shopify.resources/type
key which is embedded into all attribute maps returned by the library (and which is stripped from request params before they get serialized).
Lastly, you can delete stuff:
(shop/delete! :page {:id 9855576} auth)
; nil
If you're building a Shopify web app, you'll need to use OAuth2. The shopify.friend
namespace provides a friend workflow for this purpose.
First, you'll need a map representing your app:
(def my-shopify-app
{:url "http://localhost:3000"
:key "70bc2f19efa5129f202e661ac6fd38f3"
:secret "8eb54f11bedfad9c3e2287479f2d525c"
:scope [:read_products :read_orders]})
This is used to configure the workflow provided by shopify.friend
:
(def shopify-auth
(shopify.friend/workflow {:api-client my-shopify-app}))
(def app
(-> my-handler
some-other-middleware
(cemerick.friend/authenticate
{:allow-anon? true
:workflows [shopify-auth]})))
Here's a working example. With the middleware in place, you can authenticate a shop by hitting /auth/shopify?shop=some-shop.myshopify.com
. This is most often done with a form.
You can get a list of the current Shopify authentications from the request like so:
(shopify.friend/shopify-auths request)
This returns a sequence of auth maps which can be used as the basis of shop resource requests in the same way as the auth map for a private app.
Copyright © 2012-2013
Distributed under the Eclipse Public License, the same as Clojure.