8
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ElixirDesktopで作るブログアプリ モバイル ベース作成 APIクライアント作成

Posted at

はじめに

この記事はElixirアドベントカレンダー2025シリーズ2の18日目の記事です。

アプリのベースの作成と認証関連ファイルの作成の不要なもの削除、APIクライアントモジュールを作成します

モバイルアプリのベースを作成

いつものコマンドで作成します
ectoとかを色々使いまわしたいのでdbはデフォルトのpostgresqlで行きます

mix phx.new blog_app

作成したらデ開発用のデスクトップアプリ化とiOS,Androidのプロジェクトを作成します

mix.exs
  defp deps do
    [
-     {:bandit, "~> 1.5"}
+     {:bandit, "~> 1.5"},      
+     {:desktop_setup, github: "thehaigo/desktop_setup", only: :dev}
    ]
  end
mix deps.get
mix depktop.install
mix desktop.setup.ios
mix desktop.setup.android

APIサーバーはport 4000で動くので被らないように4001にしておきます

config/dev.exs
config :blog_app, BlogAppWeb.Endpoint,
  # Binding to loopback ipv4 address prevents access from other machines.
  # Change to `ip: {0, 0, 0, 0}` to allow access from other machines.
-  http: [ip: {127, 0, 0, 1}, port: String.to_integer(System.get_env("PORT") || "4000")],
+  http: [ip: {127, 0, 0, 1}, port: String.to_integer(System.get_env("PORT") || "4001")],
  check_origin: false,
  code_reloader: true,
  debug_errors: true,
  secret_key_base: "vRyeXRi9mEUimeCBErvjdcuNS506gCtCCyLpckNM4XdILmjoYiJnU0Urq4mjYeHS",
  watchers: [
    esbuild: {Esbuild, :install_and_run, [:blog_app, ~w(--sourcemap=inline --watch)]},
    tailwind: {Tailwind, :install_and_run, [:blog_app, ~w(--watch)]}
  ]

最終的には消えますが、開発中はDBに繋がっていると楽なので、DB名をapiと同じにしておきます

config/deve.exs
# Configure your database
config :blog_app, BlogApp.Repo,
  username: "postgres",
  password: "postgres",
  hostname: "localhost",
- database: "blog_app_dev",
+ database: "blog_dev",
  stacktrace: true,
  show_sensitive_data_on_connection_error: true,
  pool_size: 10

認証周りの下準備

スキーマやセッション管理を使い回すのでphx.gen.authを実行します

mix phx.gen.auth Accounts User users

パスワード関連を削除

パスワードは使用しないのとやるとしてもAPIサーバーで行うので削除していきます

bcryptはモバイル側では使わないので削除します

mix.exs
  defp deps do
    [
-     {:bcrypt_elixir, "~> 3.0"},
      {:desktop, "~> 1.5"},
      ...
    ]
  end

パスワードは使わない方針なので以下をごっそり削ります

lib/blog_app/accounts/user.ex:L59-L131
  @doc """
  A user changeset for changing the password.

  It is important to validate the length of the password, as long passwords may
  be very expensive to hash for certain algorithms.

  ## Options

    * `:hash_password` - Hashes the password so it can be stored securely
      in the database and ensures the password field is cleared to prevent
      leaks in the logs. If password hashing is not needed and clearing the
      password field is not desired (like when using this changeset for
      validations on a LiveView form), this option can be set to `false`.
      Defaults to `true`.
  """
  def password_changeset(user, attrs, opts \\ []) do
    user
    |> cast(attrs, [:password])
    |> validate_confirmation(:password, message: "does not match password")
    |> validate_password(opts)
  end

  ...
  
  def valid_password?(_, _) do
    Bcrypt.no_user_verify()
    false
  end

テストの設定の方にも記載があるので消しておくとともにdb名も変更しておきます

config/test.exs
import Config

# Only in tests, remove the complexity from the password hashing algorithm
- config :bcrypt_elixir, :log_rounds, 1

# Configure your database
#
# The MIX_TEST_PARTITION environment variable can be used
# to provide built-in test partitioning in CI environment.
# Run `mix help test` for more information.
config :blog_app, BlogApp.Repo,
  username: "postgres",
  password: "postgres",
  hostname: "localhost",
- database: "blog_app_test#{System.get_env("MIX_TEST_PARTITION")}",
+ database: "blog_test#{System.get_env("MIX_TEST_PARTITION")}",  
  pool: Ecto.Adapters.SQL.Sandbox,
  pool_size: System.schedulers_online() * 2

不要ファイルの削除

使わないのを色々消しときます
スキーマ管理はAPIでやるので以下のファイルを消しときます

  • lib/blog_app/accounts/user_notifier.ex
  • lib/blog_app/accounts/user_token.ex
  • lib/blog_app_web/live/user_live/confirmation.ex
  • priv/repo/migrations/20251219080415_create_users_auth_tables.exs

モデル周り修正

API側のデータのidがULIDなので、合わせてこちらもULIDを入れておきます

mix.exs
  defp deps do
    [
-     {:desktop_setup, github: "thehaigo/desktop_setup", only: :dev}    
+     {:desktop_setup, github: "thehaigo/desktop_setup", only: :dev},
+     {:ecto_ulid_next, "~> 1.0.2"}
    ]
  end
lib/blog_app/accounts/user.ex
defmodule BlogApp.Accounts.User do
  use Ecto.Schema
  import Ecto.Changeset

+ @primary_key {:id, Ecto.ULID, autogenerate: true}
+ @foreign_key_type Ecto.ULID  

APIクライアントの作成

APIリクエストする部分を作成します

APIのベースとなるモジュールを作ります
clientは以下の2種類を作成

  • 接続先サーバーとプレフィックスをセットしたAPIクライアント(トークンなし)
  • 接続先サーバーとプレフィックスをセットしたAPIクライアント(トークンあり)

レスポンスのヘルパーとして以下を実装します

  • assign_error -> エラーレスポンスを各モデルのchangesetに対応した形式に変換
  • key_convert -> レスポンスデータのキーをAtomに変換してモデルに変換しやすくする
lib/blog_app/api.ex
defmodule BlogApp.Api do
  alias Ecto.Changeset

  def client() do
    url = Application.get_env(:blog_app, :server_address, "http://localhost:4000")
    req = Req.new(base_url: url <> "/api/v1")

    case File.read(BlogApp.token_path()) do
      {:ok, token} ->
        Req.Request.put_header(req, "authorization", "Bearer #{token}")

      {:error, _} ->
        req
    end
  end

  def client(:no_auth) do
    url = Application.get_env(:blog_app, :server_address, "http://localhost:4000")
    Req.new(base_url: url <> "/api/v1")
  end

  def assign_error(changeset, resp) do
    changeset
    |> Map.put(:errors, [])
    |> then(
      &Enum.reduce(resp.body["errors"], &1, fn {k, v}, acc ->
        Changeset.add_error(acc, String.to_existing_atom(k), List.first(v))
      end)
    )
    |> Map.put(:action, :update)
  end

  def key_convert(res) do
    Enum.into(res, %{}, fn {k, v} -> {String.to_atom(k), v} end)
  end
end

トークンのパスを以下のようにヘルパーとして登録してきます

lib/blog_app.ex:L20
  def token_path() do
    config_dir() <> "/token"
  end

最後に

長くなったので一旦ここまでで、続きは明日の記事で解説します
本記事は以上になりますありがとうございました

8
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?