Try T.M Engineer Blog

多摩市で生息するエンジニアが「アウトプットする事は大事だ」と思って始めたブログ

【strapi】HeadlessCMSのstrapiを触ってみた

前回の記事では、HeadlessCMSの概要について書きました。

kodak.hatenablog.com

今回はHeadlessCMSのstrapiを触ってみた記事を書いていきます。

strapiとは?

APIを簡単に作成できるオープンソースのHeadlessCMSです。
まだアルファ版ようですが、完成度が非常に高く、GUIAPIを簡単に作ることができます。
ちなみにstrapiは、Node.jsで作られています。

strapi.io

私が動作確認した時のバージョンは以下の通りです。

  • Strapi 3.0.0-alpha.24.1
  • Node 10.3.0
  • npm 6.4.1

インストール

まずは、以下手順にそってStrapiのインストールとプロジェクトの作成を行います。

Quick Start Guide | Strapi Documentation

npm install strapi@alpha -g
strapi new cms --quickstart

quickstartオプションをつけると、データベースはSQLite3が自動で選択されます。
quickstartオプションをつけずにコマンドを実行して、Customを選ぶとデータベースはSQLite3、MongoDB、MySQL、Postgresから選択できます。
ちなみに、strapiはMongoDBを押しているようです。

APIを作る

ユーザー登録をして、管理画面に入るとAPIを直接作ることができます。
コンテンツタイプ作成からコンテンツタイプの名前を決めて、フィールドを選びます。
注)コンテンツタイプの名前は必ず単数形にしてください。(コンテンツタイプの名前はAPIの名前にもなります。APIは自動で複数形になります。)

f:id:special-moucom:20190503235759p:plain

APIの公開範囲を設定

上記でAPIの作成は完了しています。自動で以下APIが作成されています。

  • count ・・・ コンテンツタイプのデータをカウント
  • create ・・・ コンテンツタイプにデータ追加
  • destroy ・・・ コンテンツタイプからデータを削除
  • find ・・・ コンテンツタイプ内のデータ検索
  • findone ・・・ コンテンツタイプ内のデータ検索(id検索)
  • update ・・・ コンテンツタイプ内のデータ更新

しかし、APIはデフォルトでは未公開の状態になっています。
ロールと権限から(とりあえず)Publicを選択して、自動で作られたAPIの公開範囲を設定します。

f:id:special-moucom:20190503235905p:plain

APIを実行してみる

APIcurlコマンドや、ブラウザから直接URLを叩いてみます。
こんなに簡単にAPIが作られるなんて、衝撃ですよね!!

f:id:special-moucom:20190503235925p:plain

どのような仕組みでAPIが実装しているのか?

では、この自動生成されたAPIはどのようにして実装されているのか、少し深掘りしてみます。strapiのプロジェクト内では以下のようなディレクトリ構成でAPIが作られていることがわかります。(MVCモデルを採用しているようです。)また、strapiのマニュアルを読むと改良時にコードを書くべき場所も指定されていました。

f:id:special-moucom:20190503235942p:plain

少し改良を加えてみる

お題:

コンテンツタイプGoogleuserコンテンツタイプのuidには、重複可能なデータを入力することができてしまう。これを重複できないようにしたい。

回答:

■controllers(変更)

  create: async (ctx) => {
    // 自動生成されたコードはコメントアウト
    // return strapi.services.googleuser.add(ctx.request.body);
    // 登録するuidをカウントしてデータがあるかを確認する。カウント結果が0以外は400エラーを発生させる 。
    if ( await strapi.services.googleuser.uidcount(ctx.request.body) !== 0 ) {
      return ctx.response.badRequest('invalid query');
    }
    return strapi.services.googleuser.add(ctx.request.body);
  },

■services(追加)

  uidcount: async (values) => {
    // query関数を呼び、where句を指定してcount関数を呼び出す
    return Googleuser.query(function(qb){
      qb.where('uid', '=', values.uid );
    }).count();
  },

実行結果:

uid : test-uidのデータを追加するとエラーコード400を返す

$ curl -X POST -H "Content-Type: application/json" -d '{"uid":"test-uid"}' localhost:1337/googleusers
{"statusCode":400,"error":"Bad Request","message":"invalid query"}$

uid : test3(未登録)のデータを追加する登録したコンテンツタイプのデータを返す

