- 投稿日:2021-06-23T22:54:02+09:00
【バックもフロントもNodeを使う人向け】monorepoでコマンド一発でインストールを完了させる
TL;DR npmやyarnの標準機能のみを使って、「ローカルで、コマンド一発で複数ディレクトリのパッケージインストール」を実現する。また、CIを実行しやすいように、それぞれのディレクトリにlockファイルができるようにする。 はじめに monorepoは複数のプロジェクトを一つのリポジトリで管理する手法です。例えば、Web開発ではフロントエンドとバックエンドを以下のように、同じリポジトリ内で管理することがあります。 モノレポのディレクトリ web/ server/ monorepoのメリットとしては、 - フロントとバックエンドでコードを共有しやすい(とくにTypeScriptの型定義) - issueやPull requestがひとつのリポジトリに集まるため管理しやすい などがあります。 やりたいこと コマンド一発で、web/とserver/のyarn install(もしくはnpm install or npm ci)を完了させたい。あわよくば、コマンド一発でフロントとバックエンドを両方起動したい。 web/とserver/それぞれでlockファイルを持ちたい(「CIでweb側だけpackageをインストールをしてテストを走らせる」といったことがやりたいため) 解決方法 1. Docker Composeの利用 複数のコンテナを良い感じに扱える技術。環境による差異もなくせて便利です。ただし、Docker環境で開発するのは普通のローカル環境よりも実行速度が遅い(特にMacで顕著)のが難点です。チームメンバー全員がWindows or LinuxならばDockerでも良いのですが、Macで開発している人が多い場合は、「DBはDockerで、アプリケーションはローカルの環境で」という構成にしたいです。 2. Workspace機能を使う npm v7やyarnのworkspaceを使うことで、ルートディレクトリでインストールコマンドを走らせるだけで良くなります。 workspaceの設定例(package.json) "workspaces": [ "web", "server" ] しかし、ルートディレクトリに1つのlockファイルがまとまってできてしまう問題が発生します。 webディレクトリでyarn installをすると、ルートのlockファイルは無視されてしまいます。かといってルートディレクトリでインストールすると、「フロント側のテストを走らせたいだけなのに、バックエンドのpackageもインストールされてしまう」という事態を招きます。 3. ルートディレクトリのnpm scriptsで解決(結論) yarn --cwd <ディレクトリ> <コマンド>とすることで指定したディレクトリでコマンドを実行することができます(参考)。 例えば、yarn --cwd web installとすると、webディレクトリでyarn installが実行されます。 npmの場合はnpm --prefix <ディレクトリ> <コマンド>です。 この機能を使って、以下のようにルートディレクトリのpackage.jsonを設定します。 package.json(yarnの場合) "scripts": { "all:install": "yarn install && yarn --cwd web install && yarn --cwd server install", "all:dev": "run-p dev:*", "dev:web": "yarn --cwd dev", "dev:server": "yarn --cwd server" }, "devDependencies": { "npm-run-all": "^4.1.5" } run-pはコマンドを並列実行します。 これにより、yarn all:installをするだけで、ルート・フロント・バックのパッケージインストールが終わります。また、yarn all:devとすると、フロントとバック両方のdevサーバーが起動します。 最後に 他に良い方法がございましたらご教授いただけますと幸いです。
- 投稿日:2021-06-23T22:54:02+09:00
【バックもフロントもNodeを使う人向け】monorepoで、コマンド一発でインストールを完了させる色々な方法
TL;DR npmやyarnの標準機能のみを使って、「ローカルで、コマンド一発で複数ディレクトリのパッケージインストール」を実現する。また、CIを実行しやすいように、それぞれのディレクトリにlockファイルができるようにする。 はじめに monorepoは複数のプロジェクトを一つのリポジトリで管理する手法です。例えば、Web開発ではフロントエンドとバックエンドを以下のように、同じリポジトリ内で管理することがあります。 モノレポのディレクトリ web/ server/ monorepoのメリットとしては、 - フロントとバックエンドでコードを共有しやすい(とくにTypeScriptの型定義) - issueやPull requestがひとつのリポジトリに集まるため管理しやすい などがあります。 やりたいこと コマンド一発で、web/とserver/のyarn install(もしくはnpm install or npm ci)を完了させたい。あわよくば、コマンド一発でフロントとバックエンドを両方起動したい。 web/とserver/それぞれでlockファイルを持ちたい(「CIでweb側だけpackageをインストールをしてテストを走らせる」といったことがやりたいため) 解決方法 1. Docker Composeの利用 複数のコンテナを良い感じに扱える技術。環境による差異もなくせて便利です。ただし、Docker環境で開発するのは普通のローカル環境よりも実行速度が遅い(特にMacで顕著)のが難点です。チームメンバー全員がWindows or LinuxならばDockerでも良いのですが、Macで開発している人が多い場合は、「DBはDockerで、アプリケーションはローカルの環境で」という構成にしたいです。 2. Workspace機能を使う npm v7やyarnのworkspaceを使うことで、ルートディレクトリでインストールコマンドを走らせるだけで良くなります。 workspaceの設定例(package.json) "workspaces": [ "web", "server" ] しかし、ルートディレクトリに1つのlockファイルがまとまってできてしまう問題が発生します。 webディレクトリでyarn installをすると、ルートのlockファイルは無視されてしまいます。かといってルートディレクトリでインストールすると、「フロント側のテストを走らせたいだけなのに、バックエンドのpackageもインストールされてしまう」という事態を招きます。 3. ルートディレクトリのnpm scriptsで解決(結論) yarn --cwd <ディレクトリ> <コマンド>とすることで指定したディレクトリでコマンドを実行することができます(参考)。 例えば、yarn --cwd web installとすると、webディレクトリでyarn installが実行されます。 npmの場合はnpm --prefix <ディレクトリ> <コマンド>です。 この機能を使って、以下のようにルートディレクトリのpackage.jsonを設定します。 package.json(yarnの場合) "scripts": { "all:install": "yarn install && yarn --cwd web install && yarn --cwd server install", "all:dev": "run-p dev:*", "dev:web": "yarn --cwd dev", "dev:server": "yarn --cwd server" }, "devDependencies": { "npm-run-all": "^4.1.5" } run-pはコマンドを並列実行します。 これにより、yarn all:installをするだけで、ルート・フロント・バックのパッケージインストールが終わります。また、yarn all:devとすると、フロントとバック両方のdevサーバーが起動します。 最後に 他に良い方法がございましたらご教授いただけますと幸いです。
- 投稿日:2021-06-23T22:22:27+09:00
React + Rails API 環境構築 ①React側の設定 〜ESLintの設定まで
概要 1からSPAアプリケーションの環境を作っていくので、その手順を残す 構築していく途中で調べた内容も別途記事にしてまとめているので良ければ見てください フロントエンド環境構築 1. node.jsをインストール〜Linter&Formatterの設定前まで anyenv1をインストール(env系をマネージするパッケージマネージャー) brew install anyenv echo ‘eval “$(anyenv init -)”’ >> ~/.zshrc Exec $SHELL -l anyenv経由でnodenvをインストールする anyenv install nodenv exec $SHELL -l anyenvとnodenv用のプラグインを入れる nodenvを含めたenv系をまとめてアップデートしてくれるanyenvプラグインのanyenv-update npmインストール時にデフォルトで一緒にインストールしておくパッケージを指定できるnodenvプラグインのnodenv-default-packages2 mkdir -p $(anyenv root)/plugins git clone https://github.com/znz/anyenv-update.git $(anyenv root)/plugins/anyenv-update mkdir -p "$(nodenv root)"/plugins git clone https://github.com/nodenv/nodenv-default-packages.git "$(nodenv root)/plugins/nodenv-default-packages" nodenv rootにdefault-packagesを作成し、中身を以下のように編集する touch $(nodenv root)/default-packages yarn Typescript ts-node typesync node.jsをインストールする anyenv update # インストール可能なリストを更新する(これしないとNode最新版にあげられない) nodenv install -l #インストールできるバージョンを一覧で確認 nodenv install 14.4.0 #バージョンを指定してインストール nodenv global 14.4.0 #端末上でデフォルトで使用するnode.jsのバージョンを指定 nodenv local 14.4.0 #カレントディレクトリ配下で使用するnode.jsのバージョン指定 create-react-appでreactアプリの雛形を作成する npx create-react-app <directry name> —template typescript #reactアプリを始めるためのディレクトリが既にある場合は「.」、まだない場合はディレクトリ名を設定する #—template typescriptでTypeScriptのためのテンプレートを指定する typesync3をインストールする reactのプラグインを入れた後にtypescriptの型情報を一緒にインストールできるようにするプラグイン yarn add type sync tsconig.jsonファイルを編集する tsconfig.json “Jsx”: “react-jsx”, + “baseUrl”: “src”, #絶対パスでimportできるようにする設定 + “downlevelItteration”: true # vscodeで開発するときは上記の設定をした後にコンソールから touchtsconig.json をして設定を読み込ませる 2. Linter&Formatterの設定 TypeScript他パッケージを最新にする yarn upgrade-interactive —latest #対話的にyarnで管理しているライブラリを更新する yarn upgarade typescript@latest #typescriptを最新にする ESLint4をインタラクティブにインストールする $ yarn eslint --init ? How would you like to use ESLint? 》To check syntax, find problems, and enforce code style ? What type of modules does your project use? JavaScript modules (import/export) 》JavaScript modules (import/export) ? Which framework does your project use? 》React ? Does your project use TypeScript? 》Yes ? Where does your code run? 》Browser ? How would you like to define a style for your project? 》Use a popular style guide ? Which style guide do you want to follow? 》Airbnb: https://github.com/airbnb/javascript ? What format do you want your config file to be in? 》JavaScript The config that you've selected requires the following dependencies: eslint-plugin-react@^VERSION @typescript-eslint/eslint-plugin@latest eslint-config-airbnb@latest eslint@^VERSION eslint-plugin-import@^VERSION eslint-plugin-jsx-a11y@^VERSION eslint-plugin-react-hooks@^VERSION @typescript-eslint/parser@latest ? Would you like to install them now with npm? 》No yarnで先ほどの拡張ルールやプラグインをインストールする yarn add -D eslint-plugin-react @typescript-eslint/eslint-plugin \ eslint-config-airbnb eslint-plugin-import eslint-plugin-jsx-a11y \ eslint-plugin-react-hooks @typescript-eslint/parser yarn #package.jsonのscriptsにpreinstallでtypesyncする設定にしていない場合はこれの前にtypesyncする .eslintrc.jsファイルをカスタマイズする extendsに記述するのは各プラグインルールの推奨の共有設定 プラグインは読み込んだだけでは何のルールも適用されない プラグイン開発者がパッケージ内で共有設定を一緒に提供しているので、それをextendsで適用する必要がある 共有設定間でルールが衝突した場合は後の記述が上書きするため、順番には注意する .eslintrc.js extends: [ 'plugin:react/recommended', 'airbnb', + 'airbnb/hooks', + 'plugin:import/errors', + 'plugin:import/warnings', + 'plugin:import/typescript', + 'plugin:@typescript-eslint/recommended', + 'plugin:@typescript-eslint/recommended-requiring-type-checking', ], parserの設定 @typescript-eslint/parserに渡すオプションを定義する .eslintrc.js parser: '@typescript-eslint/parser', parserOptions: { ecmaFeatures: { jsx: true }, ecmaVersion: 12, + project: './tsconfig.eslint.json', sourceType: 'module', + tsconfigRootDir: __dirname, }, parserに読ませるファイルを作成する(tsconfig.jsonはそのまま読ませない) パーサがnpmパッケージのファイルまでパースし、VSCodeと連携させたときのパフォーマンス低下、新規ファイルのパースの失敗を防ぐため 現状ではこうするしかないらしい(今後対応が変わる可能性は大いにある) tsconfig.eslint.json { "extends": "./tsconfig.json", "include": [ "src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx" ], "exclude": [ "node_modules" ] } pluginsの設定 yarn addしたプラグインのルールを適用するための内容を追加する 追加ルールのプラグインはyarn addしただけでは適用されないのでここにルールを追加する必要がある ESLintはデフォルトでは親ディレクトリの設定ファイルまで読み込むようになっているので、それを抑止するためにrootのオプションを追加している .eslintrc.js plugins: [ '@typescript-eslint', + 'import', + 'jsx-a11y', 'react', + 'react-hooks', ], + root: true, rulesの設定 rulesに各種ルールを設定する 各ルールの適用の可否やエラーレベルを設定する 主にextendsで読み込んだ共有設定の値を書き換えるときにここに記述する 各種ルールの説明 1. no-use-before-define, @typescript-eslint/no-use-before-define 定義前の変数の使用を禁じる ESLint と TypeScript ESLint のルール。本来なら上記でやっているような記述は必要なく TypeScript で正常に動作するはずだが、不具合が解消されてはリグレッションが繰り返されており、react-scripts 4.0.1 の環境ではこうしないと import React from 'react' のインポート文でエラーになってしまう 2. lines-between-class-members クラスメンバーの定義の間に空行を入れるかどうかを定義するルール。eslint-config-airbnb で常に空行を入れるように設定されていたのを、ここでは 1 行記述のメンバーのときは空行を入れなくていいようにしている 3. no-void void 演算子の(式としての)使用を禁ずるルール。Effect Hook20 内で非同期処理を記述する際、@typescript-eslint/no-floating-promises ルールに抵触してしまうのを回避するのに void 文を記述する必要があるため、文としての使用のみを許可している 4. padding-line-between-statements 任意の構文の間に区切りの空行を入れるかどうかを定義するルール。ここでは return 文の前に常に空行を入れるよう設定している 5. @typescript-eslint/no-unused-vars 使用していない変数の定義を許さないルール。ここでは変数名を _ にしたときのみ許容するように設定 6. import/extensions * インポートの際のファイル拡張子を記述するかを定義するルール。npm パッケージ以外のファイルについて .js、.jsx、.ts、.tsx のファイルのみ拡張子を省略し、他のファイルは拡張子を記述させるように設定 7. react/jsx-filename-extension * JSX のファイル拡張子を制限するルール。eslint-config-airbnb で .jsx のみに限定されているので、.tsx を追加 8. react/jsx-props-no-spreading JSX でコンポーネントを呼ぶときのprops の記述にスプレッド構文を許さないルール。eslint-config-airbnb にてすべて禁止されているが、 のように個々の props を明記する書き方のみ許容するように設定 9. react/react-in-jsx-scope JSX 記述を使用する場合に react モジュールを React としてインポートすることを強制する。新しい JSX 変換形式を用いる場合はインポートが不要になるためこの設定を無効化 10. react/prop-types * コンポーネントの props に型チェックを行うための propTypes プロパティ21の定義を強制するルール。eslint-config-airbnb で設定されているが、TypeScript の場合は不要なのでファイル拡張子が .tsx の場合に無効化するよう設定を上書き .eslintrc.js rules: { 'no-use-before-define': 'off', '@typescript-eslint/no-use-before-define': [ 'error', ], 'lines-between-class-members': [ 'error', 'always', { exceptAfterSingleLine: true } ], 'no-void': [ 'error', { allowAsStatement: true, }, ], 'padding-line-between-statements': [ 'error', { blankLine: 'always', prev: '*', next: 'return', }, ], '@typescript-eslint/no-unused-vars': [ 'error', { 'vars': 'all', 'args': 'after-used', 'argsIgnorePattern': '_', 'ignoreRestSiblings': false, 'varsIgnorePattern': '_', }, ], 'import/extensions': [ 'error', 'ignorePackages', { js: 'never', jsx: 'never', ts: 'never', tsx: 'never', } ], 'react/jsx-filename-extension': [ 'error', { extensions: ['.jsx', '.tsx'] } ], 'react/jsx-props-no-spreading': [ 'error', { html: 'enforce', custom: 'enforce', explicitSpread: 'ignore', }, ], 'react/react-in-jsx-scope': 'off', }, overrides: [ { 'files': ['*.tsx'], 'rules': { 'react/prop-types': 'off', }, }, ], settings: { 'import/resolver': { node: { paths: ['src'], }, }, }, }; .eslintignoreファイルを作成する ESLintチェックの対象外にするための設定ファイル 関係ないコードまでlintが走って不要な場面でエラーが出るのを防ぐため build/ Public/ **/coverage/ **/node_modules/ **/*.min.js *.config.js .*lintrc.js setings.jsonに追加で設定をする ファイル保存時にVSCode内蔵のものではなく、ESLintの自動整形が走るようにする 最後の行はプロジェクトにTypeScriptがインストールsれている場合はどっちを使うか尋ねさせるための設定 settings.json "editor.codeActionsOnSave": { "source.fixAll.eslint": true }, "editor.formatOnSave": false, "eslint.packageManager": "yarn", "typescript.enablePromptUseWorkspaceTsdk": true, ...React + Rails API 環境構築 ②React側の設定 〜Prittier,stylelintの設定 に続く リンク集 anyenvまとめ ↩ nodenv-default-packagesまとめ ↩ typesyncまとめ ↩ ESLintまとめ ↩
- 投稿日:2021-06-23T21:15:01+09:00
nodenv-default-packages まとめ
概要 nodenv-default-packagesというプラグインの存在を知ったので調べてまとめる 参考資料:nodenvでいつも使うパッケージを自動でインストールする nodenv-default-packagesとは nodenvのプラグイン nodeインストール時に自動的に指定したパッケージをインストールすることができる デフォルトでインストールするものを固定しておくことでインストール忘れを防げる 使用方法 1. インストール mkdir -p "$(nodenv root)/plugins" git clone https://github.com/nodenv/nodenv-default-packages.git "$(nodenv root)/plugins/nodenv-default-packages" 2. デフォルトでインストールするパッケージの設定 default-packagesファイルを作成する touch $(nodenv root)/default-packages 作成したファイルにデフォルトでインストールするパッケージを記載する yarn Typescript ts-node typesync まとめ プロジェクト毎に共通してインストールするものをまとめることができるのは便利だと思った まだ使用歴はないがこれから使用していく
- 投稿日:2021-06-23T16:38:51+09:00
ALBのリスナールールを自動で切り替えるLambda関数
SetRulePriorities APIを使用して、ALBのリスナールールを自動で切り替える関数です。 CloudFormationからSNS経由で通知を受け取り、CloudFormationによる更新中はメンテンナンス画面を表示し、更新が完了したらEC2のサイトを表示するというルールの切り替えを行います。 概要 ランタイム:Node 14.x アクセス権限: AWSLambdaBasicExecutionRole ELB v2 SetRulePriorities(カスタムポリシーで選択する) 環境変数 LogicalResourceId : 更新するCloudFormationスタック名 ForwardRuleArn : ターゲットグループへの転送ルールのARN MaintenanceRuleArn : メンテナンス用の固定レスポンスを返すルールのARN ソース index.js const AWS = require('aws-sdk'); AWS.config.update({ region: 'ap-northeast-1' }); AWS.config.apiVersions = { elbv2: '2015-12-01', // other service API versions }; const elbv2 = new AWS.ELBv2(); exports.handler = async (event) => { console.log(JSON.stringify(event, null, 2)); const messages = event.Records[0].Sns.Message.split('\n'); //リソースの論理IDを取得 const logicalResourceId = messages .find((message) => { return message.includes('LogicalResourceId'); }) .replace(/LogicalResourceId=|\'/g, ''); console.log(logicalResourceId); if (logicalResourceId !== process.env.LogicalResourceId) { return; } //更新状況を取得 const resourceStatus = messages .find((message) => { return message.includes('ResourceStatus='); }) .replace(/ResourceStatus=|\'/g, ''); console.log(resourceStatus); let params = ''; if (resourceStatus === 'UPDATE_IN_PROGRESS') { params = { RulePriorities: [ /* required */ { Priority: 1, RuleArn: process.env.MaintenanceRuleArn, }, { Priority: 2, RuleArn: process.env.ForwardRuleArn, }, /* more items */ ], }; } else if ( resourceStatus === 'UPDATE_COMPLETE' || resourceStatus === 'UPDATE_ROLLBACK_COMPLETE' ) { params = { RulePriorities: [ /* required */ { Priority: 1, RuleArn: process.env.ForwardRuleArn, }, { Priority: 2, RuleArn: process.env.MaintenanceRuleArn, }, /* more items */ ], }; } else { return; } try { const result = await elbv2.setRulePriorities(params).promise(); console.log(JSON.stringify(result, null, 2)); } catch (err) { console.log(err); } }; eventの中身 CloudFormationからSNS経由で飛んでくる情報はeventに入っていますが、実際には以下のような情報が入っています。 { "Records": [ { "EventSource": "aws:sns", "EventVersion": "1.0", "EventSubscriptionArn": "arn:aws:sns:ap-northeast-1:467964259659:Study_Topic_NotifyCloudFormationEvent:986df782-8a51-45e7-b523-9bd396c5ebf3", "Sns": { "Type": "Notification", "MessageId": "d255cf45-5bc0-5286-af38-97b50ae294b0", "TopicArn": "arn:aws:sns:ap-northeast-1:467964259659:Study_Topic_NotifyCloudFormationEvent", "Subject": "AWS CloudFormation Notification", "Message": "StackId='arn:aws:cloudformation:ap-northeast-1:467964259659:stack/StudyRelease/2fc47fe0-8ab6-11eb-b4b5-0e9ed3f1b3df'\nTimestamp='2021-04-14T02:09:38.195Z'\nEventId='77321280-9cc6-11eb-bae0-0a1871fe28bb'\nLogicalResourceId='StudyRelease'\nNamespace='467964259659'\nPhysicalResourceId='arn:aws:cloudformation:ap-northeast-1:467964259659:stack/StudyRelease/2fc47fe0-8ab6-11eb-b4b5-0e9ed3f1b3df'\nPrincipalId='AROAWZ5GX5VF6AF2NRDU6:tnet_admin_119503'\nResourceProperties='null'\nResourceStatus='UPDATE_ROLLBACK_COMPLETE'\nResourceStatusReason=''\nResourceType='AWS::CloudFormation::Stack'\nStackName='StudyRelease'\nClientRequestToken='Console-ExecuteChangeSet-6ed7afc4-c02a-bac3-48e8-dea1b25a96b9'\n", "Timestamp": "2021-04-14T02:09:38.250Z", "SignatureVersion": "1", "Signature": "JrKCtXJEL6aNAZStjXVRfFYHEHYPM63zVPgAf929WKD+2AlB3YUuMc4ZtFg4ly2DDN+ilOzCcCAs5i+fUaGDF8dkSmQS34L7ikCmJPVVXrabam8yZ8CP9lcn2bbTUCYCb75ec9cdDqE3c0Snp6fCKB/ZgkMMCV7w4TTdMzNAHyldjseWu/1goxq91q6X9dOcSYVoV5GuNrI0kQOfRKJQOguWCYV8Ze7uXFJ5frmUrcD/xb3eRNnzCNjAe3pvTqNPVU6RgLO2fIfTq6Qlo+NSweQZ7niJvrEQ+X+uugnWAoFKz7tqO+mehsifFeS2K/oJDvz9XmOC0EAOWF3rDgukuw==", "SigningCertUrl": "https://sns.ap-northeast-1.amazonaws.com/SimpleNotificationService-010a507c1833636cd94bdb98bd93083a.pem", "UnsubscribeUrl": "https://sns.ap-northeast-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:ap-northeast-1:467964259659:Study_Topic_NotifyCloudFormationEvent:986df782-8a51-45e7-b523-9bd396c5ebf3", "MessageAttributes": {} } } ] } 処理の最初の方で、Messageから必要な情報のみを取得しています。 CloudFormationからの通知内容の判定 CloudFormationからはスタック自体の更新情報の他にもリソースごとの更新情報も飛んできます。 この関数の処理の目的は、 「スタックの更新が開始されたらメンテナンス画面を表示し、スタックの更新が完了したらサイトを表示するようルールの順序を変更する」 ことなので、必要な情報はスタック名とスタックの更新状況の2つです。 まずはスタック名が含まれているかを判定します。 if (logicalResourceId !== process.env.LogicalResourceId) { return; } 続いて更新状況の判定です。 更新中(UPDATE_IN_PROGRESS)ならメンテ画面を表示するルールを1に設定します。 更新完了(UPDATE_COMPLETE)またはロールバック完了(UPDATE_ROLLBACK_COMPLETE)ならサイトを表示するルールを1に設定します。 if (resourceStatus === 'UPDATE_IN_PROGRESS') { params = { RulePriorities: [ /* required */ { Priority: 1, RuleArn: process.env.MaintenanceRuleArn, }, { Priority: 2, RuleArn: process.env.ForwardRuleArn, }, /* more items */ ], }; } else if ( resourceStatus === 'UPDATE_COMPLETE' || resourceStatus === 'UPDATE_ROLLBACK_COMPLETE' ) { params = { RulePriorities: [ /* required */ { Priority: 1, RuleArn: process.env.ForwardRuleArn, }, { Priority: 2, RuleArn: process.env.MaintenanceRuleArn, }, /* more items */ ], }; } else { return; } ALBへ設定変更リクエスト リクエストにはSetRulePriorities APIを使用します。 パラメーターの詳細はドキュメントをご覧ください。 この関数では優先順位であるPriorityとALBリスナールールのARNであるRuleArnを設定しています。 まとめ 今回はALBのリスナールールを自動で切り替える関数を紹介しました。 現状はCloudFormationの通知機能から発火させていますが、他にもEventBridgeのスケジュール実行や、フロントエンドからボタンでの切り替えなどもできると思うので、参考にして頂ければ幸いです。 参考資料 SetRulePriorities - Elastic Load Balancing
- 投稿日:2021-06-23T11:15:15+09:00
Node.jsのnpxでBusiness Card(電子名刺)を作る方法
はじめに 先日、仕事でご一緒した方が、npxを使ったBusiness Cardで自己紹介をされていて、素敵だな〜と思ったので、真似させていただき作ってみました!! デモ まず、早速デモをご紹介します。 ターミナルで npx misa335 と入力してみてください。 ターミナル上にBusiness Cardが表示されるはずです! npmパッケージのリンク https://www.npmjs.com/package/misa335 作り方 ① プロジェクトの作成 npm initで新規プロジェクトを作成します。 ② package.jsonの編集 package.jsonを編集して、npx <your-project-name>を実行すると、npmパッケージを落として、package.jsonに記載された"main": "index.js"が実行されるように設定します。 package.json { ... "main": "index.js", "bin": { "misa335": "index.js" }, ... ③ Business Cardの作成 Business Cardとして表示させたい項目を、index.jsのconsole.log内に記載します。 また、1行目に#!/usr/bin/env nodeと、nodeのインタプリタを実行する記述をします。 index.js #!/usr/bin/env node console.log(' Business Cardとして表示する項目 '); ローカルでBusiness Cardが表示されるかどうか、 node index.jsで確認することができます。 ④ Githubにpushする 任意のGithubレポジトリにpushします。 npmに公開する方法 ① npmアカウントの作成 https://www.npmjs.com/ で、npmアカウントを作成します。 ② npmへのログイン ターミナルでnpm loginと打ち、Username, Password, Email Addressを順に入力してnpmにログインします。 ③ プロジェクトの公開 npm publish --access publicと入力すると、npm上でプロジェクトが公開されます。 最後に、<npx your-project-name>を試して、Business Cardが表示されるか確認します。 おわりに 簡単にnpxを使ったBusiness Cardが作成できました! よかったらぜひ試してみてください♪ また、SNSでつながっていただけると嬉しいです?
- 投稿日:2021-06-23T09:54:53+09:00
Error: Cannot find module 'express'
npm install -g express expressをインストールしてもexpressが見つけられない。。。 node server.js internal/modules/cjs/loader.js:883 throw err; ^ Error: Cannot find module 'express' やろうとしていること vuex/examples at dev · vuejs/vuex のserver.jsを実行したい 解決 npm install でパッケージをインストールすればいいだけ
- 投稿日:2021-06-23T04:20:20+09:00
node.js、express、handlebarsを使ってウェブサーバーの立ち上げからページの制作までをやってみた - 1
次はNode環境で、ウェブサイトを作っていこう 前回からの続き、expressをインストールし、サーバーも立ち上がったので、早速ウェブサイトを作っていくよ。 通常のサイト制作と大きく違っているのは、ディレクトリの構造。 静的データを動的データを分けて、expressに登録していかんといかんのじゃ。 あと、テンプレートエンジンと言われているプラグインをexpressに入れて、ページを作っていくよ。 何故そんなものを使うかと言うと、ただの静的なサイトをテキストエディタでもIDEでも使って作って登録した静的フォルダにいれれば、今までのような阿部 寛的なページは勿論作れるのじゃが、php的な作り方。つまり、ヘッダーとかフッタとかの使い回す部分を分けておいて、ページ読み込み時に合体させるみたいな事をするには、このテンプレートエンジンを使うのじゃ。 名前の通り、”テンプレート”を生成するエンジンじゃの。 色々種類はあるが、自分はhandlebarsが使いやすいので、これで解説するぞ。いいな! handlebarsではなくて、それのexpress用エンジンhbsをインストール。 ~/myProject/web-server$ npm i hbs up to date, audited 89 packages in 2s 2 packages are looking for funding run `npm fund` for details found 0 vulnerabilities そして、expressにテンプレートエンジンとして登録。 app.set('view engine', 'hbs'); 次に、publicディレクトリを制作。 ここに静的なファイルは保存していく。img,css,クライアント側jsなんかじゃな。 それが出来たら、publicディレクトリをexpressに登録。 その前に、pathモジュールをダンロードして、インストール。 npm i path const path = require('path'); nodeのサイトは、app.jsが全ての起点になるのと、サーバーにデプロイする際に、ディレクトリの構造が従来のサイト構造と違っているので、app.jsファイルを基点にモジュールでパスをしっかりと確定しておかないとややこしい事になる。なので、モジュールで指定する。 const publicDir = path.join(__dirname, "../public") app.use(express.static(publicDir)); 何をやっているかと言うと、ルートフォルダからのpublicディレクトリへのパスをモジュールで割り出して、expressで静的ディレクトリとして登録している。静的なファイルは全部ここやでとexpressに伝えている。 そして、viewsディレクトリを作る。テンプレートエンジンで制作したページは、基本ここに保存していくのである。 静的ファイルはpublic、動的ファイルはviews、nodeのローレベルファイルはルート直下っていうイメージでディレクトリを整理していくと良いよ。多分! で、その中にindex.hbsを作ろう。ここがランディングページになる。 hbsって言っても、普通のhtmlページ。 index.hbs <!DOCTYPE html> <html lang="en"> <head> <title>Node de Site</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <h1>Title here</h1> </body> </html> で、前回やったルーティング。 このindex.hbsをルートページにする。sendでなくて、render。テンプレートをレンダリングする。 app.render('',(req, res)=>{ res.render('index') }) で、一度サーバーを立ち上げて、ページを表示させてみよう。 ~/myProject$ node src/app.js server running on Port:3030 ブラウザで localhost:3030/ にアクセスすると、ページが表示されてるね。 次は、少し動的に、ページのタイトルとサイトの説明文を挿入してみよう。 {{}}で囲ったところに、変数を入れると、レンダリング時に表示される。 index.hbs <!DOCTYPE html> <html lang="en"> <head> <title>Title here</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <h1>{{title}}</h1> <h3>{{description}}</h3> </body> </html> app.js app.get('/',(req, res)=>{ res.render('index', { title: "Hishori Abe no site", description: "Welcome to fastest site in japan" }) }) で、ブラウザでローカルにアクセスし、表示するとこうなってるはず。 ここまでで、node環境にexpressをインストールし、handlebarsを使って、htmlページをサーバー上で表示することが出来たんじゃ。 app.jsファイルはこんなこんな感じ。 const path = require('path'); const express = require('express'); const app = express(); const port = process.env.PORT || 3030; app.set('view engine', 'hbs'); app.get('/',(req, res)=>{ res.render('index', { title: "Hishori Abe no site", description: "Welcome to fastest site in japan" }) }) const publicDir = path.join(__dirname, "../public") app.use(express.static(publicDir)); // routing // / => root app.get('/', (req, res)=>{ res.send('Hello Express!'); }) app.listen(port, ()=>{ console.log(`server running on Port:${port}`); }) バイトの時間がきたんで、handlebarsを使ってページをテンプレート化していくのは次回じゃ! じゃあの。
- 投稿日:2021-06-23T04:20:20+09:00
node.js、express、handlebarsを使ってウェブサーバーの立ち上げからページの制作までをやってみた - 2
次はNode環境で、ウェブサイトを作っていこう 前回からの続き、expressをインストールし、サーバーも立ち上がったので、早速ウェブサイトを作っていくよ。 通常のサイト制作と大きく違っているのは、まず、ディレクトリの構造。 前回もやったけど、エンドポイントって考え方がメインになるので、旧来の絶対パスとか相対パスっていう考え方とはちがうなぁという感じ。 基本、静的データと動的データを分けて、expressに登録していくんじゃ。 あと、テンプレートエンジンと言われているプラグインをexpressに入れて、ページを作っていくよ。 何故そんなものを使うかと言うと、ただの静的なサイトをテキストエディタでもIDEでも使って作って登録した静的フォルダにいれれば、今までのような阿部 寛的なページは勿論作れるのじゃが、php的な作り方。つまり、ヘッダーとかフッタとかの使い回す部分を分けておいて、ページ読み込み時に合体させるみたいな事をするには、このテンプレートエンジンを使うのじゃ。 名前の通り、”テンプレート”を生成するエンジンじゃの。 色々種類はあるが、自分はhandlebarsが使いやすいので、これで解説するぞ。いいな! handlebarsではなくて、それのexpress用エンジンhbsをインストール。 ~/myProject/web-server$ npm i hbs up to date, audited 89 packages in 2s 2 packages are looking for funding run `npm fund` for details found 0 vulnerabilities そして、expressにテンプレートエンジンとして登録。 app.set('view engine', 'hbs'); 次に、publicディレクトリを制作。 ここに静的なファイルは保存していく。img,css,クライアント側jsなんかじゃな。あと、岩のようなhtmlページとか。 それが出来たら、publicディレクトリをexpressに登録。 その前に、pathモジュールをダンロードして、インストール。 npm i path const path = require('path'); nodeのサイトは、app.jsが全ての起点になるのと、サーバーにデプロイする際に、ディレクトリの構造が従来のサイト構造と違っているので、app.jsファイルを基点にモジュールでパスをしっかりと確定しておかないとややこしい事になる。なので、モジュールで指定する。 const publicDir = path.join(__dirname, "../public") app.use(express.static(publicDir)); 何をやっているかと言うと、ルートフォルダからのpublicディレクトリへのパスをモジュールで割り出して、expressで静的ディレクトリとして登録している。静的なファイルは全部ここやでとexpressに伝えている。 そして、viewsディレクトリを作る。テンプレートエンジンで制作したページは、基本ここに保存していくのである。 静的ファイルはpublic、動的ファイルはviews、nodeのローレベルファイルはルート直下っていうイメージでディレクトリを整理していくと良いよ。多分! で、その中にindex.hbsを作ろう。ここがランディングページになる。 hbsって言っても、普通のhtmlページ。 index.hbs <!DOCTYPE html> <html lang="en"> <head> <title>Node de Site</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <h1>Title here</h1> </body> </html> で、前回やったルーティング。 このindex.hbsをルートページにする。sendでなくて、render。テンプレートをレンダリングする。 app.get('',(req, res)=>{ res.render('index') }) で、一度サーバーを立ち上げて、ページを表示させてみよう。 ~/myProject$ node src/app.js server running on Port:3030 ブラウザで localhost:3030/ にアクセスすると、ページが表示されてるね。 次は、少し動的に、ページのタイトルとサイトの説明文を挿入してみよう。 {{}}で囲ったところに、変数を入れると、レンダリング時に表示される。 index.hbs <!DOCTYPE html> <html lang="en"> <head> <title>Title here</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <h1>{{title}}</h1> <h3>{{description}}</h3> </body> </html> app.js app.get('/',(req, res)=>{ res.render('index', { title: "Hishori Abe no site", description: "Welcome to fastest site in japan" }) }) で、ブラウザでローカルにアクセスし、表示するとこうなってるはず。 ここまでで、node環境にexpressをインストールし、handlebarsを使って、htmlページをサーバー上で表示することが出来たんじゃ。 app.jsファイルはこんな感じ。 const path = require('path'); const express = require('express'); const app = express(); const port = process.env.PORT || 3030; app.set('view engine', 'hbs'); app.get('/',(req, res)=>{ res.render('index', { title: "Hishori Abe no site", description: "Welcome to fastest site in japan" }) }) const publicDir = path.join(__dirname, "../public") app.use(express.static(publicDir)); // routing // / => root app.get('/', (req, res)=>{ res.send('Hello Express!'); }) app.listen(port, ()=>{ console.log(`server running on Port:${port}`); }) バイトの時間がきたんで、handlebarsを使ってページをテンプレート化していくのは次回じゃ! じゃあの。 [追記]hbsを使って、ウェブページのテンプレート化 時間が出来たので、追記じゃ。phpでやるような、ヘッダやフッタなんかのサイトで使い回すパーツを分けて保存し、それをレンダリング時に合体させるというアレを、hbsを使って.hbsファイルでやってみようと思うのじゃ。 で、次回はお待ちかねのサーバーアップで、wwwに公開するのじゃ! 初めの方で、動的ファイルの保存場所として、viewsフォルダを作ったと思うのだが、この名前はexpressの中で決まっていて、expressはrenderする為に、いわゆるhtmlのソースをこの中から探そうとする。 今回はページをテンプレート化するので、整理しやすいようにtemplateファルダを作り、メインのソースページとテンプレート部品をviewsとpartialsに分けて保存。 こうすると、expressにこのフォルダがviewsだよと、教えてあげないといけない。 const viewsPath = path.join(__dirname, "../templates/views") app.set('views', viewsPath); やっている事は、前回のように、app.jsを基点に、templateファルダへのパスを設定。 で、expressにviewsとして設定。このtemplateがviewsって事を伝えてる。 その次は、部品用のpartialsディレクトリを作り、hbsに、ここに部品が入ってるよという事を伝えないといけない。 hbsモジュールをapp.jsファイルに追加。 const hbs = require('hbs'); 下記コードで設定完了。 const partialPath = path.join(__dirname, "../templates/partials") hbs.registerPartials(partialPath); 試しに、index.hbsからヘッダー部分をカットして、header.hbsファイルを作り、そこにペースト。 header.hbs <!DOCTYPE html> <html lang="en"> <head> <title>Title here</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> で、{{>ファイルの名前}}でターゲットに挿入。 index.hbs {{>header}} <body> <h1>{{title}}</h1> <h3>{{description}}</h3> <h2>test</h2> </body> </html> index.hbsをローカルで開いた結果。 ちゃんと表示されてるね。 これでナビでもフッタでも、共通部分のファイルを作って、{{>ファイルの名前}}で読み込めばオケ。 ここまでのapp.jsファイル app.js const path = require('path'); const express = require('express'); const hbs = require('hbs'); const { send } = require('process'); const app = express(); const port = process.env.PORT || 3030; // define path for express const publicDir = path.join(__dirname, "../public") const viewsPath = path.join(__dirname, "../templates/views") const partialPath = path.join(__dirname, "../templates/partials") // set-up handlebars engine and path to views app.set('view engine', 'hbs'); app.set('views', viewsPath); hbs.registerPartials(partialPath); // set-up static directory to serve app.use(express.static(publicDir)); // routing // / => root app.get('/',(req, res)=>{ res.render('index') }) app.get('*', (req, res)=>{ send.res('404 not found!') }) app.listen(port, ()=>{ console.log(`server running on Port:${port}`); })