はじめに
この記事はElixirアドベントカレンダー2025シリーズ2の18日目の記事です。
アプリのベースの作成と認証関連ファイルの作成の不要なもの削除、APIクライアントモジュールを作成します
モバイルアプリのベースを作成
いつものコマンドで作成します
ectoとかを色々使いまわしたいのでdbはデフォルトのpostgresqlで行きます
mix phx.new blog_app
作成したらデ開発用のデスクトップアプリ化とiOS,Androidのプロジェクトを作成します
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 :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と同じにしておきます
# 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はモバイル側では使わないので削除します
defp deps do
[
- {:bcrypt_elixir, "~> 3.0"},
{:desktop, "~> 1.5"},
...
]
end
パスワードは使わない方針なので以下をごっそり削ります
@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名も変更しておきます
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.exlib/blog_app/accounts/user_token.exlib/blog_app_web/live/user_live/confirmation.expriv/repo/migrations/20251219080415_create_users_auth_tables.exs
モデル周り修正
API側のデータのidがULIDなので、合わせてこちらもULIDを入れておきます
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
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に変換してモデルに変換しやすくする
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
トークンのパスを以下のようにヘルパーとして登録してきます
def token_path() do
config_dir() <> "/token"
end
最後に
長くなったので一旦ここまでで、続きは明日の記事で解説します
本記事は以上になりますありがとうございました