$ curl -X POST -H "Content-Type: application/json" -d '{"uid":"test3"}' localhost:1337/googleusers
{"id":23,"uid":"test3","created_at":1556893121297,"updated_at":1556893121297}$

こんな感じでcontrollersとservicesに改良を加えていけば、APIに改良を加えたり、新しいAPIも作ることができそうです。

感想

strapiの完成度の高さに驚きました。なによりGUIで簡単にAPIが作れるのに凄く驚きました。Node.jsで作られているので相性が良いFirebaseと組み合わせて作ると面白そうです。
他にも説明しきれていない機能もたくさんあるので、ちょいちょい見つけたらまた記事にしたいと思います。

ヘッドレスCMSとは?ヘッドレスCMSの概要について調べてみた

私は、CMSWordPress等)を使って仕事をすることがあるのですが、社内であまり聞き慣れない「ヘッドレスCMS」という言葉を耳にしたので、少し調べてみることにしました。

ヘッドレスCMSとは?

ただのCMSとは異なるアーキテクチャで作られたCMSのこと。 CMSWordPress等)は、Webページ(フロントエンド)と管理(バックエンド)が1つに統合されています。

しかし、ヘッドレスCMSはそのWebページ(フロントエンド)と管理(バックエンド)を分けようという構造(アーキテクチャ)から産まれたCMSです。そのため、バックエンドとフロントエンドは各々の開発に集中することができ、CMSもページを管理しないことで、CMS本来の目的であるコンテンツ管理に重点を置くことができます。

このフロントエンドとバックエンドの分けようという構造(アーキテクチャ)を「デカップルド・アーキテクチャ」と呼ぶそうです。

カップルド・アーキテクチャの名前の由来

これがなかなかユニークでおもしろいです。
「2つの〜、結びつける〜」の意味を持つ"couple"(カップル)に否定の"de"をつけて「デカップル」(decouple)という言葉からこの名前が産まれたそうです。 この"couple"(カップル)を指すのは、フロントエンドとバックエンドの事ですね。

詳しくはここを読ませていただきました。
「ヘッドレス」とは?Drupalにおけるデカップルド・アーキテクチャについてご紹介。 | さくらのナレッジ

フロントエンドとバックエンドを分けて考えるとは?

一番オーソドックスな考え方は、バックエンドでAPIを実装して、そのAPI経由でフロントエンドにデータを渡す方法です。
RESTful APIやGraphQL APIを使う方法ですね。
そのため、CMSでよく利用されるテンプレートの用意は必要無く、フロントエンドは自由にWebページを作ることができます。
バックエンドもフロントエンドを意識する必要が無く、開発できます。

上記までの内容を纏めてイメージ図で書いてみると、こんなイメージ。

f:id:special-moucom:20190423230030p:plain

ヘッドレスCMSのメリット・デメリットを考えてみる

個人的に色々と考えてみたのですが、以下メリット・デメリットがあるように思えました。

メリット

  • フロントエンドとバックエンドが並行して作業が可能。
  • 上記にも書きましたが、バックエンドとフロントエンドが各々の開発に集中することができる。
  • テンプレートを意識した設計をしなくて良い(SPA等のフロントエンドモダンな設計も可能)

デメリット

  • テンプレートを使用しないため、開発難易度・コストは高い。
  • APIに対する知識、とくにセキュリティ周りには気を配る必要がある。

ヘッドレスCMSの種類

「ヘッドレスCMS」の種類はこちらで紹介されています。

headlesscms.org

Contentfulなどは、名前だけは聞いたことがあるのではないでしょうか。
あと、CMSの代表であるWordPressにもREST APIの機能を備えているので、ヘッドレスCMSとしても使うことが可能です。

というわけでヘッドレスCMSを使ってみよう!

こちらは次回に書きたいと思います。使ってみるのは「Strapi」です。

「Vue.jsとFirebaseで作るミニWebサービス(技術書典シリーズ)」を写経した話

以前から積読していた『Vue.jsとFirebaseで作るミニWebサービス(技術書典シリーズ)』を読む&写経しました。

改訂新版 Vue.jsとFirebaseで作るミニWebサービス (技術書典シリーズ(NextPublishing))

改訂新版 Vue.jsとFirebaseで作るミニWebサービス (技術書典シリーズ(NextPublishing))

すべての写経を終えると、以下のようなmarkdownで書けるテキスト保存アプリを作ることができます。

