Phoenixお気軽API開発③：軽量APIにて実装した一覧REST APIにDBを接続する

[fukuoka.ex／kokura.ex](https://fukuokaex.connpass.com/)のpiacereです
ご覧いただいて、ありがとうございます :bow:

[前作の軽量APIでの参照REST API実装](https://qiita.com/piacerex/items/e11ef7b5cdcc81ac69c9)を元に、DB接続を実装してみます

DBは、ローカルPostgreSQLやCloud SQLとかでは無く、Elixir標準のインメモリ＆ローカルファイルDB「Mnesia」でサクっと済ませます（これ以降のどこかで、外部APIやGraphQLでサクっとやってもいいかも知れない）

# 本コラムの検証環境

本コラムは、以下環境で検証しています（Windowsで実施していますが、Linuxやmacでも動作する想定です）

- Windows 10
- Elixir 1.10.1 　　　　 ※[<b>最新版のインストール手順はコチラ</b>](https://qiita.com/piacere_ex/items/6714e1440e3f25fb46a1#%E4%BA%8B%E5%89%8D%E6%BA%96%E5%82%99elixir%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB)
- Phoenix 1.4.15　　　※[<b>最新版のインストール手順はコチラ</b>](https://qiita.com/piacerex/items/b7787580fce5f148242f#phoenix%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB)
- Node.js 12.14.0

あと下記コラムシリーズの続きとして実施するので、未実施であれば、事前に実施しておいてください（近々、ボイラープレート生成のmixコマンドとしてOSS化は考えています）

[<b>PHP的ハックを応用してNode.js Express／Go的な軽量APIをPhoenixで実現してみた</b>](https://qiita.com/piacerex/items/144089aa6f2052f4d1cb)
|> [<b>Express／Go的なPhoenix軽量APIの上に参照系REST APIを実装する</b>](https://qiita.com/piacerex/items/e11ef7b5cdcc81ac69c9)

# 手順①：Mnediaテーブル作成、データ投入

まず、Mnesiaを起動し、テーブル作成します

このテーブルは、ファイル化して永続化してあるので、インメモリ時のような、Phoenixを落とすとDBが消えてしまうといったことがありません

```
iex> :mnesia.create_schema( [ node() ] )
:ok
iex> :mnesia.start
:ok
iex> :mnesia.create_table( :members, [ attributes: [ :id, :name, :age, :team, :position ], disc_copies: [ node() ] ] )
{:atomic, :ok}
```

次に、データ投入用コードを用意します

```elixir:lib/basic.ex
defmodule Basic do
  def inserts() do
    :mnesia.transaction( fn -> :mnesia.write( { :members, 1, "enぺだーし", 49, "有限会社デライトシステムズ", "代表取締役、性能探求者" } ) end )
    :mnesia.transaction( fn -> :mnesia.write( { :members, 2, "ざっきー", 45, "公立大学法人 北九州市立大学", "准教授、カーネルハッカー" } ) end )
    :mnesia.transaction( fn -> :mnesia.write( { :members, 3, "つちろー", 34, "カラビナテクノロジー株式会社", "リードエンジニア、アプリマイスター" } ) end )
    :mnesia.transaction( fn -> :mnesia.write( { :members, 4, "piacere", 43, "カラビナテクノロジー株式会社", "CTO、福岡Elixirプログラマ、重力プログラマ、技術顧問" } ) end )
  end
end
```

データ投入します

```
iex> recompile
Compiling 1 file (.ex)
:ok
iex> Basic.inserts
{:atomic, :ok}
```

# 手順②：DBアクセッサモジュールを作り、select処理を追加

PJフォルダ内のlibフォルダ配下にutilフォルダを掘り、下記コラムと同一のDBアクセスモジュールを作ります

[<b>Excelから関数型言語マスター3回目：WebにDBデータ表示【Mnesia編】</b>](https://qiita.com/piacerex/items/a7558adc6856e3577dc6)
https://qiita.com/piacerex/items/a7558adc6856e3577dc6

```elixir:lib/util/db_mnesia.ex
defmodule DbMnesia do
	def select( table_name ) do
		:mnesia.start
		table_atom = table_name |> String.to_atom
		:mnesia.wait_for_tables( [ table_atom ], 1000 )
		columns = :mnesia.table_info( table_atom, :attributes ) 
			|> Enum.reduce( [], fn( item, acc ) -> acc ++ [ Atom.to_string( item ) ] end )
		columns_spec = 1..Enum.count( columns ) 
			|> Enum.reduce( { table_atom }, fn( x, acc ) -> Tuple.append( acc, :"$#{ x }" )  end )
		rows = :mnesia.transaction( fn -> 
			:mnesia.select( table_atom, [ { columns_spec, [], [ :"$$" ] } ] ) end ) |> elem( 1 )
		%{
			columns: columns, 
			command: :select, 
			connection_id: 0, 
  			num_rows: Enum.count( rows ), 
			rows: rows
		}
	end
end
```

```elixir:lib/util/db.ex
defmodule Db do
	def query( sql ) when sql != "" do
		Regex.named_captures( ~r/select( *)(?<columns>.*)( *)from( *)(?<tables>.*)/, sql )[ "tables" ]
		|> DbMnesia.select
	end
	def columns_rows( result ) do
		result
		|> rows
		|> Enum.map( fn row -> Enum.into( List.zip( [ columns( result ), row ] ), %{} ) end )
	end
	def rows( %{ rows: rows } = _result ), do: rows
	def columns( %{ columns: columns } = _result ), do: columns
end
```

# 手順③：index.json.eex内でDBデータ取得

上記で投入したDBデータを取得し、JSON化していきます

まず、1件JSON返却を、DBデータ列から作るようにします

```elixir:lib/basic_web/templates/api/v1/users/data.json.eex
%{
  name:     data[ "name" ], 
  age:      data[ "age" ], 
  team:     data[ "team" ], 
  position: data[ "position" ]
}
```

次に、DBデータ取得し、for式の中で、データ1件ずつをdata指定してテンプレート展開を繰り返します

```elixir:lib/basic_web/templates/api/v1/users/index.json.eex
json = File.read!( "lib/basic_web/templates/api/v1/users/data.json.eex" ) 
datas = Db.query( "select * from members" ) |> Db.columns_rows

for data <- datas do
  json |> Code.eval_string( [ params: params, data: data ] ) |> elem( 0 )
end
```

# 手順④：動作確認

これで準備完了です

RESTクライアントで``「GET http://localhost:4000/api/v1/users」``のアクセスを行うと、以下のように、DBからデータ取得した行が、一覧としてJSON返却されます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155423/7bbf6e08-c146-ac93-64d4-644d7261ea23.png)

# 終わり

JSONテンプレートでDBデータ取得を行い、一覧REST APIにてJSON返却できるようにしました

[次回は、DBモジュールをwhere対応しつつ、1件ずつのDB値取得に対応します](https://qiita.com/piacerex/items/7c969e42e7cf4fa5ed50)

# p.s.このコラムが、面白かったり、役に立ったら…

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155423/67568d54-6584-ad33-77c7-b3b3afe70287.png) や ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155423/f5b819ef-d655-ef72-294c-b1a9d2c5b563.png) にて、どうぞ応援よろしくお願いします:bow: