A Clojure library which converts a REST specification into functions that emit clj-http
request maps.
Very light-weight.
This library requires Clojure 1.9.0 or higher.
Add to dependencies:
[clj-rest-client "1.0.0"]
In your namespace add the dependency:
(ns example.core
(:require [clj-rest-client.core :refer [defrest]]
[clj-http.client :as client]))
Define a rest interface:
(defrest {"http://example.com" {"person" {GET (get-person-by-id [id pos-int?])}}})
This defines a function named get-person-by-id
that you can now call and it will return a clj-http
compatible map.
You can then run the request by using clj-http
's request function:
(client/request (get-person-by-id 3))
The function is spec instrumented and will raise an error if parameters aren't valid by spec.
You can make the HTTP call immediate by adding an HTTP request executing function as a client function to API definition:
(defrest {"http://example.com" {"person" {GET (get-person-by-id [id pos-int?])}}} :post-process-fn client/request)
(get-person-by-id 3)
All data structure merges in this framework are deep merges using meta-merge
library. See https://github.com/weavejester/meta-merge for specifics.
A series of tutorials using GitHub v3 API as basis:
A description of features, options and solutions for common use-cases.
This framework allows multiple different ways to specify things so I urge you to read all the tutorials
The definition is a nested map where keys are string paths or symbols/keywords HTTP methods.
{"path"
{"subpath" {DELETE ...}
GET ...
:post ...}}
This nested structure defines endpoints for GET and POST methods at "/path". Path parts should not start or end with /
.
It also continues nesting and defines an endpoint for "/path/subpath" for DELETE method.
Values for path parts are another path map, values for methods are endpoint function definitions
Symbol or keyword keys define the signature for an HTTP method for this subpath. E.g.
(defrest {"http://example.com"
{"person"
{"{id}"
{GET (get-person-by-id [id pos-int? detail-level pos-int?] {:as :bytes})}}}})
Here the GET symbol key is followed by an endpoint definition, which defines a function for GET method for http://example.com/person
subpath.
The method can be specified equally as GET
or get
or :get
.
The string keys in the nested definition map are there to denote subpaths. They shouldn't start or end with /
.
In this particular example the root string key also defines the protocol, server, port.
This is in no way required. You can define a REST with relative paths only.
It is demonstrated in our tutorials how to approach using definitions without a pre-defined host.
{GET (get-example any? [id pos-int? time inst?] {:as :bytes})}
The endpoint definition is a list or vector (in this example a list), which contains the following:
-
a symbol, name of a function to be declared
-
an optional spec that is applied to the conformed parameter list
-
an argument vector; must contain alternating parameter names and parameter specs. The vector may contain symbol
&
, all argument specs following the symbol are added as kw-varargs on the generated function with provided specs wrapped innilable
. -
an optional non-vector expression that evaluates to a map, defaults to
{}
, contains additional properties for returned request map, it can use parameters in the definition
In this example, the spec applied to parameter list [id time]
will be (s/& (s/cat :id pos-int? :time inst?) any?)
.
Due to optional parts of the definition, the following definition is also legal:
{GET (get-all [])}
If the endpoint is the only one at a subpath you can skip specifying the method and it gains the default method:
(get-all [])
The method given to such endpoint definition is set by :default-method
defrest
parameter, defaulting to :get
.
The macro supports options as varargs key-values.
Here are the options with their defaults:
(defrest {} :param-transform identity :json-responses true :jsonify-bodies :smart :post-process-fn identity)
This option specifies a function that is invoked after generating clj-http in API function. Defaults to identity.
There are benefits to separating request generation and request execution, thus the default being identity function.
Sets the default method for endpoints with no method specified, defaults to :get
This option specifies function that is used to transform query parameter names: parameter (symbol) -> query parameter name (string).
This is useful to transform Clojure's kebab-case symbol names to camel case param names.
This option specifies a function that is applied to all arguments after argument spec and conforming and before being embedded into the request map. It's a function of two arguments: param name symbol and param value and returns the new param value.
Default implementation replaces keyword params with their name string. It's available (for delegating purposes) as default-val-transform
in core namespace.
If true then all requests specify {:as :json}
and all responses are expected to be json responses. Defaults to true.
Set to :always
, :smart
, :never
. Body params will be ran through serializer if set to :always
.
Option :smart
will not run string bodies through JSON serializer. Defaults to :smart.
The defaults
option should resolve to a map. This map is included in every request map as a baseline. Defaults to {}
If the definition is a string (link to EDN file), then this parameter specifies
additional readers besides the default readers of #crc/ns
and #crc/ref
.
When loading EDN use a specific key in loaded EDN, useful when using refs.
Emit a fdef for generated functions, defaults to false.
Copyright © 2018, 2019 Rok Lenarčič
Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.