f:id:special-moucom:20190416231841p:plain

読者ターゲット

この本は、Vue.jsに触れた事がない人や初心者の方には難しいように思えました。
ある程度Vue.jsを理解している人で、何かしらアプリケーションを作ってみたい!という方をターゲットにしているようです。

本書にも、アプリケーションを作る事が目的であり、Vue.jsの細かな動きについては深く説明しない事が書かれています。

整理をすると、以下に該当する人にはオススメです。

  • ある程度Vue.jsを理解している
  • 何かしらVue.jsを使ってアプリケーション開発がしたい
  • Firebaseを使ってみたい

ちなみに、私は上記3つすべて該当しました。

感想

この本を写経して、はじめてFirebaseを使いましたが、すごく簡単・便利で感動しました。とくに「Firebase Authentication」いわゆるBaaS(Backend as a service)と呼ばれるものですね。

Google,Twiiter,Facebook等でのログインを管理・サポートしてくれる機能ですが、これがなんと無料で使えます。
正直、私自身も「ぇ、無料で使っていいの!?」と驚きました。

使い方も簡単なので、この情報を知り得ただけでも、この本を買ってよかったと思えました。
また、Vue.jsの方もMarkdownをサポートしてくれるライブラリの存在・使い方を知ることができて、学びになりました。
Web上でMarkdownエディターを実装するのが、ライブラリを使うだけとは・・・驚きです。

この本は、Kindle版であれば700円くらいで買うことがでるので、なにかVue.jsを使ってアプリ開発したいなぁと思っている人にはお手軽で良い本かと思います。

そういえば・・・

本のタイトルにもありますが、こちらの本は技術書典で書かれたものをKindle化してAmazonで販売されたもののようですね。
技術書典といえば、先週ありましたね。
私は今回も忙しくて参加できなかったのですが、またこちらのBoothからいくつか本をピックアップして買って読みたいと思います。

booth.pm

とりあえず、「まんがではじめるKubernetes」が気になってしかたない・・・

AWSJapan主催『ハンズオンはじめてのServerless Tokyo 20190326』に参加した話

はじめに

今回、AWSJapan株式会社主催の『ハンズオンはじめてのServerless Tokyo 20190326』に参加してきました。私もはじめてのハンズオン形式のイベントに参加だったので、少しドキドキしました。

今回のハンズオンの中で扱うAWSの機能は以下の通り。

さすが、「Serverless」というタイトルだけあってServerlessの技術で扱うAWSの機能が多く含まれていますね。私も最近マイブームな「Serverless」という単語のハンズオンと聞いてさっそく参加してみました。

私のAWSServerless機能理解度

IAM、LambdaやS3は仕事でも使用した経験があります。他の機能についてはどのような機能なのかは理解していますが、実際に使ったことはありません。
6つの機能の内、3つが使ったことがないので、まさに私向けのハンズオンです!

参加理由

