how to make post request using tesla in elixir

6.8k Views Asked by At

I'm trying to make post request using tesla but getting error:

(CaseClauseError) no case clause matching: %{name: "ecdebit"}
(hackney) /deps/hackney/src/hackney_request.erl:312::hackney_request.handle_body/4
(hackney) /deps/hackney/src/hackney_request.erl:81::hackney_request.perform/2
(hackney) /deps/hackney/src/hackney.erl:372::hackney.send_request/2
(tesla) lib/tesla/adapter/hackney.ex:69: Tesla.Adapter.Hackney.request/5
(tesla) lib/tesla/adapter/hackney.ex:31: Tesla.Adapter.Hackney.call/2

my request code is

request_body = %{
  name: "ecdebit",
}
Tesla.post(client, "/contactdb/lists", request_body)

in tesla base url is: https://api.sendgrid.com/v3 and also set authorization key. how can we pass data for post request ?

as in tesla documentation define post request as this:

Tesla.post("http://httpbin.org/post", "data", headers: [{"content-type", "application/json"}])

Is there any in this planet who could help to get rid of this glitch :(.

1

There are 1 best solutions below

0
On

Your quote from the docs about Tesla.post():

Tesla.post("http://httpbin.org/post", 
            "data", 
            headers: [{"content-type", "application/json"}])

shows that the first argument is a string containing a url, and the second argument is a string containing the data, and the third argument is a keyword list. Yet, when you call Tesla.post() you pass a path as the second argument, and an Elixir map as the third argument:

Tesla.post(client, 
           "/contactdb/lists", 
           request_body)

Here is how you can take advantage of Tesla's features to incrementally convert your request to what you want:

1) Create a mix project:

~/elixir_programs$ mix new http
* creating README.md
* creating .formatter.exs
* creating .gitignore
* creating mix.exs
* creating config
* creating config/config.exs
* creating lib
* creating lib/http.ex
* creating test
* creating test/test_helper.exs
* creating test/http_test.exs

Your Mix project was created successfully.
You can use "mix" to compile it, test it, and more:

    cd http
    mix test

Run "mix help" for more commands.

~/elixir_programs$ cd http

2) Add jason, hackney, and tesla as dependencies in the mix.ex file:

  defp deps do
    [
      {:jason, "~> 1.0"}, #for tesla JSON middleware
      {:hackney, "~> 1.14.0"}, #recommended for tesla
      {:tesla, "~> 1.2.1"}, #http requests

      # {:dep_from_hexpm, "~> 0.3.0"},
      # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"},
    ]
  end

3) Change lib/http.ex to the following:

defmodule Http do

  use Tesla

  plug Tesla.Middleware.BaseUrl, "https://webhook.site/ab8b7259-feb4-4e62-b8dd-46bb03b614ba"
  plug Tesla.Middleware.FormUrlencoded

  #plug Tesla.Middleware.Headers, [{"header-name", "header-value"}]

  def go do  #Use this function to test whether a Tesla request succeeds without having to repeatedly type the following variables and their values into iex.
    request_body = %{name: "Joel", age: 21}
    path = "/" 
    post(path, request_body)  #Outside of this module, you would need to write Http.post(...)
  end

end

When you specify Tesla.Middleware.BaseUrl, Tesla will automatically prepend the base url to the string you specify as the first argument in the post() function (unless the string starts with "http" or "https").

When you specify Tesla.Middleware.FormUrlencoded, Tesla will automatically convert an Elixir map given as the second argument to the post() function into the format that a form on a web page sends to a server, which simply looks like this:

"name=Joel&age=21"

Tesla also automatically adds the following header to the request:

Contet-Type: application/x-www-form-urlencoded

If you want Tesla to convert the Elixir map into a json string, then specify Tesla.Middleware.JSON instead (note this requires jason in your dependencies). Tesla will also automatically add the following header to the request:

 Contet-Type: application/json

When you specify Tesla.Middleware.Headers, Tesla will automatically add the specified headers to all your requests.

Now try it out in iex:

~/elixir_programs/http$ iex -S Mix
...
...

iex(1)> {:ok, response} = Http.go
{:ok,
 %Tesla.Env{
   __client__: %Tesla.Client{adapter: nil, fun: nil, post: [], pre: []},
   __module__: Http,
   body: "",
   headers: [
     {"cache-control", "no-cache"},
     {"connection", "keep-alive"},
     {"date", "Thu, 29 Nov 2018 03:56:49 GMT"},
     {"server", "nginx/1.10.3"},
     {"vary", "Accept-Encoding"},
     {"content-length", "0"},
     {"content-type", "text/plain; charset=UTF-8"},
     {"x-request-id", "55a92302-6459-49bc-b7eb-f3e7ff037599"},
     {"x-token-id", "ab8b7259-feb4-4e62-b8dd-46bb03b614ba"},
     {"x-ratelimit-limit", "30"},
     {"x-ratelimit-remaining", "29"},
     {"strict-transport-security", "max-age=63072000; includeSubdomains"},
     {"x-frame-options", "DENY"},
     {"x-content-type-options", "nosniff"}
   ],
   method: :post,
   opts: [],
   query: [],
   status: 200,
   url: "https://webhook.site/ab8b7259-feb4-4e62-b8dd-46bb03b614ba/"
 }}

The formatted output is the response. At the website webhook.site, I can see the request (click on the image to enlarge it):

enter image description here

and also set authorization key

Tesla has two middleware modules for authorization:

Tesla.Middleware.BasicAuth 
Tesla.Middleware.DigestAuth