ラベル NodeJS の投稿を表示しています。 すべての投稿を表示
ラベル NodeJS の投稿を表示しています。 すべての投稿を表示

2025年1月21日火曜日

Prisma で many to many のリレーションシップを定義する

Prisma での many to many のリレーションシップ定義・操作の勉強を行う。

前提

プロジェクトの初期化

npm init

必要パッケージのインストール

npm i dotenv
npm i -D typescript tsx @types/node prisma
  • dependencies
    • dotenv: 接続先設定を env に記述するために利用
  • devDependencies
    • typescript: TypeScript トランスパイルに利用
    • tsx: ts を実行できるコマンド。チュートリアルに入っていたから入れたが、使いどころは謎。
    • @types/node: node の型情報

DB 接続設定の作成

.env ファイルに接続先設定を記述する。

DATABASE_URL=postgres://postgres:postgres@postgres/postgres

TypeScript 設定ファイル(tsconfig.json)の作成

tsconfig.json の作成

tsc コマンドで、 tsconfig.json を作成する

npx tsc --init

tsconfig.json のパラメーター編集

以下表のとおりパラメーターを設定。

パラメーター
outDir ./dist

ここまでの動作確認

ひとまず TypeScript をコンパイル・実行できるかを確認。

mkdir src
echo 'console.log("Hello, World!");' > ./src/index.ts
tsc
node ./dist/index.js

Hello, World! が表示される … ok!

Prisma の設定

Prisma の初期化

npx prisma init --datasource-provider postgresql

prisma/schema.prisma にスキーマファイルが生成される。

NOTE: ここで、 npx prisma db pull とすると、既存 DB を基にスキーマファイルを更新してくれる

DB スキーマの作成

Prisma の DSL でスキーマを書いていく。

model Book {
  id      Int      @id @default(autoincrement())
  title   String
  authors BooksOnAuthors[]
}

model Author {
  id    Int    @id @default(autoincrement())
  name  String
  books BooksOnAuthors[]
}

model BooksOnAuthors {
  id         Int    @id @default(autoincrement())
  // 本を削除する際には、著者へのリレーションシップは消えて欲しいのでカスケード設定
  book       Book   @relation(fields: [bookId], references: [id], onDelete: Cascade, onUpdate: Cascade)
  bookId     Int // リレーションスカラーフィールド。 上記の `@relation``fields` で指定した名前にする。
  // 著者を消そうとしたときに、その著者の本がまだある場合はエラーにして火しいのでカスケードなし
  author     Author @relation(fields: [authorId], references: [id])
  authorId Int // リレーションスカラーフィールド
}

prisma client のインストールとクライアントコード生成

この時点で、モデルに対応した API が生成されるようだ。

npx prisma generate

マイグレーションの実行

npx prisma migrate dev --name init

prisma/migrations にマイグレーション用 SQL が生成され、それが実行される。

A5:SQL-Mk2 で確認してみると、確かにテーブルが作成されている … ok!

メインファイルの作成

作成した Prisma の設定で DB にアクセスし、 CRUD するアプリケーションを作成する。

src/index.ts:

import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

async function main() {
  // Author を生成
  const createdAuthor = await prisma.author.create({
    data: {
      name: 'mikoto2000',
    },
  })
  console.dir(`createdAuthor: ${JSON.stringify(createdAuthor)}`);

  // Book と、 Author へのリレーションシップを生成
  const createdBook = await prisma.book.create({
    data: {
      title: 'mikoto2000\'s history',
      authors: {
        create: [
          {
            authorId: createdAuthor.id
          }
        ]
      }
    },
  })
  console.dir(`createdBook: ${JSON.stringify(createdBook)}`);

  // 作成した Book の検索(中間テーブル・Author を JOIN して、戻り値に含める)
  const foundBook = await prisma.book.findMany({
    include: {
      authors: {
        include: {
          author: {}
        }
      }
    }
  });
  console.dir(`foundBook: ${JSON.stringify(foundBook)}`);
  console.dir(`プロパティ取得の練習: { title: ${foundBook[0].title}, Author: ${foundBook[0].authors.map((e) => e.author.name)}}`);

  // Book を削除(中間テーブルの要素と Book 自体の削除。中間テーブルはカスケード設定をしているので自動で削除される)
  const deletedBook = await prisma.book.delete({
    where: {
      id: createdBook.id
    },
  });
  console.dir(`deletedBook: ${JSON.stringify(deletedBook)}`);

  // Author を削除
  const deletedAuthor = await prisma.author.delete({
    where: {
      id: createdAuthor.id
    }
  });
  console.dir(`deletedAuthor: ${JSON.stringify(deletedAuthor)}`);
}

