20211010のNode.jsに関する記事は4件です。

varでできてletでできないこと(偏向報道)

遭遇した背景 「Node.js超入門第3版」という本を借りていた。p.104を写経し、実行すると、 ... let content = ejs.render(other_page,{ ... $ SyntaxError: Identifier 'content' has already been declared というエラーが現れた。 読んでそのまま「contentはすでに定義されている」ということだろう。 しかし、プログラムは全く同じだった。 ある一部を除いて。 解決編1 どこぞのYouTuberがvarよりもletが使われているとか言っていたので、 調子に乗って私もletを使っていた。 試しに問題の変数の定義をletからvarに変えると、 完全に解決したッ! 解説 私はurlパースを使ってswitch-case文でルーティングをしていました。 app.js let url_parts = url.parse(request.url); switch(url_parts.pathname){ case '/': let content = ejs.render(index_page,{ ... }); ... break; case '/other': let content = ejs.render(other_page,{ ... }) ... break; ... ここで、問題なのは2つの let content です。 letとvarの違い とりあえず、上のままの単語で検索し、TechAcademyの記事にアクセスしました。 まとめると、次の表のようになります。 let var 再代入 ○ ○ 再宣言 ☓ ○ スコープ ブロック 関数内 letを使ってエラーが出て、varを使ってエラーが出ないのは varが再宣言可能だから。 というだけでした。 これだけでもいいけれど。 動くのでいいのだが、 再宣言は普通に気持ち悪い。 解決してしまおう。 letのスコープ範囲をよく見る。 ここで、一度letのスコープ範囲について確認しましょう。 MDNの解説ではletは let文はブロックスコープのローカル変数を宣言します。任意で値を代入して初期化できます。 とされています。 他のサイトも漁ったところ、 ブロックスコープとは変数が {...}の範囲だけで定義される と分かりました。 おや?するとどうでしょう。 今、このapp.js、caseで区切られてはいますが、{...}では区切られていないですね? 試しにcase文を{...}で囲ってみましょうか。 解決編2 それでは、case '...': ~~ break;までを{ }で囲っていきます。 app.js let url_parts = url.parse(request.url); switch(url_parts.pathname){ case '/':{ let content = ejs.render(index_page,{ ... }); ... break; } case '/other':{ let content = ejs.render(other_page,{ ... }) ... break; } ... はい、実行してみましょう。 $ node app.js Server Start! OK! あとがき なんかさ...こんなところ(case内)で宣言せず、関数内で定義しとけば丸く収まらない? お読み頂きありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

package-lock.json の lockfileVersion が変更されてしまう場合の対処法

解決策 チーム内でnpmのバージョンをv6かv7に合わせる。 発生した問題 npmをアップデート npmで作業中に以下のようなアップデートガイドが表示されたため、npmをv6からv7にアップデートしました。(Node.jsはv14) Update available 6.14.15 → 7.24.2 Run npm i -g npm to update lockfileVersion 1 → 2 すると、その後に npm i を実行した際、以下のような警告が表示されました。 npm WARN old lockfile: The package-lock.json file was created with an old version of npm, so supplemental metadata must be fetched from the registry. 通常より時間がかかり、installは成功しますが、 package-lock.json の lockfileVersion が 1 → 2 に変わり、ファイル全体に大きな修正が入っていました。 lockfileVersion 2 → 1 例えばこの package-lock.json をpushして、他のチームメンバー(npm v6使用)が npm i を実行した場合、以下のような警告が表示されます。 npm WARN read-shrinkwrap: This version of npm is compatible with lockfileVersion@1, but package-lock.json was generated for lockfileVersion@2. I'll try to do my best with it! この場合もinstallは成功しますが、 lockfileVersion が 2 → 1 に戻り、ファイル全体に再び大きな修正が入ります。 原因 npm Docs によると、npm v7からは、生成される package-lock.json のフォーマットが大きく変更されていて、 lockfileVersion: 2 が指定されるようです。 解決策: チーム内でnpmバージョンを合わせる npmは、Node.jsと一緒にインストールされ、デフォルトのバージョンは Node v14 → npm v6、 Node v15 → npm v7 のように、ある程度決まっているようです。 ですが、今回のように各個人がnpmをアップデートした際に、 package-lock.json の中身が変わってしまうという場合があるため、チーム開発では、npmのバージョンについても、メジャーバージョンだけは合わせておいた方が良いようです。 npm i -g npm@バージョン などで指定バージョンをインストールできます。 (バージョン情報は npm/cliのGitHub 等を参照)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【公式ドキュメント和訳】TypeORM Migration

この記事は、2021/10/10時点のTypeORMの仕様に依存します。 TypeORMのバージョンが違ったり、記事の公開から長い時間が経っている場合、正確ではない可能性があります。 typeorm: v0.2.38 翻訳元: migrations.md ライセンス: The MIT License 目次 マイグレーションとは マイグレーションを作成する マイグレーションを実行する マイグレーションを自動生成する コネクションオプション マイグレーションを記述するときに利用できるAPI マイグレーションとは 本番環境でもモデルの変更をデータベースに同期する必要があるわけですが、本番環境のデータベースにデータを入れた状態で、synchronize: trueを使うのは危険です。そこで、マイグレーションを使います。 マイグレーションの正体は普通のJSファイルで、SQLを使用してデータベースのスキーマを更新したり、既存のデータベースに変更を加えるというモノです。 では、すでにデータベースと、Postエンティティがあると仮定します。 import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'; @Entity() export class Post { @PrimaryGeneratedColumn() id: number; @Column() title: string; @Column() text: string; } このエンティティは、数ヶ月の間、本番環境で特に何も変更を加えられていませんでした。そして、その間に数千件の投稿がデータベースに保存されていました。 この状態で、titleというカラム名をnameに変更する必要が出てきました。どうすれば良いでしょうか。 正解は、以下のSQLを実行するマイグレーションを新規作成すれば良いです。(PostgreSQLの例) ALTER TABLE "post" ALTER COLUMN "title" RENAME TO "name"; このSQLを実行すれば、データベースが更新されます。TypeORMはこのようなSQLを記述する 場所 を提供し、必要なときに実行できるようにします。この"場所"のことをマイグレーションと呼んでいます。 マイグレーションを作成する 事前にCLIのインストールが必要です。 マイグレーションを作成する前に、セットアップが必要です。 { "type": "mysql", "host": "localhost", "port": 3306, "username": "test", "password": "test", "database": "test", "entities": ["entity/*.js"], "migrationsTableName": "custom_migration_table", "migrations": ["migration/*.js"], "cli": { "migrationsDir": "migration" } } オプションの詳細は以下の通りです。 "migrationsTableName": "migrations" マイグレーションテーブルの名前をオプションのmigrations以外にする場合に指定します。 "migrations": ["migration/*.js"] マイグレーションの読み込み先のディレクトリを指定します。 "cli": { "migrationsDir": "migration" } CLIを利用して作成するマイグレーションの保存先ディレクトリを指定します。 セットアップが完了したら、CLIでマイグレーションを作成することができます。 typeorm migration:create -n PostRefactoring PostRefactoringというのがマイグレーションの名前を指していて、ここには好きな名前を指定することができます。このコマンドを実行すると、"migration"ディレクトリに{TIMESTAMP}-PostRefactoring.tsという形式のファイルが作成されます。{TIMESTAMP}の部分は、マイグレーションを作成した日時のタイムスタンプです。では、ファイルを開いて、SQLを書き込んでみましょう。 マイグレーションの中身は、以下のようになっているはずです。 import {MigrationInterface, QueryRunner} from "typeorm"; export class PostRefactoringTIMESTAMP implements MigrationInterface { async up(queryRunner: QueryRunner): Promise<void> { } async down(queryRunner: QueryRunner): Promise<void> { } } upとdownという二つのメソッドがあります。upはマイグレーションそのものを書くためのもので、downはupで行った変更を取り消すためのものです。 upもdownも、QueryRunnerオブジェクトを持っています。データベースに対する操作は、全てこのオブジェクトを介して行います。Query Runnerについてはこちらを参照してください。 Postを変更するマイグレーションは以下のようになります。 import {MigrationInterface, QueryRunner} from "typeorm"; export class PostRefactoringTIMESTAMP implements MigrationInterface { async up(queryRunner: QueryRunner): Promise<void> { await queryRunner.query(`ALTER TABLE "post" RENAME COLUMN "title" TO "name"`); } async down(queryRunner: QueryRunner): Promise<void> { await queryRunner.query(`ALTER TABLE "post" RENAME COLUMN "name" TO "title"`); // reverts things made in "up" method } } マイグレーションを実行する マイグレーションを作成したら、CLIコマンドで実行することができます。 typeorm migration:run typeorm migration:createかtypeorm migration:generateコマンドをoフラグを付けずに実行した場合、.tsファイルが生成されます。(詳しくは、Generating migrationsをご参照ください。)migration:runとmigration:revertコマンドでは、.jsファイルしか扱うことができません。従って、TypeScriptファイルは、マイグレーションの実行前にトランスパイルされている必要があります。他の方法としては、typeormをts-nodeと組み合わせて使用することで、.ts拡張子のマイグレーションファイルを実行することができます。 ts-nodeのサンプル ts-node --transpile-only ./node_modules/typeorm/cli.js migration:run ts-nodeのサンプル(node_modulesを参照しない場合) ts-node $(yarn bin typeorm) migration:run これらのコマンドは、保留中の全てのマイグレーションをタイムスタンプ順で実行します。言い換えると、作成したマイグレーションのupメソッド内に書いたSQLクエリが全て実行されるということです。これで、データベーススキーマを更新することができました。 マイグレーションを取り消すときは、以下のコマンドを実行します。 typeorm migration:revert このコマンドは、実行した中で最新のマイグレーションのdownを実行します。複数のマイグレーションを取り消す必要がある時は、このコマンドを複数回呼び出してください。 マイグレーションを自動生成する TypeORMでは、スキーマに対して行った変更から、自動的にマイグレーションを生成する機能があります。 titleカラムを持つPostエンティティがあったとして、titleをnameに変更した場合を想定しましょう。以下のコマンドを実行したとします。 typeorm migration:generate -n PostRefactoring すると、以下の内容を含む{TIMESTAMP}-PostRefactoring.tsの形式のファイルが自動生成されます。 import {MigrationInterface, QueryRunner} from "typeorm"; export class PostRefactoringTIMESTAMP implements MigrationInterface { async up(queryRunner: QueryRunner): Promise<void> { await queryRunner.query(`ALTER TABLE "post" ALTER COLUMN "title" RENAME TO "name"`); } async down(queryRunner: QueryRunner): Promise<void> { await queryRunner.query(`ALTER TABLE "post" ALTER COLUMN "name" RENAME TO "title"`); } } oフラグ(--outputJsのエイリアス)を使って、JavaScriptファイルを生成することもできます。TypeScriptのパッケージがインストールされていなくて、JavaScriptのみのプロジェクトの場合は便利です。その場合は、以下の内容を含んだ{TIMESTAMP}-PostRefactoring.jsファイルが生成されます。 const { MigrationInterface, QueryRunner } = require("typeorm"); module.exports = class PostRefactoringTIMESTAMP { async up(queryRunner) { await queryRunner.query(`ALTER TABLE "post" ALTER COLUMN "title" RENAME TO "name"`); } async down(queryRunner) { await queryRunner.query(`ALTER TABLE "post" ALTER COLUMN "title" RENAME TO "name"`); } } 自分でSQLクエリを書く必要がないというのは便利ですね。ただし、この方法では、モデルに変更を加えるたびにマイグレーションを生成してください。複数行のSQLクエリを含むマイグレーションを生成するには、pフラグ(--prettyのエイリアス)を使用してください。 コネクションオプション デフォルトではないコネクションに対してrunやrevertを実行する必要がある場合は、-cフラグ(--connectionのエイリアス)を使用して、コンフィグ名を引数として渡すことができます。 ここの説明はちょっと分かりにくいので、こっちを参照→Using ormconfig.json マイグレーションを記述するときに利用できるAPI APIを使ってデータベーススキーマを変更するときには、QueryRunnerを使用します。 サンプル import {MigrationInterface, QueryRunner, Table, TableIndex, TableColumn, TableForeignKey } from "typeorm"; export class QuestionRefactoringTIMESTAMP implements MigrationInterface { async up(queryRunner: QueryRunner): Promise<void> { await queryRunner.createTable(new Table({ name: "question", columns: [ { name: "id", type: "int", isPrimary: true }, { name: "name", type: "varchar", } ] }), true) await queryRunner.createIndex("question", new TableIndex({ name: "IDX_QUESTION_NAME", columnNames: ["name"] })); await queryRunner.createTable(new Table({ name: "answer", columns: [ { name: "id", type: "int", isPrimary: true }, { name: "name", type: "varchar", }, { name: 'created_at', type: 'timestamp', default: 'now()' } ] }), true); await queryRunner.addColumn("answer", new TableColumn({ name: "questionId", type: "int" })); await queryRunner.createForeignKey("answer", new TableForeignKey({ columnNames: ["questionId"], referencedColumnNames: ["id"], referencedTableName: "question", onDelete: "CASCADE" })); } async down(queryRunner: QueryRunner): Promise<void> { const table = await queryRunner.getTable("answer"); const foreignKey = table.foreignKeys.find(fk => fk.columnNames.indexOf("questionId") !== -1); await queryRunner.dropForeignKey("answer", foreignKey); await queryRunner.dropColumn("answer", "questionId"); await queryRunner.dropTable("answer"); await queryRunner.dropIndex("question", "IDX_QUESTION_NAME"); await queryRunner.dropTable("question"); } } マイグレーションを記述するときに利用できるAPI getDatabases(): Promise<string[]> 全てのデータベース名を取得。 getSchemas(database?: string): Promise<string[]> database - データベースが指定されると、そのデータベースのスキーマを返す 全てのスキーマ名を取得。SQLServerとPostgreSQLでのみ使用可能。 getTable(tableName: string): Promise<Table|undefined> tableName - 読み込むテーブル名 指定した名前のテーブルを読み込む。 getTables(tableNames: string[]): Promise<Table[]> tableNames - 読み込むテーブル名(複数) 指定した名前のテーブルを読み込む(複数)。 hasDatabase(database: string): Promise<boolean> database - チェックするデータベース名 指定した名前のデータベースが存在するかをチェックする。 hasSchema(schema: string): Promise<boolean> schema - チェックするスキーマ名 指定した名前のスキーマが存在するかチェックする。SQLServerとPostgreSQLでのみ使用可能。 hasTable(table: Table|string): Promise<boolean> table - テーブルのオブジェクト、もしくは名前 テーブルが存在しているかチェックする。 hasColumn(table: Table|string, columnName: string): Promise<boolean> table - テーブルのオブジェクト、もしくは名前 columnName - チェックするカラムの名前 テーブルに指定したカラムが存在するかチェックする。 createDatabase(database: string, ifNotExist?: boolean): Promise<void> database - データベース名 ifNotExist - すでにデータベースが存在したとき、trueが指定されていればスキップするが、そうでなければ例外が投げられる 新しいデータベースを作成する。 dropDatabase(database: string, ifExist?: boolean): Promise<void> database - データベース名 ifExist - データベースが存在しなかったとき、trueが指定されていればスキップするが、そうでなければ例外が投げられる データベースを削除する。 createSchema(schemaPath: string, ifNotExist?: boolean): Promise<void> schemaPath - スキーマ名。SQLServerではスキーマパスをパラメータとして渡せる。スキーマパスを渡した場合、指定されたデータベースにスキーマが作成される。 ifNotExist - すでにスキーマが存在したとき、trueが指定されていればスキップするが、そうでなければ例外が投げられる 新しいテーブルスキーマを作成する。 dropSchema(schemaPath: string, ifExist?: boolean, isCascade?: boolean): Promise<void> schemaPath - スキーマ名。SQLServerではスキーマパスをパラメータとして渡せる。スキーマパスを渡した場合、指定されたデータベースにスキーマが削除される。 ifExist - データベースが存在しなかったとき、trueが指定されていればスキップするが、そうでなければ例外が投げられる isCascade - trueが指定された場合、スキーマに含まれるオブジェクト(テーブルや関数)も削除される。PostgreSQLでのみ指定可能。 テーブルスキーマを削除する。 createTable(table: Table, ifNotExist?: boolean, createForeignKeys?: boolean, createIndices?: boolean): Promise<void> table - テーブルオブジェクト。 ifNotExist - すでにスキーマが存在したとき、trueが指定されていればスキップするが、そうでなければ例外が投げられる createForeignKeys - テーブル作成時に外部キーを作成するかどうか(デフォルトはtrue) createIndices - テーブル作成時にインデックスを作成するかどうか(デフォルトはtrue) テーブルを作成する。 以降はAPIの詳細が続くので、原文を参照してください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

npmとyarnとNode.jsについて

はじめに VueやReactを使ってWeb開発をする際、npmやyarnといったコマンドを使ってプロジェクトの作成やパッケージのインストール等を行っていると思います。 自分はこのnpmやyarnについてなんとなく使っていたので理解を深めるため、主に以下の3点について調べてみました。 そもそもnpm, yarnとは何か Node.jsとは何か npmとyarnで何か違いはあるのか npm, yarn とは 共に、Node.js上で動くJavaScriptのパッケージマネージャーのことを指します。 まず 「パッケージマネージャー」とは コンピュータに何のソフトウェアがインストールされたかを記録し、新しいソフトウェアのインストール・新しいバージョンへのソフトウェアの更新・以前インストールしたソフトウェアの削除を容易に行えるようにするプログラムのこと 参考 パッケージマネージャとは 特徴 パッケージAのバージョン1.5にはパッケージBのバージョン5.0以上が必要といった依存関係を解決する情報や現時点でインストールされているパッケージの情報をもつ パッケージのインストール・アンインストール・アップグレード・ダウングレードを行う インストール・アップグレート時には依存関係にあるパッケージを自動でインストール・アップグレートしてくれる 言語ごとにパッケージマネジャーが分かれている Linux, Windows, macOSなど, OSを管理するパッケージマネジャーもある パッケージマネージャーを使わないと大変 無数のソフト(パッケージ、ライブラリ)をパッケージマネージャーを使わず、ソフトのホームページから手動でインストールするのは骨の折れる作業です。また、それぞれのソフトにはバージョンの依存関係が発生し、それらを確認しながら必要なバージョンをインストールする必要があるためかなり時間がかかります。 パッケージマネージャーの例 パッケージマネジャー 言語 or OS pip Python gem Ruby composer PHP glide Go npm JavaScript yarn JavaScript Homebrew macOS apt-get Linux (Debian系) 次に 「Node.js上で動く」とは そもそもNode.jsとは Node.jsとはサーバ上でJavaScriptを実行するための環境のことです。 もともとJavaScriptはブラウザ上で動く言語ですが、Node.jsをインストールすることでサーバ上でJavaScriptが使えるようになります。「Node.js上で動く」とは「サーバ上でJavaScriptを実行するための環境で動作する」ということになります。 つまり、npm, yarnはNode.jsという環境で動くJavaScriptでできたツール(パッケージやモジュール)を管理するプログラムということになります。 サーバ上で実行する環境とは Node.jsをインストールすることで、RubyやPythonのようなバックエンド言語としてJavaScriptを使うことができ、今までできなかったファイルの読み書きや、ネットワーク通信等のOSの機能にアクセスできるようになるということです。 Pythonを動かしたいとき、PythonをインストールしてPythonの実行環境をつくる必要があるように、サーバ上でJavaScriptを動かしたいときはNode.jsをインストールしてJavaScript実行環境を作る必要があるといったイメージです。 ちなみに、ブラウザ上で動いているJavaScriptはブラウザ自体が実行環境の役割を担ってくれています。 なぜReactで開発を行う際Node.jsが必要になるのか 「ReactはJavaScriptのUIライブラリであり、ブラウザ上で動くものなのでNode.js(サーバ上でのJavaScript実行環境)は不要ではないか」と思っていました。 調べてみると、Node.jsがないと色々と不便なことがあり、Node.jsのインストールは基本的に必要とされています。 ただ以下の記事で紹介されている通り、Node.jsがなくてもReact自体は使えます。 正真正銘のReactだけの不純物なしでReact入門 ブラウザだけで動作するNode.jsなしReactの使い方 Node.jsが必要な理由 npmもしくはyarnを使用したい Reactを使った大規模なアプリケーション開発となると、様々なパッケージをインストールする必要があります。前述しましたが、それらのパッケージを依存関係を意識しながらインストールするにはnpmがあると嬉しくてNode.jsが必要になるということです。 babelを使用したい JavaScriptの新しい書き方から古い書き方に変換する 新しいJS文法に対応していない古いブラウザでも使えるようになる webpackを使用したい JavaScript, CSS, 画像等を一つのjsファイルにまとめる 機能ごとにモジュールを細かく分けられる HTTPリクエストの回数を減らすことができる その他様々なビルドツールや開発補助ツールを使用したい 用途の遷移 つまり、サーバーサイドでJavaScriptのパッケージを管理するnpmでしたが、ReactやVue等のフロントエンド用のパッケージを提供するのにも使われるようになったということですね。 流れとしては以下のようなイメージです。 1. JavaScriptは元々ブラウザで動作するプログラミング言語だった 2. Node.jsの誕生によってサーバでJavaScriptが使えるようになった。 3. 最近ではnpm, babel, webpack等のパッケージを提供する「クライアントサイドのJavaScript実行環境」として使われることが多くなった npmとyarnの特徴 npm Node.jsがリリースされた翌年(2010年)リリース Node Package Managerの略 package-lock.jsonファイルを自動的に生成する Node.jsをインストールすれば自動的にインストールされる yarn 2016年リリース Facebook、Google、Exponent、Tildeによって開発された新しいJavaScriptパッケージマネージャー npmと互換性がある 同じpackage.jsonが使える npmより厳密にモジュールのバージョンを固定できる npmよりインストールが速い yarnの方が優れているように見えますが、最近ではnpmがアップデートされて機能の差はあまりないようです。 参考 パッケージマネージャがパッケージをインストールする仕組み パッケージ管理システムとは? Node.jsとはなにか?なぜみんな使っているのか? Node.js入門編 JSのモジュールとbabelとwebpackとは何かまとめてみる(初心者向け)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む