Phoenix API uygulaması #1

Murat Baştaş
Apr 13 · 3 min read

Merhabalar, uzun bir süredir yan projelerde kullanarak öğrenmeye çalıştığım Elixir dili için bir rehber niteliğinde notlar paylaşacağım yazı serime hoş geldiniz. Bu yazıya sıklıkla Ruby on Rails ile karşlılaştırılmaları yapılan Phoenix web framework ile API-only bir uygulama yazma rehberi olarak bakabilirsiniz. Hazırsam başlayayım… :)

Yeni bir Phoenix projesi oluşturma

Bu adımda Elixir, Erlang ve Phoenix framework’ün sisteminizde kurulu olduğunu varsayıyorum. Değilse eğer sizi şuraya yönlendirebilirim.

Aşağıdaki komutu terminalimizde çalıştırarak bir phoenix projesi açalım.

mix phx.new app --no-html --no-webpack

--no-html ve --no-webpack flagleri projemizin bir front-endi olmadığını belirtiyor, ve phx.new mix taskı da bunu anlayarak front-endsiz bir proje açıyor. Bu komut bulunduğunuz dizinde ./app adında bir dizin açarak içine phoenix boilerplate’i yerleştirecektir. Peşine mix deps.get taskını çalıştırarak bağımlılıkları indirecek ve dizinin içine girecektir.

Şimdi bir API uygulamasının olmazsa olmazı olan auth endpointlerinden başlayabiliriz.

Auth contexti ile User modeli

Öncelikle Auth contexti altında User data modelimizi oluşturalım.

mix phx.gen.context Auth User users name:string email:string:unique password_digest:string confirmed_at:datetime

Phoenix bu Auth contexti altında User data modelimizi oluştururken users adında bir tablo oluşturmak için de gerekli migration dosyalarımızı priv/repo/migrations dizini altına oluşturdu. password_digest isimli string olan alanımız bcrypt ile şifrelemek için mix.exs dosyasında deps fonksiyonuna aşağıdaki hex paketini ekleyelim.

# Add bcrypt for pass hashing in mix.exs {:bcrypt_elixir, "~> 2.0"}

mix deps.get komutunu çalıştırınca bcrypt_elixir paketi hex.pm den indirilerek yüklenecektir.

Bu arada testlerde performansı arttırmak amacı ile config/test.exs konfigürasyon dosyasına aşağidaki ayarları ekleyerek bcrypt güvenlik seviyesini düşürelim.

# config/test.exs
config :bcrypt_elixir, :log_rounds, 4

lib/app/auth/user.ex dosyasını açıp password adında sanal bir field oluşturalım.

field :password, :string, virtual: true
field :password_digest, :string

virtual: true bu field’ın veritabanında olmadığını belirtmektedir. Bu field’ı kullanıcı girdisi olarak alacağız ve üzerinde validasyonlarımızı yapıp, testlerimizden geçer ise password_digest verisini bcrypt ile üreteceğiz.

Bunun için changeset fonksiyonunu aşağıdaki gibi değiştirelim.

def changeset(user, attrs) do
  user
  |> cast(attrs, ~w(name email password confirmed_at)a)
  |> validate_required(~w(name email password)a)
  # E-mail validasyonu
  |> unique_constraint(:email)
  |> validate_format(:email, ~r/([\w-\.]+)@((?:[\w]+\.)+)([a-zA-Z]{2,4})/)
  # Password validasyonu (min 6 karakter ve confirmation ister)
  |> validate_length(:password, min: 6)
  |> validate_confirmation(:password)
  # Changeset'e password_digest ekle
  |> put_password_digest()
enddefp put_password_digest(
  %Ecto.Changeset{valid?: true, changes: %{password: password}} = changeset) do
  change(changeset, password_digest: Bcrypt.hash_pwd_salt(password)) enddefp put_password_digest(changeset), do: changeset

Yukarıda yorum satırlarında bahsettiğim gibi validasyonlarımız gerçekleşecek ve sıkıntısız changeset geldi ise bcrypt ile şifrelenmiş bir password_digest değeri oluşacaktır.

Otomatik oluşturulan auth testleri bizim eklediklerimiz ile bozulduğu için orayı düzeltsek iyi olur. *_attrs değerlerine bakınız…

CORS (Cross-Origin Resource Sharing)

API’ımıza hangi originlerden ve hangi methodlarla erişilebileceğini belirtmek ve ya kısıtlamak için mix.exse cors_plug paketini ekliyoruz.

Ve ardından mix deps.get ile paketi indirirken config/dev.exs ve config/prod.exs dosyalarında aşağıdaki tanımlamaları yapıyoruz.

# config/dev.exs
config :cors_plug,
  origin: ~r/localhost/,
  max_age: 86400, # 24 hours
  methods: ["GET", "POST", "PUT", "PATCH", "DELETE"]# config/prod.exs
config :cors_plug,
  origin: ["https://domain.com", "https://example.com"],
  max_age: 86400, # 24 hours
  methods: ["GET", "POST", "PUT", "PATCH", "DELETE"]

İlk endpointimiz

Phoenix de aynı Rails gibi güzel generatorlere(mix tasks) sahip. Json endpointleri hazırlamak için aşağıdaki mix taskını kullanabiliriz.

mix phx.gen.json Auth User users name:string email:string password:string --no-context --no-schema# it asks for proceed that with existed Auth context, you can say [Y]  * creating lib/app_web/controllers/user_controller.ex
  * creating lib/app_web/views/user_view.ex
  * creating test/app_web/controllers/user_controller_test.exs
  * creating lib/app_web/views/changeset_view.ex
  * creating lib/app_web/controllers/fallback_controller.ex  Add the resource to your :api scope in lib/app_web/router.ex:
    resources "/users", UserController, except: [:new, :edit]

router.ex dosyamıza rotamızı ekleyelim.

scope "/api", AppWeb do
  pipe_through :api
  # ...
  scope "/v1", V1 do
    resources "/users", UserController, except: [:new, :edit]
  end
end

Tarayıcımızdan http://localhost:4000/api/v1/users adresine gidersek yeni rotamızı görebiliriz.

Tabi user_controller_test dosyamızı da testleri geçecek şekilde güncellemeyi de ihmal etmeyelim. Özellikle password fieldını gördüğümüz her yerden kaldıralım.

lib/app_web/views/user_view.ex dosyasında da password ü kaldırmayı unutmayalım.

Şimdi şuradan devam edebilirsiniz…


Originally published at murat.github.io on April 13, 2019.

Murat Baştaş

Written by

full stack web developer at omg!medya #ruby #rails #php #laravel