main()
  .then(async () => {
    await prisma.$disconnect()
  })
  .catch(async (e) => {
    console.error(e)
    await prisma.$disconnect()
    process.exit(1)
  })

動作確認

ビルド

npx tsc

実行

node ➜ ~/tmp $ node ./dist/index.js 
'createdAuthor: {"id":1,"name":"mikoto2000"}'
`createdBook: {"id":1,"title":"mikoto2000's history"}`
`foundBook: [{"id":1,"title":"mikoto2000's history","authors":[{"id":1,"bookId":1,"au
thorId":1,"author":{"id":1,"name":"mikoto2000"}}]}]`
"プロパティ取得の練習: { title: mikoto2000's history, Author: mikoto2000}"
`deletedBook: {"id":1,"title":"mikoto2000's history"}`
'deletedAuthor: {"id":1,"name":"mikoto2000"}'

ok! 以上。

参考資料

2025年1月20日月曜日

Prisma に TypeScript と PostgreSQL で入門する

Drizzle との違いを理解するため Prisma の Get Started をやってみる。

前提

プロジェクトの初期化

npm init

必要パッケージのインストール

npm i dotenv
npm i -D typescript tsx @types/node prisma
  • dependencies
    • dotenv: 接続先設定を env に記述するために利用
  • devDependencies
    • typescript: TypeScript トランスパイルに利用
    • tsx: ts を実行できるコマンド。チュートリアルに入っていたから入れたが、使いどころは謎。
    • @types/node: node の型情報

DB 接続設定の作成

.env ファイルに接続先設定を記述する。

DATABASE_URL=postgres://postgres:postgres@postgres/postgres

TypeScript 設定ファイル(tsconfig.json)の作成

tsconfig.json の作成

tsc コマンドで、 tsconfig.json を作成する

npx tsc --init

tsconfig.json のパラメーター編集

以下表のとおりパラメーターを設定。

パラメーター
outDir ./dist

ここまでの動作確認

ひとまず TypeScript をコンパイル・実行できるかを確認。

mkdir src
echo 'console.log("Hello, World!");' > ./src/index.ts
tsc
node ./dist/index.js

Hello, World! が表示される … ok!

Prisma の設定

Prisma の初期化

npx prisma init --datasource-provider postgresql

prisma/schema.prisma にスキーマファイルが生成される。

NOTE: ここで、 npx prisma db pull とすると、既存 DB を基にスキーマファイルを更新してくれる

DB スキーマの作成

Prisma の DSL でスキーマを書いていく。

以下コードを見れば、どんなスキーマを作りたいのかわかるはず…

model User {
  id    Int     @id @default(autoincrement())
  email String  @unique
  name  String?
  posts Post[]
}

model Post {
  id        Int     @id @default(autoincrement())
  title     String
  content   String?
  published Boolean @default(false)
  author    User    @relation(fields: [authorId], references: [id])
  authorId  Int
}

prisma client のインストールとクライアントコード生成

この時点で、モデルに対応した API が生成されるようだ。

npx prisma generate

マイグレーションの実行

npx prisma migrate dev --name init

prisma/migrations にマイグレーション用 SQL が生成され、それが実行される。

A5:SQL-Mk2 で確認してみると、確かにテーブルが作成されている … ok!

メインファイルの作成

作成した Prisma の設定で DB にアクセスし、 CRUD するアプリケーションを作成する。

