20200922のNode.jsに関する記事は6件です。

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.yaml
service: 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例であり、あくまで参考程度にしていただけたらと思います。
間違っている部分等があればご指摘よろしくお願いします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初心者による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です。
以下の画像のようになっていれば、大丈夫です!
image.png

それでは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.コンポーネントとして切り出すことで再利用できるようにします

最後に

今回はここで終了です!
次回も続きを書ければいいなと思います。
最後までご覧いただきありがとうございました!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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);

以上になります.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Express+PostgreSQL+SequelizeをDockerで構築してみる【後編】

前回

https://qiita.com/rockguitar67/items/b644a63b3e39cc1fcaea

はい続きです。

③DB周りの設定

コンテナ内のExpress環境においてDB設定がなされているファイルがあります。
ここでdocker-compose.ymlで設定したDBへのアクセス情報が明記されています。

db_client.js
module.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.js
router.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:migrate

migration

migrationが完了すると、DBのカラム情報がmigrationフォルダ内に表示されます。

./migration/日付-create-user.js
module.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.js
user.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

フォームからユーザー:藤川球児を入力して表示させました。

スクリーンショット 2020-09-22 18.04.27.png

スクリーンショット 2020-09-22 18.04.41.png

#参考記事

https://qiita.com/markusveeyola/items/64875c9507d5fa32884e

PostgreSQLへのアクセス
https://qiita.com/yusuke-ka/items/448843020c0406363ba5

ORM関連
https://qiita.com/izszzz/items/31d448c501d24d31c846

ORM関連
https://qiita.com/KoKeCross/items/144949ba03e5138fc6d5

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Express+PostgreSQL+SequelizeをDockerで構築してみる【前編】

前説

将来的にはフロントエンドはVue、バックエンドはExpressのような構成を目指します。
まずはバックエンドでExpress+PostgreSQL+Sequelizeでの環境構築です!

①Dockerでコンテナ動作まで

初期ファイル構成

-Dockerfile
-docker-compose.yml
-app
|-- package.json

Dockerfile

Dockefile
# イメージ指定
FROM node:latest

# 環境変数設定
ENV NODE_ENV="development"

# 作業ディレクトリ作成&設定
WORKDIR /src

COPY ./app /src

RUN npm install

docker-compose.yml

ここでNode.jsのExpress(サーバーサイド)コンテナの構造とDBコンテナの構造を設計します。

  • ポート番号は5432が一般的です。
  • Volumesではコンテナを破棄しても、データを永続化できます。その為データをコンテナ外に保存します。
docker-compose.yml
version: '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: trust

package.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/caffca436546a1a5fcc8

https://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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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 jest 

jest設定ファイルを生成

$ jest --init
command not found: jest の場合

以下を実行します。

./node_modules/.bin/jest --init   

Enzymeインストール

yarn add --dev enzyme enzyme-adapter-react-16

Enzymeの利用時は一度だけEnzyme.configure()を呼ぶ必要があるため、下記のスクリプトを追加。

jest.setup.js
import Enzyme from "enzyme";
import Adapter from "enzyme-adapter-react-16";

Enzyme.configure({ adapter: new Adapter() });

Jestのテスト前に実行されるようにする。

jest.config.js
module.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/"
],
...
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む