AWSの機能を実際に使ったことがない」という壁はとても厚く感じませんか?
私なんかは、なかなか機会がないとこの壁が壊せなくて困っています(汗
なので、今回のハンズオンでこの壁を壊して、もっとServerlessの機能の理解を深めたいと思ったのが、今回の私の参加理由です。

ハンズオンの流れと何を作ったのか?

会場は、AWSJapan目黒本社のAWS Loft Tokyoですね。 会場はこんな感じです。ハンズオンの参加者も結構多かった印象。

f:id:special-moucom:20190409231545j:plain f:id:special-moucom:20190409231558j:plain

AWS Serverlessの概要説明

まず最初は、AWSJapanの亀田さんがServerlessで扱う各機能の簡単な概要を大変分かりやすく解説してくださいました。
Serverlessは最近流行りだけど、コンプライアンスが厳しいところは導入が難しいよねぇ・・・みたいな話もしてくれて、とても勉強になりました。
とはいえ、ハンズオンがメインのイベントなので、説明時間は30分でイソイソと話した感じでした。

1:Cognitoハンズオン

作品内容

Cognito+IAMを組み合わせたアプリ。
※ハンズオンの資料は公開して良いか未確認のため、この様に記載しています。

あと、もう一つ作ります。

感想

Cognitoをはじめて使いましたが、凄く便利でした。
Cognitoは自身が管理するユーザーに対して、IAMの権限を割り当てることができるので、ユーザー毎にAWSの機能の制限を設けることができ、それがとても便利そうだと感じました。
今回のハンズオンで、Cognitoへの恐怖心(壁)は無くなりました。これも大きな成果です。
個人的にFirebase Authorizationとの比較もしてみたいですね。

2:LambdaAPIDynamoハンズオン

作品内容

APIGateway+Lambda+DynamoDBを組み合わせたアプリ。
※ハンズオンの資料は公開して良いか未確認のため、この様に記載しています。
1:と比べると少しモダンな作りです。

感想

API GateWayがクセモノ感半端なかったです。ですが、これだけでAPIが1つできてしまうのは、すごいと思いましたし、画期的とも感じました。
Dynamo DBは、以前にLocalSAMで操作した経験があったので、そんなに驚きはしませんでした。
Lambdaも使用経験があったので、理解に困ることはなかったです。
実はこの3つは、すべてLocalSAMのDockerで動かすことができるので、もしかすると同じことがLocalでできるかもしれません。 (今度試してみよう!)

全体を通しての感想

ハンズオンのボリュームが多くて、時間以内に終わらなかった方が多くいました。
ハンズオンって「グループで詰まるところをわいわいと相談しながら解決していく」イメージを持っていたのですが、今回のハンズオンは1人でもくもく作業をしていたので、少し意外でした。(まぁボリュームと作業時間を考えれば、1人もくもくになるのも仕方ない?)

でも、楽しかったですし、今回のハンズオンに参加して1番大きかったのはやっぱり壁を壊せたことです。
Cognite、API GatewayDynamo DBは、個人でもまた使ってみようと思います!

DynamoDBについて調べてみたことを書いてみた その3

前回書いた記事「DynamoDBについて調べてみたことを書いてみた その1、その2」の続きの話です。

kodak.hatenablog.com

kodak.hatenablog.com

今回は、いよいよLSIとGSIについての話です。

LSIとGSIとは?

LSIとGSIは、それぞれの正式名称は以下になります。

そう、インデックスなんです!!
ですが、ただのインデックスではありません。これらはインデックスですが、テーブルを複製しています。
何を言っているかわからないと思いますが、LSIから見ていきます。

LSI(ローカル・セカンダリ・インデックス)

DynamoDBの一意となるKey(プライマリーキー)は、Hash、HashとRangeの組み合わせの2つから作ります。
しかし、それでは使いづらい・・・
何故ならテーブル内に、他にも一意になりそうなKey項目や検索条件で使用したい項目があっても、今のままでは指定する事ができないからです。そんなときに使うのがGSIとLSIです。

LSIは、テーブルに対して最初に指定したHashとRangeの組み合わせに対して、もう1つ別のRangeを設定できます。 (ただし、テーブル作成時にHashとRangeの両方を定義した場合に限ります。)
つまり、検索条件やソートしたい項目を追加で1つ選ぶ事ができるのがLSIの魅力です。

実際にテーブルを作成するコマンドを見てみましょう。

aws dynamodb create-table \
    --table-name Music2 \
    --attribute-definitions \
        AttributeName=Artist,AttributeType=S \
        AttributeName=SongTitle,AttributeType=S \
        AttributeName=SongWriter,AttributeType=S \
    --key-schema \
        AttributeName=Artist,KeyType=HASH \
        AttributeName=SongTitle,KeyType=RANGE \
    --provisioned-throughput \
        ReadCapacityUnits=1,WriteCapacityUnits=1 \
    --local-secondary-indexes \
        "IndexName=Music-Lsi,
            KeySchema=[
                {AttributeName=Artist,KeyType=HASH},
                {AttributeName=SongWriter,KeyType=RANGE}
            ],Projection={ProjectionType=KEYS_ONLY}"
  • --local-secondary-indexes : LSIを定義する。
  • IndexName : LSIの名前を定義する。
  • KeySchema-[{AttributeName=XXX,KeyType=HASH}] : LSIのHashを定義する。--key-schemaのHASHと同じになる。
  • KeySchema-[{AttributeName=XXX,KeyType=RANGE}] : LSIのHashに対して、新しいRangeを指定する。
  • Projection : インデックスに射影される属性を指定する。

ちなみに、LSIのHashを既存テーブルのHash以外を選ぶと以下のように怒られます。

An error occurred (ValidationException) when calling the CreateTable operation: Local Secondary indices must have the same hash key as the main table

上記で「ただのインデックスではありません。これらはインデックスですが、テーブルを複製します。」と言いました。
LSIは、ProjectionTypeで指定した項目+既存テーブルのHashとRangeを持つ別テーブルを作成します。
それらのテーブルのデータは、既存テーブルのHash毎に格納されます。
図で書くとこんな感じです。

f:id:special-moucom:20190331220857p:plain

なんかインデックスっぽくないですよね(= =;;)

GSI(グローバル・セカンダリ・インデックス)

LSIはHashは変えずに、新しいRangeを追加していました。
HashもRangeも既存テーブルとは異なる項目を設定したい!そんなときに使うのが、GSIです。

テーブルを作成するコマンドを見てみましょう。

aws dynamodb create-table \
    --endpoint-url http://localhost:8000 \
    --table-name Music4 \
    --attribute-definitions \
        AttributeName=Artist,AttributeType=S \
        AttributeName=SongTitle,AttributeType=S \
        AttributeName=Label,AttributeType=S \
        AttributeName=SongWriter,AttributeType=S \
    --key-schema \
        AttributeName=Artist,KeyType=HASH \
        AttributeName=SongTitle,KeyType=RANGE \
    --provisioned-throughput \
        ReadCapacityUnits=1,WriteCapacityUnits=1 \
    --global-secondary-indexes \
        "IndexName=Music-Gsi,
            KeySchema=[
                {AttributeName=Label,KeyType=HASH},
                {AttributeName=SongWriter,KeyType=RANGE}
            ],
            Projection={ProjectionType=KEYS_ONLY},
            ProvisionedThroughput={ ReadCapacityUnits=1, WriteCapacityUnits=1 }"
  • --global-secondary-indexes : GSIを定義する。
  • IndexName : GSIの名前を定義する。
  • KeySchema-[{AttributeName=XXX,KeyType=HASH}] : GSIのHashを定義する。--key-schemaのHASHと異なる項目を設定する。
  • KeySchema-[{AttributeName=XXX,KeyType=RANGE}] : GSIのHashに対して、Rangeを指定する。
  • Projection : インデックスに射影される属性を指定する。
  • ProvisionedThroughput : 指定したテーブルのスループットを指定します。(こちらの説明はまた今度)

LSIと書き方はほぼ同じです。とはいえ、GSIで指定するHASHに制限はありません。

LSI同様、インデックスでありながらGSIもテーブルを複製しています。しかし、恐らくデータの格納方法はLSIとの違うように思えます。
LSIは、既存のテーブルのHash毎にデータを格納していましたが、GSIは完全に別テーブル、別Hashを使っているように見えるからです。
図で書くとこんな感じです。

f:id:special-moucom:20190331220934p:plain

GSIで指定するHashですが、これがどこまで一意を気にしないといけないのか・・・私もわかっていません。
ですが、あくまでインデックスなので、既存テーブルのHashを何かしらうまいこと使ってくれているようにも思えます。 ここら辺はもう少し調査が必要ですね。(= =;;)

LSIとGSIの使いどころ

LSIとGSIの使いどころですが、単純にRange=SortKeyと考えると別の項目でソートしたい場合、既存のテーブルに対して、色々な角度からテーブルを参照したい場合(例えば、テーブルに曲名とジャンルの項目がある場合、曲名からテーブルを見たり、ジャンルからテーブルを見たり等)に使えそうです。
調べてみると、LSIよりもGSIの方が使い勝手が良く、GSIをいかに上手く使うのかがDynamoDBのポイントだそうです。

最後に・・・

DynamoDBについて、個人的に調べていた事を3回に渡って書いてみました。
私自身、DynamoDBの使用経験はありません。(少しコマンドで叩いて使ってみた程度です。)
そんな状態で調べた事をずらずらと書いたので、間違った情報もきっと幾つかあると思います。

とはいえ、ブログに書く事で色々と学びになりましたし、整理もできました。結果的にやってよかったと思っています。

現在、LambdaからDBを扱うならDynamoDB一択ですが、もう一つDocumentDB(MongoDB互換)というのがあります。

aws.amazon.com

こちらは東京リージョンにはまだ来ていないのですが、噂ではこちらもLambdaとの相性が良く、DynamoDBの代わりになるDBになる様です。
こちらも東京リージョンにきた時には色々と調べてみたいなぁと思います。