src/index.ts:

import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

async function main() {
  // User を生成
  const createdUser = await prisma.user.create({
    data: {
      name: 'mikoto2000',
      email: 'mikoto2000@gmail.com',
    },
  })
  console.dir(`createdUser: ${JSON.stringify(createdUser)}`);

  // user を検索
  const foundUser = await prisma.user.findUnique({
    where: {
      email: 'mikoto2000@gmail.com',
    }
  });
  console.dir(`foundUser: ${JSON.stringify(foundUser)}`);

  // user を更新
  if (foundUser) {
    const updatedUser = await prisma.user.update({
      where: {
        email: foundUser.email,
      },
      data: {
        name: 'mikoto2000+prisma@gmail.com',
      },
    })
    console.dir(`updatedUser: ${JSON.stringify(updatedUser)}`);
  }

  // user を再び検索
  const foundUser2 = await prisma.user.findMany({
    where: {
      name: 'mikoto2000+prisma@gmail.com',
    }
  });
  console.dir(`foundUser2: ${JSON.stringify(foundUser2)}`);

  // user を削除
  const deletedUser = await prisma.user.delete({
    where: {
      id: createdUser.id
    }
  });
  console.dir(`deletedUser: ${JSON.stringify(deletedUser)}`);
}

main()
  .then(async () => {
    await prisma.$disconnect()
  })
  .catch(async (e) => {
    console.error(e)
    await prisma.$disconnect()
    process.exit(1)
  })

動作確認

ビルド

npx tsc

実行

node ➜ /workspaces $ node ./dist/index.js
'createdUser: {"id":21,"email":"mikoto2000@gmail.com","name":"mikoto2000"}'
'foundUser: {"id":21,"email":"mikoto2000@gmail.com","name":"mikoto2000"}'
'updatedUser: {"id":21,"email":"mikoto2000@gmail.com","name":"mikoto2000+prisma@gmail.com"}'
'foundUser2: [{"id":21,"email":"mikoto2000@gmail.com","name":"mikoto2000+prisma@gmail.com"}]'
'deletedUser: {"id":21,"email":"mikoto2000@gmail.com","name":"mikoto2000+prisma@gmail.com"}'

ok! 以上。

参考資料

2025年1月1日水曜日

Drizzle のチュートリアルを TypeScript でやる

npm プロジェクトの初期化

npm init

指示通り設定を埋めていくと、 package.json が生成される。

必要パッケージのインストール

npm i drizzle-orm pg dotenv
npm i -D drizzle-kit tsx @types/pg typescript
  • dependencies
    • drizzle-orm: ORM 本体
    • pg: PostgreSQL へ接続するための Driver
    • dotenv: 接続先設定を env に記述するために利用
  • devDependencies
    • drizzle-kit: スキーマファイル生成や、マイグレーション等を行うのに利用
    • tsx: 謎。チュートリアルに入っていたから入れた。
    • @types/pg: pg の型情報
    • typescript: TypeScript トランスパイルに利用

DB 接続設定の作成

.env ファイルに接続先設定を記述する。

echo 'DATABASE_URL=postgres://postgres:postgres@postgres/postgres' > .env

TypeScript 設定ファイル(tsconfig.json)の作成

tsconfig.json の作成

tsc コマンドで、 tsconfig.json を作成する

npx tsc --init

tsconfig.json のパラメーター編集

以下表のとおりパラメーターを設定。

パラメーター
outDir ./dist

ここまでの動作確認

ひとまず TypeScript をコンパイル・実行できるかを確認。

mkdir src
echo 'console.log("Hello, World!");' > ./src/index.ts
tsc
node ./dist/index.js

Hello, World! が表示される … ok!

Drizzle の設定

コネクション初期化処理の作成

以下の通りコネクション初期化処理を作成。

mkdir -p ./src/db
cat << EOF > ./src/db/index.ts
import 'dotenv/config';
import { drizzle } from 'drizzle-orm/node-postgres';

