exrequester
exrequester copied to clipboard
Quickly create API clients using module attributes.
Installation
Add exrequester
to your mix.exs
deps
def deps do
[{:exrequester, "~> 0.1.0"}]
end
Then fetch your project's dependencies:
$ mix deps.get
Usage
Start by adding use EXRequester
in your module
defmodule SampleAPI do
use EXRequester
end
This will make defreq/1
macro available. This macro takes a function name and a set of parametrs as its argument
defmodule SampleAPI do
use EXRequester
@get "/path/to/resource/{resource_id}"
defreq get_picture
end
The above is the simplest form to define an api function.
-
@get
is used to define the relative path that will be fetched -
{resource_id}
the resource id to use, this has to be passed when calling the function
Compiling the above will make the following functions available:
defmodule SampleAPI do
def client(base_url)
def get_picture(client, resource_id: resource_id)
end
-
client/1
is used to set hte base url that will be used in get picture -
get_picture/2
will execute the url, it takes the client and the parameters specified in the call todefreq get_picture...
For example, to call get_picture
you would do this:
SampleAPI.client("http://base_url.com")
|> SampleAPI.get_picture(resource_id: 123)
Setting HTTP method
Define a get request endpoint
defmodule SampleAPI do
use EXRequester
@get "/path/to/resource/{resource_id}"
defreq get_resource
@delete "/path/to/resource/{resource_id}"
defreq delete_picture
end
Now to use it:
SampleAPI.client("http://base_url.com")
|> SampleAPI.get_resource(resource_id: 123)
SampleAPI.client("http://base_url.com")
|> SampleAPI.post_picture(resource_id: 123, body: %{key: value})
This will hit
http://base_url.com/path/to/resource/123
Available http methods are:
defmodule SampleAPI do
use EXRequester
@get "/path/to/resource/{resource_id}"
defreq get_resource
@put "/path/to/resource/{resource_id}"
defreq put_resource
@delete "/path/to/resource/{resource_id}"
defreq delete_resource
end
Handling request body
Body is handled as a normal parameter
defmodule SampleAPI do
use EXRequester
@post "/path/to/resource/{resource_id}"
defreq post_picture
end
Body is handled in a special way based on its type.
- String bodyis sent as is
- List and map bodies is Json encode
SampleAPI.post_picture(resource_id: 123, body: ["1", "2"])
SampleAPI.post_picture(resource_id: 123, body: %{key: value})
Will send json:
[\"1\", \"2\"]
and
{\"key\":\"value\"}
- Keyword list are currently ignored and will send an empty body
Handling query
To add query to your api endpoint you would use the following:
defmodule SampleAPI do
use EXRequester
@query [:sort, :filter]
@get "/path/to/resource/{resource_id}"
defreq get_resource
end
You now can call the function defined with all, some or none of the query values:
SampleAPI.client("http://base_url.com")
|> SampleAPI.get_resource(resource_id: 123)
SampleAPI.client("http://base_url.com")
|> SampleAPI.get_resource(resource_id: 123, sort: "ascending")
SampleAPI.client("http://base_url.com")
|> SampleAPI.get_resource(resource_id: 123, sort: "ascending", filter: "all")
These will hit the following endpoint in order:
http://base_url.com/path/to/resource/123
http://base_url.com/path/to/resource/123?sort=ascending
http://base_url.com/path/to/resource/123?sort=ascending&filter=all
Setting headers
Dynamic Headers
defmodule SampleAPI do
use EXRequester
@headers [
Authorization: :auth,
Key1: :key1
]
@get "/path/to/resource/{resource_id}"
defreq get_resource
end
Now to use it:
SampleAPI.client("http://base_url.com")
|> SampleAPI.get_resource(resource_id: 123, auth1: "1", key1: "2")
This will hit
http://base_url.com/path/to/resource/123
The Authorization
and Key1
headers will also be set.
Static Headers
Static headers are defined by using strings, instead of atom, in the @headers
definition
defmodule SampleAPI do
use EXRequester
@headers [
Authorization: :auth,
Accept: "application/json",
"Accept-Language": "en-US"
]
@get "/path/to/resource/{resource_id}"
defreq get_resource
end
Calling SampleAPI.get_resource
will perform a request that always sends these headers:
Accept: application/json
Accept-Language: en-US
Notice the use of quotes in the "Accept-Language"
. This is needed since Accept-Language
is not a valid atom name. In order to solve that, add quotation around atoms.
Decoding HTTP Response
EXRequester
allows you to define a parse function/block to be used as a parser for the resceived response.
The parser can be set in three ways.
First: You can pass the anonymouse function at the function definition, For example:
defmodule SampleAPI do
....
defreq get_resource(fn response ->
"Value is " <> response.body
end)
end
When calling get_resource
the HTTP response of type EXRequester.Response
will be sent to the passed anonymous function.
Using this way, you can create a response decoder in place.
Second: By defining a body to the get_resource function, inside this body, you can use response
object which will be injected by the macro
defmodule SampleAPI do
....
defreq get_resource do
"Value is " <> response.body
end
end
response
will be set by the macro to the value of the EXRequester.Response
received.
Alternatively, you can pass a response decoder when calling the method pass a decoder as a parameter when calling get_resource
For example:
SampleAPI.client("http://base_url.com")
|> SampleAPI.get_resource(resource_id: 123, auth: "1", decoder: fn response ->
# Parse the response and return a new one
"Response is " <> response.body
end)
The anonymous function passed to decoder will receive an EXRequester.Response
structure. The anonymous function can parse the response and return a new response.
The returned new parsed response will finally returned from get_resource
.
In the above example, the return value will be "Response is The body content"
Handle response
Hitting any request will return a EXRequester.Response
strucutre.
This structure contains headers
, status_code
and body
The body will not be parsed and will be returned as is.
Runtime safty
When calling the wrong method at runtime, exrequester
will fail with a descriptive message.
For example:
@get "/path/to/resource/{resource_id}"
defreq get_resource
If you wrongly call the method as:
AMod.client("http://localhost:9090/")
|> AMod.get_resource(key: 123)
The following error will be raised:
** (RuntimeError) You are trying to call the wrong function
get_resource(client, key: key)
please instead call:
get_resource(client, resource_id: resource_id)
The error will inform you about the correct method invocation
Future improvments
- Ability to set the URL in the function definition instead