- 投稿日:2020-09-22T22:34:44+09:00
next.jsをGAEにデプロイ
概要
Google App Engine にnext.jsで実装したFrontendサービスをデプロイするための手順とポイントについてまとめます。
また、gcloud SDKはローカル環境で利用可能な状態であることを想定します。package.json
最初にpackage.json内に記載のscriptをGCPにデプロイするために編集します。(これは必須ではありませんが開発効率向上のため設定)
package.json{ "scripts": { "dev": "next", "build": "rm -rf ./build && NODE_ENV=production next build", "start": "next start", "deploy": "npm run build && gcloud app deploy", "export": "next export" }, "dependencies":{ '''''''''''''略''''''''''''''''''' } }デプロイのために書き加えた部分はbuildとdeployです。
まずbuildの部分ですが、nextの場合buildすると通常は.nextディレクトリ下にビルドされますが、GAEはこれを認識できないのでbuildフォルダを作成してその中にビルドファイルを入れるようにします。
次にdeployではnom run deploy
を実行してそのまままGAEへのデプロイも行うようにしてあります。また、たまにstartのスクリプトを
next start -p 8080
としている記事を目にしますが、この記述はパフォーマンスの面で良くありません(スケーリング時、不必要に何度も実行されてしまう。)ので、次のapp.yamlの中のentrypointで記述するようにします。app.yaml
app.yamlservice: front runtime: nodejs12 entrypoint: next start -p $PORT handlers: - url: .* script: auto今回はfrontendとbackendを分けているのでserviceにforntと記述しておくことでGAE内でのserviceとして独立させています。
entrypointの部分でnext start -p $PORT
と記述することでアプリ起動時のコードを設定できます。
アプリの起動時に entrypoint コマンドを実行することで、デフォルトの起動動作をオーバーライドすることができ、今回の場合だと環境変数PORTにポートを開いてアプリをスタートすることができます。
entorypointの記述が誤っているとHTTP500エラーが出てしまう場合があるので、entrypointに正しく記述をすることで修正できます。あとはpackage.jsonで設定した
npm run deploy
を実行してあげることで自動的にGAEにデプロイされます。終わり
今回の例は自分の実行環境下での1例であり、あくまで参考程度にしていただけたらと思います。
間違っている部分等があればご指摘よろしくお願いします。
- 投稿日:2020-09-22T21:12:08+09:00
初心者によるVue.jsハンズオン
はじめに
初めて記事を書くので暖かく見守って下さい。
仕事でVue.jsを使用するということで、JS初心者が手っ取り早くVue.jsに触れてみる。早速触ってみる
用意するものは使い慣れたブラウザとエディターがあれば十分です。
私は今回以下のものを用いて開発を行います。
-ブラウザ:Google Chrome
- エディター:Visual Studio Codeまずはベースページの作成
index.html<html> <body> Hello, World <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </body> </html>こちらはvue.js本体を読み込むだけの空ページの作成を行っています。
今回のハンズオンでは、vue.jsのCDNを使っていきます。こちらのコードをVSCodeで書いたら保存しましょう。
そしてGoogle Chromeにドラッグ&ドロップすることで表示できます、便利。(今まで右クリックしてブラウザに表示していたので、、、、)表示されたら、まずブラウザに「Hello, World」と表示されるか確認しましょう。
そして、動作としてvue.jsがリクエストされているか確認するためにChromeの開発者ツールを使いましょう。
確認方法は開発者ツール(windowsの場合は「F12」キー)->Networkです。
以下の画像のようになっていれば、大丈夫です!
それではVue.jsを実行/展開
index.html<html> <body> <div id="app"></div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> new Vue({ el: '#app', template: '<button></button>', }); </script> </body> </html>こちらのコードでは、Vue.jsの展開ポイントとなるdivを作成し、newで展開しています。
先ほどのブラウザをリロードしてボタンが表示されたら成功です!解説
1. 展開ポイントは<p>でもでもなんでもいいですが慣例的に<div>を使います。
2. idもなんでもいいですが同じく慣例的にappと名付けます。ボタンに振る舞いを追加し。紐づける
index.html<html> <body> <div id="app"> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> new Vue({ el: '#app', template: '<button @click="showMessage"></button>', methods: { showMessage() { alert('ok'); }, }, }); </script> </body> </html>先ほどのコードに、ユーザーがボタンをクリックしてアラートを表示させるコードを追加します。
実際にクリックしたらメッセージを表示されたら成功です。解説
1. Vue.jsの挙動は基本的にmethodsの中に定義していきます
2. Vue.jsでは生のhtmlのonclick="..."と同じようにhtmlタグにリスナ/ハンドラを記述します状態を保持する
index.html<html> <body> <div id="app"> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> new Vue({ el: '#app', template: '<button @click="toggle"></button>', data: function () { return { active: false, }; }, methods: { toggle() { this.active = !this.active; }, showMessage() { alert('ok'); }, }, }); </script> </body> </html>こちらのコードでは、中でステートを保持して、クリック時にそのステートを変更するようにします。
今回は内部の状態変更のみなので確認できないです。解説
1. 状態、ステートは基本的にdata配下に定義していきます
2. elやtemplate、methodsと違い、dataの値には無名関数を利用して、状態一覧のオブジェクトを返すようにします
3. テンプレート中でdataの値を参照する場合はthisは付けない、methodsの中で参照する場合はthisを付ける、ので注意テンプレートに値を埋め込む
index.html<html> <body> <div id="app"> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> new Vue({ el: '#app', template: '<button @click="toggle">{{ active }}</button>', data: function () { return { active: false, }; }, methods: { toggle() { this.active = !this.active; }, showMessage() { alert('ok'); }, }, }); </script> </body> </html>こちらのコードでは、テンプレートに{{ }}で値を埋め込んでいて、クリック時の状態反転に連動してボタンのラベルが変わったら成功です。
解説
1. {{ ステートの変数名 }} でテンプレート中に変数を埋め込むことができます
2. Vueはリアクティブなため、変数の値が変わったら(テンプレートを書き直さなくても)自動的に反映されます{{ }}の中に簡単なjs処理を記述する
index.html<html> <body> <div id="app"> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> new Vue({ el: '#app', template: '<button @click="toggle">{{ active ? "Like済み" : "Likeする" }}</button>', data: function () { return { active: false, }; }, methods: { toggle() { this.active = !this.active; }, showMessage() { alert('ok'); }, }, }); </script> </body> </html>ここはactive変数そのものだとBooleanがStringにキャストされてtrue/falseと表示されるのでボタンのラベルっぽい表示にしてます。
解説
1.{{ }} の中ではjsコードそのものを記述できます
(ただし過度な記述をすると可読性が下がるので注意)コンポーネント化する
index.html<html> <body> <div id="app"> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> Vue.component('like-button', { template: '<button @click="toggle">{{ active ? "Like済み" : "Likeする" }}</button>', data: function () { return { active: false, }; }, methods: { toggle() { this.active = !this.active; }, showMessage() { alert('ok'); }, }, }); new Vue({ el: '#app', template: '<div><like-button />aaa<br />bbb<br />ccc<br /><like-button /></div>', }); </script> </body> </html>最後に、いままで書いてきた一連のテンプレートと振る舞い、状態をセットとしてコンポーネント化します。
解説
1.コンポーネントとして切り出すことで再利用できるようにします最後に
今回はここで終了です!
次回も続きを書ければいいなと思います。
最後までご覧いただきありがとうございました!
- 投稿日:2020-09-22T19:43:37+09:00
Firestoreのエラー "Converting circular structure to JSON Firebase"
エラーの原因
document内で, referenceタイプのフィールドを使っている場合に発生します.
筆者のアプローチ
referenceタイプのフィールドが他のドキュメントを指すことで再帰的になっている為, そのドキュメントのパスを文字列型に変換してあげればいいと思います.
他の方法があればご教授いただきたいです.?♂️具体的な方法
下記は取得したいドキュメントの
DocumentReference
を引数として与えてあげるとそのドキュメントをオブジェクトとして返してあげる関数です.const resolveCirculations = (documentRef) => { const data = documentRef.data(); const circlerRefResolved = Object.keys(data).reduce((ret, key) => { ret[key] = has(data[key], 'path') ? data[key].path : data[key]; return ret; }, {}); return { ...circlerRefResolved, id: documentRef.id }; };ドキュメントの一件取得は下記のように使ってあげます.
const postRef = await db.collection('posts').doc(id).get(); const post = resolveCirculations(postRef);ドキュメントを複数(コレクションで)取得したい場合は以下で,
CollectionReference
用に関数を用意してあげます.先程のresolveCirculations
を使います.const getDocs = (collectionRef) => collectionRef.docs.map(resolveCirculations);上記を使うと, 複数件は以下で取得できます.
const usersRef = await db.collection('users').get(); const users = getDocs(usersRef);以上になります.
- 投稿日:2020-09-22T18:06:49+09:00
Express+PostgreSQL+SequelizeをDockerで構築してみる【後編】
前回
https://qiita.com/rockguitar67/items/b644a63b3e39cc1fcaea
はい続きです。
③DB周りの設定
コンテナ内のExpress環境においてDB設定がなされているファイルがあります。
ここでdocker-compose.ymlで設定したDBへのアクセス情報が明記されています。db_client.jsmodule.exports = { pg_client: function () { const { Client } = require('pg') const client = new Client({ user: process.env.DB_USER, host: process.env.DB_HOST, port: process.env.DB_PORT, database: process.env.DB_NAME }) return client }, };一応現状でもDBにアクセス自体はできると思います。
routes内のindex.jsを見てみましょう!'URL/'にGETでアクセスした時にどういった挙動をするかを書きます。
先ほどのsb_client.jsを読み取って、DBとの接続を可能にしています。routes/index.jsrouter.get('/', function (req, res, next) { const client = require("../db_client").pg_client() client.connect() .then(() => console.log("success!!")) .then(() => client.query("select * from chat order by timestamp desc")) .then(function (results) { console.table(results.rows) res.render('index', { result: results.rows }) })しかし6行目を見ていただくと、DB処理をSQL文で呼び出しています。
これでもSQLの勉強としては良いのですが、複数のテーブルを用意して、それらを繋ぎ合わせるリレーションなどの処理を行う際はもっと楽なORMを活用します。④ORMの活用
オブジェクト関係マッピング(英: Object-relational mapping、O/RM、ORM)とは、データベースとオブジェクト指向プログラミング言語の間の非互換なデータを変換するプログラミング技法である。 wikipedia参照
Node.jsを使用する際にORMとしてSequelizeを活用します。
Terminal# npm install -g sequelize sequelize-cli # sequelize-cli initこの時点で作業ディレクトリにconfig.jsonやmodelディレクトリが生成されます。
config
./config/config.json{ "development": { "username": "postgres", "password": null, "database": "mydatabase", "host": "database", "dialect": "postgres", "operatorsAliases": false },config.jsonでhostに127.0.0.1を指定するのが一般的だそうですが、Dockerの場合だとmigrationの時点で、DBにアクセスできないよ、とエラーを吐くことがあります。その為docker-compose.ymlで設定したDB_HOST(=database)をそのまま活用します。
とりあえず作成するテーブルとカラム情報を決めて、DBを作成します!
Terminal# sequelize-cli model:generate --name user --attributes firstName:string,lastName:string,email:string # sequelize-cli db:migratemigration
migrationが完了すると、DBのカラム情報がmigrationフォルダ内に表示されます。
./migration/日付-create-user.jsmodule.exports = { up: async (queryInterface, Sequelize) => { await queryInterface.createTable('users', { id: { allowNull: false, autoIncrement: true, primaryKey: true, type: Sequelize.INTEGER }, firstName: { type: Sequelize.STRING }, lastName: { type: Sequelize.STRING }, email: { type: Sequelize.STRING }, createdAt: { allowNull: false, type: Sequelize.DATE }, updatedAt: { allowNull: false, type: Sequelize.DATE } }); }, down: async (queryInterface, Sequelize) => { await queryInterface.dropTable('users'); } };model
migrationが完了すると、Modelに入力したDBのカラム情報が表示されます。
これを確認できたら一応完了です。./model/user.jsuser.init({ firstName: DataTypes.STRING, lastName: DataTypes.STRING, email: DataTypes.STRING }, { sequelize, modelName: 'user', });model/index.jsや今回作成したmodel/user.jsはリレーション設定やアソシエーションなどで編集しますが、この時点ではノータッチです。
ユーザー登録→ユーザー表示の一連の流れ
簡単にユーザー登録アプリでも作ります。
https://github.com/LostGeneration1999/Express_PostgreSQL_ORM
フォームからユーザー:藤川球児を入力して表示させました。
#参考記事
https://qiita.com/markusveeyola/items/64875c9507d5fa32884e
PostgreSQLへのアクセス
https://qiita.com/yusuke-ka/items/448843020c0406363ba5ORM関連
https://qiita.com/izszzz/items/31d448c501d24d31c846ORM関連
https://qiita.com/KoKeCross/items/144949ba03e5138fc6d5
- 投稿日:2020-09-22T18:06:20+09:00
Express+PostgreSQL+SequelizeをDockerで構築してみる【前編】
前説
将来的にはフロントエンドはVue、バックエンドはExpressのような構成を目指します。
まずはバックエンドでExpress+PostgreSQL+Sequelizeでの環境構築です!①Dockerでコンテナ動作まで
初期ファイル構成
-Dockerfile -docker-compose.yml -app |-- package.jsonDockerfile
Dockefile# イメージ指定 FROM node:latest # 環境変数設定 ENV NODE_ENV="development" # 作業ディレクトリ作成&設定 WORKDIR /src COPY ./app /src RUN npm installdocker-compose.yml
ここでNode.jsのExpress(サーバーサイド)コンテナの構造とDBコンテナの構造を設計します。
- ポート番号は5432が一般的です。
- Volumesではコンテナを破棄しても、データを永続化できます。その為データをコンテナ外に保存します。
docker-compose.ymlversion: '3' services: app: # Dockerfile保存場所 build: context: ./ depends_on: - database image: n-app #volumes設定 volumes: - "./app:/src" # コンテナ名 container_name: n-app # ポート接続 ports: - 3000:5000 environment: PORT: 5000 DB_USER: postgres DB_HOST: database DB_PORT: 5432 DB_NAME: mydatabase tty: true database: image: postgres:12.3 volumes: - ./init-sql:/docker-entrypoint-initdb.d:ro environment: POSTGRES_DB: mydatabase TZ: "Asia/Tokyo" POSTGRES_HOST_AUTH_METHOD: trustpackage.json
自分用です。
Volumesを設定することにより、コンテナ上でInstallしたパッケージもホスト上・コンテナ上にも保存されます。コンテナを破棄した場合もVolumesを設定することにより、package.jsonをもとに'npm install'で破棄したコンテナの環境を復元できます。
- scripts内では"node 〇〇"のコマンドの'〇〇'の部分を設定します。 現状./bin/wwwはExpressの雛形ファイルを生成しないと作成されませんので、注意が必要です。
app/package.json{ "name": "myapp", "version": "0.0.0", "private": true, "scripts": { "start": "node ./bin/www", }, "dependencies": { "cookie-parser": "~1.4.3", "debug": "~2.6.9", "ejs": "~2.5.7", "express": "~4.16.0", "http-errors": "^1.6.3", "morgan": "~1.9.0", "nodemon": "^2.0.4", "pg": "*", "sequelize": "^6.3.5", "sequelize-cli": "^6.2.0" } }自分は以下のサイトを参考にしました。
以下のサイトの方のgithubからcloneして、必要な情報を追加しました。
あらかじめExpressのテンプレートが整っていたので、大変便利でした。https://qiita.com/tamoco/items/caffca436546a1a5fcc8
ではDockerを使ってコンテナを動かしましょう。
Terminal$ docker-compose build Building app Step 1/5 : FROM node:10.12 ---> a2b9536415c2 Step 2/5 : ENV NODE_ENV="development" ---> Using cache ---> 40f981aef1ce Step 3/5 : WORKDIR /src ---> Using cache ---> ec233d742a63 Step 4/5 : COPY ./app /src ---> Using cache ---> 88f269307e53 Step 5/5 : RUN npm install ---> Using cache ---> b22a8c36f08e $ docker-compose up -d $ docker-compose ps Name Command State Ports ---------------------------------------------------------------------------------- n-app node Up 0.0.0.0:3000->5000/tcp ta-app_database_1 docker-entrypoint.sh postgres Up 5432/tcp $ docker exec -it n-app bash root@f195575a066f:/src#上記のところで構築したコンテナに入ることができました。
②Express環境構築
ExpressとはNode.js上で動作するWebアプリのMVC型フレームワークです。
自分の場合は偉大なる先人様のリポジトリからCloneしたため、あらかじめExpressの雛形が揃っていました。Expressの雛形を生成したい場合はこんな感じでしょうか。
# npm install express-generator -g 1. viewをpugファイルで作成したい場合(Default) # express --view=pug myapp viewをejsファイルで作成したい場合 2. express -e myapp次回
https://qiita.com/rockguitar67/items/0020d734201632077cb5
参考文献
Docker-Expressの構築
https://qiita.com/tamoco/items/caffca436546a1a5fcc8https://gitlab.com/tamoco-mocomoco/study-docker-compose/-/tree/master/app/routes
https://qiita.com/yoshiplum/items/129e7ad1ffc3a02b9eb2
Docker-Volumesについて
https://qiita.com/gounx2/items/23b0dc8b8b95cc629f32
- 投稿日:2020-09-22T12:00:56+09:00
Next.jsにjestとenzymeを導入(next/babel使用)
Next.jsにjestとenzymeを導入(next/babel使用)
以前、Next.jsにjestとenzymeを導入するという記事を書きました。
上記の手順でjestの実行はできたのですが、yarn devでアプリ起動するとなにやらbabelに関するエラーが。。
どうやらNext.js起動すると追加したbabelの設定ファイルの方が読み込まれて、babelのエラーが出てしまっているよう。
そこで、jestで使うbabelをnext.jsのbabelに変更したところ、よりすっきりした設定になったのでメモ。
next.jsのbabelが使える
next.jsにはデフォルトでbabelが入っており、これがjsxのトランスパイルなどjestにも適用できることが分かりました。
こちらの方がスッキリとした手順・設定で構築できます。jestインストール
$ npm install --save-dev jestjest設定ファイルを生成
$ jest --initcommand not found: jest の場合
以下を実行します。
./node_modules/.bin/jest --initEnzymeインストール
yarn add --dev enzyme enzyme-adapter-react-16Enzymeの利用時は一度だけ
Enzyme.configure()
を呼ぶ必要があるため、下記のスクリプトを追加。jest.setup.jsimport Enzyme from "enzyme"; import Adapter from "enzyme-adapter-react-16"; Enzyme.configure({ adapter: new Adapter() });Jestのテスト前に実行されるようにする。
jest.config.jsmodule.exports = { // ... setupFiles: ['./jest.setup.js'], // ... }babel.config.jsを設定
module.exports = { "presets": ["next/babel"], };
テストファイルのignore
Cypressを導入しており、jest実行でcypressのspecも読まれてしまうので、ignore設定をしました。
jest.config.js... testPathIgnorePatterns: [ "/node_modules/", "/cypress/" ], ...