const db = drizzle(process.env.DATABASE_URL!);
EOF

Drizzle の設定ファイル作成

以下の通り設定ファイルを作成。

cat << EOF > ./drizzle.config.ts
import 'dotenv/config';
import { defineConfig } from 'drizzle-kit';

export default defineConfig({
  out: './drizzle',
  schema: './src/db/schema.ts',
  dialect: 'postgresql',
  dbCredentials: {
    url: process.env.DATABASE_URL!,
  },
});
EOF

DB スキーマの作成

TypeScript でスキーマを書いていく。

以下コードを見れば、どんなスキーマを作りたいのかわかるはず…

cat << EOF > ./src/db/schema.ts
import { integer, pgTable, varchar } from "drizzle-orm/pg-core";

export const usersTable = pgTable("users", {
  id: integer().primaryKey().generatedAlwaysAsIdentity(),
  name: varchar({ length: 255 }).notNull(),
  age: integer().notNull(),
  email: varchar({ length: 255 }).notNull().unique(),
});
EOF

マイグレーションファイルの生成と適用

マイグレーションファイルの生成

npx drizzle-kit generate

設定ファイルの out に指定したとおり、 ./drizzle にマイグレーションファイルが出力される。

マイグレーションファイルの適用

migration サブコマンドで生成したマイグレーションファイルを DB に反映させる。

npx drizzle-kit migrate

A5:SQL-Mk2 で確認してみると、確かにテーブルが作成されている … ok!

メインファイルの作成

作成した Drizzle の設定で、DB にアクセスし、 CRD するアプリケーションを作成する。

cat <<"EOF" > ./src/index.ts
import 'dotenv/config';
import { drizzle } from 'drizzle-orm/node-postgres';
import { eq } from 'drizzle-orm';
import { usersTable } from './db/schema';
  
const db = drizzle(process.env.DATABASE_URL!);

async function main() {
  const user: typeof usersTable.$inferInsert = {
    name: 'John',
    age: 30,
    email: 'john@example.com',
  };

  await db.insert(usersTable).values(user);
  console.log('New user created!')

  const users = await db.select().from(usersTable);
  console.log('Getting all users from the database: ', users)
  /*
  const users: {
    id: number;
    name: string;
    age: number;
    email: string;
  }[]
  */

  await db
    .update(usersTable)
    .set({
      age: 31,
    })
    .where(eq(usersTable.email, user.email));
  console.log('User info updated!')

  await db.delete(usersTable).where(eq(usersTable.email, user.email));
  console.log('User deleted!')
}

main();
EOF

動作確認

ビルド

npx tsc
node ./dist/src/index.js

実行

node ➜ /workspaces $ node ./dist/src/index.js
New user created!
Getting all users from the database:  [ { id: 1, name: 'John', age: 30, email: 'john@example.com' } ]
User info updated!
User deleted!

ok! 以上。

参考資料

2024年11月30日土曜日

macOS で nexe を使って devcontainers/cli をシングルバイナリにする

前提

  • OS: macOS Sequoia 15.1.1 (24B91)
  • Homebrew インストール済み

前の macOS セットアップ記事の続きから。

Node.js のインストール

brew install nvm
mkdir ~/.nvm
export NVM_DIR="$HOME/.nvm"
[ -s "/opt/homebrew/opt/nvm/nvm.sh" ] && \. "/opt/homebrew/opt/nvm/nvm.sh"  # This loads nvm
[ -s "/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm" ] && \. "/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm"  # This loads nvm bash_completion
nvm install v21.7.3

devcontainers/cli のクローン

git clone --depth 1 -b v0.72.0 https://github.com/devcontainers/cli

ビルド

cd cli
yarn
yarn compile-prod
npx nexe --python=/usr/bin/python3 --loglevel="verbose" -i ./devcontainer.js --make="-j8" -r="scripts/updateUID.Dockerfile" -t -b -o ../dist/devcontainer-darwin-arm64-v0.72.0

node 自体に手を入れるらしく、初回に node 自体のビルドが走ります。

