timpile
Handwrite - Unofficial API wrapper for Handwrite.io
I started using the Handwrite REST API for a side project and since they don’t have an API library for Elixir I decided to make an unofficial one for them: handwrite | Hex.
This is my first published Hex package and I have only used Elixir for side projects, so I’d love to make this library more stable and usable. I’m sure there is a lot of improve on, so any feedback or code reviews would be greatly appreciated!
First Post!
malloryerik
I checked out handwrite.io because of your library, but I see their pricing policy is “inquire within”. I’m guessing they’ve got reasonable pricing if you’re using it on side projects?
Most Liked
timpile
I’m still only using it in a sandbox environment, but as far as I can tell the pricing is comparable to other card printing services. They are very responsive, if you reach out to them I’m sure they can give you a better answer.
chulkilee
Welcome! My first hex package was also a rest api wrapper (ex_force) ![]()
Here are some feedback/thoughts:
Dependency
- Although they are mostly swappable, recent packages chose jason instead of poison for json
- http client - I’m using tesla whenever possible since users can swap the http library easily.
Handwrite.Client
- Instead of
"#{secret_key()}", you can make sure secret_key returns string value.. which leads to the next point secret_key/0callsSystem.get_env/1on runtime - it may be okay for application (although not recommended), but for library, you should use application configuration (e.g.Application.get_env/3) (for global config) or pass that value down to the function.- For global configuration - check out sentry package
- See configuration related sections at Library Guidelines as well
Code organization
- I found you made a module for each resource - such as
Handwrite.Endpoint.Handwriting. There is no right answer, but I found it’s actually easier to read, use, and maintain the code I put the all things under one module if they’re at the similar level resources. See ExForce module source.- See also ExAws.S3 where the module have functions for each API call and introduce modules for high-level tasks, such as ExAws.S3.Upload (src)
Last Post!
chulkilee
Oh, I agree with you in that global config for a library must be avoided.
I should have been more clear on this part
I wanted to give hints to move away from using System.get_env/1 first…
For starter - and this kind of library, this is very likely to have one instance - which is “acceptable” (although I don’t prefer) to have (global) application config. That’s why I refer to the library guides lines, which has a section for Avoid application configuration ![]()
@timpile for the configuration - it’s better to have a library to provide building blocks and let library users load config as needed.
For example:
# in config
config :my_app, :handwrite, url: "https://example.com", api_key: "..."
# in your helper module
defmodule MyApp.Handwrite do
def client do
opts = Application.get_env(:my_app, :handwrite)
Handwrite.client(Keyword.fetch!(opts, :url), Keyword.fetch!(opts, :api_key)
end
end
# then you use it like this
MyApp.Handwrite.client()
|> Handwrite.create_whatever(my_opts)
By doing this, your library does not care how to store and retrieve the configuration at all - and that is in the library user code.
I did make a mistake when I wrote the first library - I had default config value and make authentication happens when not passed to make it easy to use.. but I found it’s not good. See my commit to change from httpoison to tesla with 1) dropping default config and 2) making all functions to take a tesla struct, which holds the all information such as url and auth token - see Replace HTTPoison with Tesla · chulkilee/ex_force@8d32d9a · GitHub
Trending in Announcing
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #elixirconf-us
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #security
- #hex