マシンスペックによりますが、初回ビルドは 1 時間以上かかることもあるので気長に待ちましょう。

ビルド後の node は保存されるので、 2 回目以降は一瞬です。

ファイルを埋め込んでアクセスしたいなどが無ければ Node 自体の Single Binary Application の仕組みを使う方ががおすすめです(これはこれで新しい node じゃないと無理ですが…)

以上。

おまけ

上記手順でシングルバイナリにしたモノを、以下リポジトリで配布しています。

mikoto2000/devcontainers-cli: Distributing a single executable binary for devcontainer/cli.

参考資料

変更履歴

日付 内容
2024/11/30-1 新規作成
2024/11/30-2 nexe の注意点について追記

macOS で Nodejs をビルドする

前提

  • OS: macOS Sequoia 15.1.1 (24B91)
  • Homebrew インストール済み

前回の macOS セットアップ記事の続きから。

Nodejs のクローン

git clone --depth 1 -b v23.3.0 https://github.com/nodejs/node

ビルド

cd node
./configure
make -j8

以上。

参考資料

2024年3月23日土曜日

Windows コンテナで Node.js アプリケーションをシングルバイナリにする

今回は、 Node.js アプリケーションのシングルバイナリ化をやってみる。

対象のアプリケーションは devcontainers/cli

nexe/nexeNode.js v21 の Single executable applications の 2 パターンでやってみる。

前提

  • OS: Windows 11 Pro 23H2 ビルド 22631.3155
  • Docker Desktop: Version 4.28.0 (139021)
  • 使用イメージ: mcr.microsoft.com/windows/servercore:ltsc2022

コンテナ起動

Node.js のビルドにはたくさんメモリが必要なので、 -m で明示的に指定。

docker run -it --rm --cpus 20 -m 16G -v "$(pwd):C:\dist" mcr.microsoft.com/windows/servercore:ltsc2022 powershell.exe

ビルド環境構築

開発ツール一式をインストール

Set-ExecutionPolicy Unrestricted -Force
iex ((New-Object System.Net.WebClient).DownloadString('https://boxstarter.org/bootstrapper.ps1'))
get-boxstarter -Force
Install-BoxstarterPackage https://raw.githubusercontent.com/nodejs/node/HEAD/tools/bootstrap/windows_boxstarter -DisableReboots
refreshenv

Visual Studio に必要なコンポーネントを追加

& 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\setup.exe' modify `
    --installPath 'C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools' `
    --add Microsoft.VisualStudio.Component.Windows11SDK.22621 `
    --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 `
    --add Microsoft.VisualStudio.Component.VC.Runtimes.x86.x64.Spectre `
    --quiet

Node.js のインストール

choco install nodejs

パス環境変数の更新

$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")

devcontainers/cli をシングルバイナリにしてみる

nexe/nexe で作る

git clone --depth 1 -b v0.58.0 https://github.com/devcontainers/cli devcontainers-cli
cd devcontainers-cli
npm i -g yarn
yarn
yarn compile-prod
npx nexe -i ./devcontainer.js -t windows-x64-21.7.1 -b -o /dist/devcontainer-windows-x64-21.7.1-nexe.exe

~\.nexe\21.7.1\out\Release\node.exe に nexe 向けの node.exe が配置される。 これを ~\.nexe\windows-x64-21.7.1 に移動し、 -b を外すことで、次回以降、このバイナリを利用してシングルバイナリを作るようになる。

mv ~\.nexe\21.7.1\out\Release\node.exe ~\.nexe\windows-x64-21.7.1

Node.js の Single executable applications で作る

# シングルバイナリに使うための node 実行ファイルをコピー
Copy-Item 'C:\Program Files\nodejs\node.exe' .\devcontainer-windows-x64-21.7.1-sea.exe

# sea 用の設定ファイル作成
Write-Output '{"main": "./dist/spec-node/devContainersSpecCLI.js", "output": "sea-prep.blob"}' | Out-File -Encoding ascii .\sea-config.json

# blob の作成
node --experimental-sea-config sea-config.json

# BLOB を注入し、シングルバイナリを作る
npx postject devcontainer-windows-x64-21.7.1-sea.exe NODE_SEA_BLOB sea-prep.blob `
    --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2

# コンテナの外にコピー
Copy-Item .\devcontainer-windows-x64-21.7.1-sea.exe \dist

参考資料

変更履歴

日付 内容
2024/3/23 新規作成
2024/4/2 nexe 用 node バイナリ格納場所の間違いを修正(~/nexe -> ~/.nexe)

2024年3月19日火曜日

Windows で Node.js をビルドする

Windows サンドボックスの起動

メイン PC に色々入れたくないので、今回は Windows サンドボックスで試す。

NodeBuildEnv.wsb を作成し、ダブルクリックで起動する。

NodeBuildEnv.wsb

<Configuration>
  <MemoryInMB>16384</MemoryInMB>
  <ClipboardRedirection>Enable</ClipboardRedirection>
  <VGpu>Disable</VGpu>
  <MappedFolders>
    <MappedFolder>
      <HostFolder>C:\Users\Public\Downloads</HostFolder>
      <SandboxFolder>C:\Users\WDAGUtilityAccount\Downloads</SandboxFolder>
      <ReadOnly>false</ReadOnly>
    </MappedFolder>
  </MappedFolders>
</Configuration>

Node.js のビルド環境構築

PowerShell を開発者権限で起動し、以下コマンドを実行。

Set-ExecutionPolicy Unrestricted -Force
iex ((New-Object System.Net.WebClient).DownloadString('https://boxstarter.org/bootstrapper.ps1'))
get-boxstarter -Force
Install-BoxstarterPackage https://raw.githubusercontent.com/nodejs/node/HEAD/tools/bootstrap/windows_boxstarter -DisableReboots
refreshenv

これだけでは、 Visual Studio のコンポーネント(ワークロード)のインストールまではしてくれないため、以下コマンドでビルドに必要なコンポーネントをインストールする。

& 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\setup.exe' modify `
    --installPath 'C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools' `
    --add Microsoft.VisualStudio.Component.Windows11SDK.22621 `
    --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 `
    --quiet

これで MSVC C++ Build ToolsWindows 11 SDK のインストールがされる。

Node.js のビルド

コードのクローン

git clone --depth=1 https://github.com/nodejs/node.git
cd node

ビルド

Windows 用にビルド用 bat ファイルがあるのでそれを叩く。

.\vcbuild.bat

ビルドに成功すると ./out/Release にビルド結果が出力されるので、 wsb ファイルで指定したディレクトリにコピーすれば受け渡せる。

Copy-Item -Recurse .\out\Release C:\Users\WDAGUtilityAccount\Downloads\

参考資料

2022年5月25日水曜日

Arch Linux に Tauri 開発環境を構築する

前提

  • OS: Arch Linux
    • GUI 最小構成でセットアップした直後
  • Node.js: 18.2.0
  • Rust: rustc 1.61.0 (fe5b13d68 2022-05-18)

前提パッケージのインストール

sudo pacman -S base-devel webkit2gtk
  • base-devel: Rust プログラムのビルドに必要
  • webkit2gtk: Tauri プログラムのビルドに必要

Node.js のインストール

curl -LO https://nodejs.org/dist/v18.2.0/node-v18.2.0-linux-x64.tar.xz
sudo tar xfv node-v18.2.0-linux-x64.tar.xz -C /opt/
export PATH=$PATH:/opt/node-v18.2.0-linux-x64/bin
echo 'export PATH=$PATH:/opt/node-v18.2.0-linux-x64/bin' >> ~/.bashrc

Rust のインストール

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
echo 'source $HOME/.cargo/env' >> ~/.bashrc
source $HOME/.cargo/env

scab-player の開発開始

mkdir ~/project
cd ~/project
git clone git@github.com:mikoto2000/scab-player
cd scab-player
npm install
npm run tauri dev

以上。

参考資料