20200527のNode.jsに関する記事は7件です。

プログラミング素人のIoTサービス開発 -ログイン認証-

はじめに

2020年3月からプログラミングの勉強を始めて、7月までにWebサービス制作に挑戦しているプログラミング素人の制作記録です。

自分の技術レベル

・ProgateでHTML/CSS、JavaScript、Node.JSを受けた
・Expressフレームワークでget通信とpost通信を扱えるようになってきた
・JSのPromeseの概念が少しだけ理解できてきた
・obnizを触ったことがある
 https://note.com/hiromae/n/n4850983a3f92

目標制作物&必要な技術ロードマップ

熱くなる季節に向けて、熱中症予防のための温度検知サービスの制作に取り組んでいます。

身近な問題をIoTで解決できないか考えてみた
https://note.com/hiromae/n/n0e4a88bb501c

■現在構想しているサービス像
制作のために今後身につける必要がある技術が山ほどあります。
2020-05-27_22h07_42.png

今回はサービスへのログイン認証サービスに触ってみました。

今回インプットしたもの

Firebase
https://firebase.google.com/?hl=ja

今回のアウトプット

emailとpwをexpressのWebサーバに渡し、Firebaseで認証成功したらログイン後ページに遷移させることができました。

http://hiromae-test-login.ga/

2020-05-27_21h56_43.png
2020-05-27_21h58_15.png

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link
      rel="stylesheet"
      href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css"
      integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk"
      crossorigin="anonymous"
    />
    <script
      src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js"
      integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI"
      crossorigin="anonymous"
    ></script>

    <title>Document</title>
  </head>
  <body>
    <div class="container py-5">
      <form method="POST" action="/login"  id="app">
        <div class="form-group">
          <label for="email">Email address</label>
          <input
            type="email"
            class="form-control"
            id="email"
            aria-describedby="emailHelp"
            name="email"
          />
          <small id="emailHelp" class="form-text text-muted"
            >We'll never share your email with anyone else.</small
          >
        </div>
        <div class="form-group">
          <label for="password">Password</label>
          <input
            type="password"
            class="form-control"
            id="password"
            name="password"
          />
        </div>
        <div class="form-group form-check">
          <input type="checkbox" class="form-check-input" id="exampleCheck1" />
          <label class="form-check-label" for="exampleCheck1"
            >Check me out</label
          >
        </div>
        <button type="submit" class="btn btn-primary">Submit</button>
      </form>
    </div>
  </body>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/vuelidate@0.7.4/dist/validators.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/vuelidate@0.7.4/dist/vuelidate.min.js"></script>

  <script>
    Vue.use(window.vuelidate.default);
    const { required, email } = window.validators;

    const app = new Vue({
      el: "#app",
      data: {
        title: "入力フォームバリデーション",
        email: "",
        password: "",
      },
      validations: {
        email: {
          required,
        },
      },
      methods: {
        submitForm() {
          console.log("submit");
        },
      },
    });
  </script>
</html>
const express = require("express");
const app = express();

//Firebaseモジュールの読み込み
const firebase = require("firebase/app");
require("firebase/auth");

//Firebaseの初期化
var firebaseConfig = {
  apiKey: "",
  authDomain: "",
  databaseURL: "",
  projectId: "",
  storageBucket: "",
  messagingSenderId: "",
  appId: "",
  measurementId: "",
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);

app.use(express.static("public"));
app.use(express.urlencoded({ extended: false }));

app.get("/", (req, res) => {
  res.render("index.ejs");
});

//ログインボタン押したときの処理
app.post("/login", (req, res) => {
  console.log(req.body.email);
  console.log(req.body.password);
  // ログイン処理の定義
  let handlerLogin = async function () {
    console.log("handlerLogin");
    try {
      const resSignInWithEmailAndPassword = await firebase
        .auth()
        .signInWithEmailAndPassword(req.body.email, req.body.password);
      console.log("↓resSignInWithEmailAndPasswordをconsole.log↓");
      console.log(resSignInWithEmailAndPassword.email);
      res.render("main.ejs");
    } catch (error) {
      console.log(error);
      const errorCode = error.code;
      const errorMessage = error.message;
      if (errorCode === "auth/wrong-password") {
        console.log("Wrong password.");
        res.render("pwWrong.ejs");
      } else {
        console.log(errorMessage);
      }
    }
  };
  handlerLogin();
});

app.listen(process.env.PORT || 8080);
console.log("server listen...");

振り返り

・これで認証したって言えるのだろうか・・・?
・認証後は確かセッション情報を保有してそれによってログイン状態を保つとか聞いたことあるけど・・・

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

JavaScript thisの挙動の不思議

thisの挙動や関数スコープのことについて。

実行環境 node v14.2.0

thisの挙動(通常関数)

function hoge() {
  console.log(this);
}

function foo() {
  "use strict";
  console.log(this);
}

hoge();  //globalオブジェクトが出力
foo();   //undefinedが出力

ほぼほぼ同じ関数ですが結果が変わってきます。
hoge関数は、globalに紐付けせずに実行してもglobalオブジェクトを参照し出力します。デフォルトでglobalオブジェクトが紐付けされているということです。

しかし、strictモードでは関数がどのオブジェクトにも紐付けされていなければ、この値は未定義になります。したがって、strictモードだとデフォルトでオブジェクトの紐付けは行われないということです。

thisの挙動(アロー関数)

console.log(this); //{} が出力
var hoge = () => {
  console.log(this);
};

var bar = () => {
  "use strict";
  console.log(this);
};

hoge(); //{} が出力
bar(); //{} が出力

ここでの結果は、globalオブジェクトを指すのではなく、関数実行時に内包している親の{}を示しています。

thisの挙動2(アロー関数)

  global.name = "Taro";
  let helloName = () => {
    console.log(`hello! ${this.name}`);
  };

  let Hanako = {
  name: "Hanako",
  hello: helloName,
  };

  helloName();    //hello!
  Hanako.hello(); //hello!

@shiracamus様にご指摘、コメントいただいたため、整理、修正中。
nodeの場合は上記のように"hello!"しか表示されません。内包している{}が表示されています。

しかし、ブラウザであれば"hello! Taro"と表示されます。つまり、globalオブジェクトのnameが呼び出されていることがわかります。
※ブラウザの場合は1行目のglobalをwindowに変更

thisはいろいろな場合で値が変わるようでややこしいです。

また、thisの挙動からは少し逸れますがこんなこともやってみます。global.nameをvar宣言に変えてみます。

thisの挙動3(アロー関数)

  var name = "Taro";
  let helloName = () => {
    console.log(`hello! ${this.name}`);
  };

  let Hanako = {
  name: "Hanako",
  hello: helloName,
  };

  helloName();    //hello!
  Hanako.hello(); //hello!

結果はブラウザ、node共に同じになります。
つまりvarもグローバル変数に値が格納されていることになります。

では次に、if文の中で宣言してみます。

thisの挙動4(アロー関数)

if(true) {
  var name = 'Taro';
}
let helloName = () => {
  console.log(`hello! ${this.name}`);
};

let Hanako = {
  name: "Hanako",
  hello: helloName,
};

helloName(); //hello!
Hanako.hello(); //hello!

nodeの場合は{}を示しているので何も表示されません。
しかし、ブラウザの場合は同じようにTaroが表示されます。if文の中で宣言してもglobal変数として宣言しているのでnameを使用することができます。

次に、実行する場所によってthisの示す値が異なる挙動をみていきます。
こちらは関数の実行する場所が異なることによる挙動の違いが見れます。
Hanakoの場合はHanakoオブジェクトの中で関数を実行しているのでHanakoが出力されます。

thisの挙動5(アロー関数)

var name="Taro"

let displayName=function(){
  console.log(this.name);
}

let hanako={
  "name":"Hanako",
  "display":displayName
}

displayName(); // nodeではundefined、 ブラウザはTaroと表示
hanako.display(); //Hanako

thisの値を制御してみます。
明示的にバインドしてthisが任意の物を指し示すようにします。

thisの挙動6(アロー関数)

var name="Taro"

let Hanako ={
  "name":"Hanako",
}

let displayName=function(){
  console.log(this.name);
}


displayName(); //Taro
displayName.apply(Hanako); //Hanako
displayName.call(Hanako); //Hanako

let bindDisplayName= displayName.bind(Hanako); //Hanako
  bindDisplayName();

thisの値を制御しました。

結果apply, call, bindDisplayNameで明示的にバインドしたものはHanakoを指し示すようになっています。

参考記事1
参考記事2

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

Javascriptで電話番号のバリデーションと変換(E.164)に便利なライブラリの紹介

今回したいこと

  • 日本の電話番号(03-1234-5678)などを、E.164表記(+81312345678)に変換したい
  • ついでにバリデーションも出来るといいかも

環境

  • Node.js

ライブラリ

Googleが提供している電話番号をバリデーションと自動フォーマット可能なlibphonenumberのnpm版であるgoogle-libphonenumberを使用します。

google-libphonenumberの使用方法

インストール

npm install google-libphonenumber

例1)'202-456-1414'を'US'のE164表記に変換

// Require `PhoneNumberFormat`.
const PNF = require('google-libphonenumber').PhoneNumberFormat;

// Get an instance of `PhoneNumberUtil`.
const phoneUtil = require('google-libphonenumber').PhoneNumberUtil.getInstance();

// Parse number with country code and keep raw input.
const number = phoneUtil.parseAndKeepRawInput('202-456-1414', 'US');

// Format number in the E164 format.
console.log(phoneUtil.format(number, PNF.E164));
// => +12024561414

例2)全角・半角混合の日本の電話番号をE164表記に変換

// Require `PhoneNumberFormat`.
const PNF = require('google-libphonenumber').PhoneNumberFormat;
// Get an instance of `PhoneNumberUtil`.
const phoneUtil = require('google-libphonenumber').PhoneNumberUtil.getInstance();

const phoneNumbers = [
        '03―1111―2222',
        '03 1111 2222',
        '(03)1111-2222',
        '0312345678',
        '03 1234 5678',
        '03-1234-5678',
        '(03)-1234-5678',
        '09012345678',
        '090-1234-5678',
        '09012345678',
        '090―1234―5678',
        '0120-123-123'
        ]
const replacedNumbers = phoneNumbers.map(phoneNumber => {
        const replacedNumber = formatNumber(phoneNumber);
        return replacedNumber;
    })

function formatNumber(str){
    const number = phoneUtil.parseAndKeepRawInput(str, 'JP');
    return phoneUtil.format(number, PNF.E164);
}

console.log(replacedNumbers);

// =>["+81311112222","+81311112222","+81311112222","+81312345678","+81312345678","+81312345678","+81312345678","+819012345678","+819012345678","+819012345678","+819012345678","+81120123123"]

その他の使い方

READMEをそのままのっけています。
バリデーションも出来そうです。

// Require `PhoneNumberFormat`.
const PNF = require('google-libphonenumber').PhoneNumberFormat;

// Get an instance of `PhoneNumberUtil`.
const phoneUtil = require('google-libphonenumber').PhoneNumberUtil.getInstance();

// Parse number with country code and keep raw input.
const number = phoneUtil.parseAndKeepRawInput('202-456-1414', 'US');

// Print the phone's country code.
console.log(number.getCountryCode());
// => 1

// Print the phone's national number.
console.log(number.getNationalNumber());
// => 2024561414

// Print the phone's extension.
console.log(number.getExtension());
// =>

// Print the phone's extension when compared to i18n.phonenumbers.CountryCodeSource.
console.log(number.getCountryCodeSource());
// => FROM_DEFAULT_COUNTRY

// Print the phone's italian leading zero.
console.log(number.getItalianLeadingZero());
// => false

// Print the phone's raw input.
console.log(number.getRawInput());
// => 202-456-1414

// Result from isPossibleNumber().
console.log(phoneUtil.isPossibleNumber(number));
// => true

// Result from isValidNumber().
console.log(phoneUtil.isValidNumber(number));
// => true

// Result from isValidNumberForRegion().
console.log(phoneUtil.isValidNumberForRegion(number, 'US'));
// => true

// Result from getRegionCodeForNumber().
console.log(phoneUtil.getRegionCodeForNumber(number));
// => US

// Result from getNumberType() when compared to i18n.phonenumbers.PhoneNumberType.
console.log(phoneUtil.getNumberType(number));
// => FIXED_LINE_OR_MOBILE

// Format number in the E164 format.
console.log(phoneUtil.format(number, PNF.E164));
// => +12024561414

// Format number in the original format.
console.log(phoneUtil.formatInOriginalFormat(number, 'US'));
// => (202) 456-1414

// Format number in the national format.
console.log(phoneUtil.format(number, PNF.NATIONAL));
// => (202) 456-1414

// Format number in the international format.
console.log(phoneUtil.format(number, PNF.INTERNATIONAL));
// => +1 202-456-1414

// Format number in the out-of-country format from US.
console.log(phoneUtil.formatOutOfCountryCallingNumber(number, 'US'));
// => 1 (202) 456-1414

// Format number in the out-of-country format from CH.
console.log(phoneUtil.formatOutOfCountryCallingNumber(number, 'CH'));
// => 00 1 202-456-1414

(おまけ)ライブラリを使わないでやってみた

最初はgoogle-libphonenumberを知らなかったので、正規表現でやってみました。

const phoneNumbers = [
        '03―1111―2222',
        '03 1111 2222',
        '(03)1111-2222',
        '0312345678',
        '03 1234 5678',
        '03-1234-5678',
        '(03)-1234-5678',
        '09012345678',
        '090-1234-5678',
        '09012345678',
        '090―1234―5678',
        '0120-123-123'
        ]

const replacedNumbers = phoneNumbers.map(phoneNumber => {
        const replacedNumber = toE164Number(phoneNumber);
        return replacedNumber;
    })


function toE164Number(strVal){
  // 半角変換
  const halfVal = strVal.replace(/[!-~]/g,
    function( tmpStr ) {
      // 文字コードをシフト
      return String.fromCharCode( tmpStr.charCodeAt(0) - 0xFEE0 );
    }
  );
  console.log("halfVal" ,halfVal);
  // 文字の変換
  const number = phoneUtil.parseAndKeepRawInput(halfVal, 'JP');
  console.log(number)
  console.log(phoneUtil.format(number, PNF.E164));
  return halfVal.replace(/[\-\s+()-―]/gi, '').replace('0','+81');
}

最後に

google-libphonenumberはかなり便利だなと思いました。
ただ電話番号を変換するときにJPなどの国コードを指定しないといけないので、素の電話番号形式から国コードを判定するものがあればいいんですけどね。。

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

PuppeteerのBrowserFetcherクラス

今回はPuppeteerのBrowserFetcherクラスについて書いていきたいと思います。

BrowserFetcherクラス

BrowserFetcherはChromiumとFirefoxの異なるバージョンをダウンロードして、管理することができます。

BrowserFetcherはChromiumのバージョンを("533271"にたいな)指定するrevision string上で操作します。revision stringはomahaproxy.appspot.comから得ることができます。と、言いたいところなのですが、確認したところ、存在しないものが多いので、こちらの方を見てください。

Firefoxの場合、BrowserFetcherはFirefox Nightlyをダウンロードして、("75"にたいな)バージョン番号を操作します。Firefox Nightlyのホストはこちらを見てください。

canDownloadメソッド

revisionが利用可能かどうかを確かめるメソッド。

ホストからダウンロード可能であれば、trueを返してくれます。

例(createBrowserFetcherの設定によって結果が変わります):

const puppeteer = require('puppeteer');

async function checkRevision(revision) {
  const browserFetcher = puppeteer.createBrowserFetcher();
  const isAvailable = await browserFetcher.canDownload(revision);

  if(isAvailable) {
    console.info('This revision is available.');
  } else {
    console.info('This revision isn\'t available.');
  }
}

checkRevision('756066');  // win64
checkRevision('1000583'); // This may release from now on but this isn't release at the moment.
checkRevision('78.0a1'); // If this product is Firefox, this result is true.

結果:

This revision is available.
This revision isn't available.
This revision isn't available.

downloadメソッド

ChromiumもしくはFirefox Nightlyをダウンロードするメソッド。

browserFetcher.download(revision[, progressCallback])

revision         string ダウンロードするためのrevision
progressCallback function(number, number)
    downloadedBytes number ダウンロードされたバイト数。
    totalBytes      number 合計ダウンロードバイト数。

簡単な例:

const puppeteer = require('puppeteer');

(async () => {
  const browserFetcher = puppeteer.createBrowserFetcher();
  await browserFetcher.download('771731', (downloadedBytes, totalBytes) => {
    // any code...
  });
})();

hostメソッド

使用されているダウンロードホストを返すメソッドです。

localRevisionsメソッド

ディスク上でローカルに利用可能な(現在のproductの)すべてのrevisionのリストを返すメソッド。

platformメソッド

maclinuxwin32、またはwin64のいずれかを返すメソッド。

productメソッド

chromeもしくはfirefoxのいずれかを返すメソッド。

removeメソッド

ダウンロードされたChromiumもしくはfiredfoxをrevisionを指定して削除するメソッド。

revisionInfoメソッド

revisionを指定してそのrevisionの情報を出すメソッド。

サンプル

ローカルにはrevision:389298と771731がダウンロード済み。

const puppeteer = require('puppeteer');

(async () => {
  const browserFetcher = puppeteer.createBrowserFetcher();

  console.info('host: ' + await browserFetcher.host());

  await (browserFetcher.localRevisions()).then(revisions => console.info(revisions));

  console.info('platform: ' + await browserFetcher.platform());
  console.info('product: ' + await browserFetcher.product());

  const info1 = await browserFetcher.revisionInfo('389298');
  const info2 = await browserFetcher.revisionInfo('771731');
  const info3 = await browserFetcher.revisionInfo('385798');

  console.info(info1);
  console.info(info2);
  console.info(info3);
})();

結果:

[ '389298', '771731' ]
platform: win64
product: chrome

{ revision: '389298',
  executablePath:
   'path\\node_modules\\puppeteer\\.local-chromium\\win64-389298\\chrome-win32\\chrome.exe',
  folderPath:
   'path\\node_modules\\puppeteer\\.local-chromium\\win64-389298',
  local: true,
  url:
   'https://storage.googleapis.com/chromium-browser-snapshots/Win_x64/389298/chrome-win32.zip',
  product: 'chrome' }

{ revision: '771731',
  executablePath:
   'path\\node_modules\\puppeteer\\.local-chromium\\win64-771731\\chrome-win\\chrome.exe',
  folderPath:
   'pathp\\node_modules\\puppeteer\\.local-chromium\\win64-771731',
  local: true,
  url:
   'https://storage.googleapis.com/chromium-browser-snapshots/Win_x64/771731/chrome-win.zip',
  product: 'chrome' }

{ revision: '385798',
  executablePath:
   'path\\node_modules\\puppeteer\\.local-chromium\\win64-385798\\chrome-win32\\chrome.exe',
  folderPath:
   'path\\node_modules\\puppeteer\\.local-chromium\\win64-385798',
  local: false,
  url:
   'https://storage.googleapis.com/chromium-browser-snapshots/Win_x64/385798/chrome-win32.zip',
  product: 'chrome' }

さいごに

次はBrowserクラスをやっていきたいと思います。

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

[保存版]最新Node.js for Raspberry Pi Zero / 1 (ARMv6向け)

ARMv6のサポート終了

2019年4月頃、Node.jsのVer.12のリリースとともにARMv6のビルドが提供されなくなりました。ARMv6を使うデバイスはだいぶ少なくなりましたが、ここにありますよ!Zero WHが!!
RFC: Consider retiring the PI1s ARMv6 (downgrading support to "experimental") · Issue #1677 · nodejs/build · GitHub
どうやら非公式ビルドというものを用意してくれているようです。ありがたや。

Node.js unofficial-builds project

Node.js Unofficial Builds Project
GitHub - nodejs/unofficial-builds: Unofficial binaries for Node.js
非公式ビルドは
https://unofficial-builds.nodejs.org/download/release/v14.3.0/
以下にあるようです。最新ビルドを見ると公式と比べて数時間差でしょうか。誤差ですね!

手動でインストール

まず初めに公式最新リリースで最新バージョンを確認 (もしかしたら都合悪く最新バージョンがビルドされていないかもしれないので、非公式Releaseを要確認。)

VERSION=v14.3.0
DISTRO=linux-armv6l
wget https://unofficial-builds.nodejs.org/download/release/$VERSION/node-$VERSION-$DISTRO.tar.xz

意外と時間かかります。終わったら、解凍。

sudo mkdir -p /usr/local/lib/nodejs
sudo tar -xJvf node-$VERSION-$DISTRO.tar.xz -C /usr/local/lib/nodejs

適当なエディタで~/.profileに追記

~/.profile
# Nodejs
VERSION=v14.3.0
DISTRO=linux-armv6l
export PATH=/usr/local/lib/nodejs/node-$VERSION-$DISTRO/bin:$PATH

.profileの再読み込み

. ~/.profile

これで完了。
参照: Installation · nodejs/help Wiki · GitHub

アップデートの仕方

毎回これやるのはダルいし、作業ゲーがアホらしいのでアップデート方法の模索。
ちょっと調べるとnというパッケージがあるが、最新バージョンを聞くとやっぱり公式ビルドの11.15.0を返す始末。当たり前だが。
となると自分で作るしかない。うん、そうしよう。

update-nodejs.sh
#!/bin/sh

. ~/.profile
old=`node -v`
echo "現在のNode.jsのバージョンは$oldです"
ver=''

if [ $# -eq 1 ]; then
  ver="v$1"
  if [ $ver = $old ]; then
    echo "Node.js($ver)が既にインストールされています"
    return 0
  fi
else
  ver=`wget -qO- https://nodejs.org/dist/latest/ | sed -nE 's|.*>node-(.*)\.pkg</a>.*|\1|p'`
  if [ $ver = $old ]; then
    echo "Node.js($ver)は最新です"
    return 0
  fi
fi

wget https://unofficial-builds.nodejs.org/download/release/$ver/node-$ver-$DISTRO.tar.xz
if [ ! -e node-$ver-$DISTRO.tar.xz ]; then
  return 1
fi

if [ -d /usr/local/lib/nodejs ]; then
  sudo rm -r /usr/local/lib/nodejs
fi
sudo mkdir -p /usr/local/lib/nodejs
sudo tar -xJvf node-$ver-$DISTRO.tar.xz -C /usr/local/lib/nodejs
sed -i -e "s/VERSION=$VERSION/VERSION=$ver/g" ~/.profile

rm node-$ver-$DISTRO.tar.xz
. ~/.profile

echo "Node.js: $old -> `node -v`"

これを実行するだけで常にlatestに更新されます。LTSとかは考慮してませんorz

source update-nodejs.sh

引数にバージョンを入れると無理やりそのバージョンに移行します。が、非公式ビルドにあれば、です。

source update-nodejs.sh 14.0.0

定期実行させる

どんなバグでも仕様変更でもかかってこいって人はCronに入れるとよいでしょう。ただ頻度はほどほどに。
中でsudoとか使ってたり環境変数変えてたりして割と雑なスクリプトなのでCronでの動作保証はしません。というかシェルスクリプト書いたの数回目なのでツッコミ大歓迎です。Cronは試してみたら追記します。

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

NestJSとGraphQLで開発用の環境を作成する

NestJS と GraphQL で開発用の環境を作成する

目次

  1. NestJS のアプリケーションを作成
  2. GraphQL の依存関係を構築する
  3. mysql(docker)を用意する
  4. mysql の依存関係を構築する

1. NestJS のアプリケーションを作成

NestCliを用いて NestJS のアプリケーションを構築する。

cli を install していない時は、上の URL から cli を install してアプリケーションを作成します。

$ nest new nestjs-graphql
$ npm run start:dev

localhost:3000で下記画面が表示されることを確認します。

localhost.png

2. GraphQL の依存関係を構築する

Qiita 記事で記載しましたが、依存関係を解決していきます。

GraphQL を利用するのに必要な 4 つをインストールしていきます。

$ npm i --save @nestjs/graphql graphql-tools graphql apollo-server-express

library の型定義で error がはかれないように、tsconfig.json を編集します。

tsconfig.json
{
  "compilerOptions": {
    "module": "commonjs",
    "declaration": true,
    "removeComments": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "target": "es2017",
    "sourceMap": true,
    "outDir": "./dist",
    "baseUrl": "./",
    "incremental": true,
    //ここから追記
    "skipLibCheck": true
  //ここまで追記
  },
  "exclude": ["node_modules", "dist"]
}

公式ドキュメントをもとに GraphQL の依存関係を app.module に定義していきます。

src/app.module.ts
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { join } from 'path';

@Module({
  imports: [
    GraphQLModule.forRoot({
      debug: false,
      playground: true,
      typePaths: ['./**/*.graphql'],
      definitions: {
        path: join(process.cwd(), 'src/graphql.ts'),
        outputAs: 'class',
      },
    }),
  ],
})
export class AppModule {}

サーバーを起動し、play groundにアクセスして paly groud が表示されることを確認します。

$ npm run start:dev

下記のように Play Ground が表示されます。

playgorund.png

3. mysql(docker)を用意する

docker-compose.ymlDockerfileを追加していきます。

$ touch docker-compose.yml
$ touch Dockerfile

現状のディレクトリ構成は、以下になる想定です。

$ tree -L 1 -I node_modules
.
├── Dockerfile
├── README.md
├── dist
├── docker-compose.yml
├── nest-cli.json
├── package-lock.json
├── package.json
├── src
├── test
├── tsconfig.build.json
└── tsconfig.json

MySQL のみの docker を作成するので、起動に必要な設定を作成していきます。

FROM mysql:5.7
docker-compose.yml
version: '3.7'
services:
  db:
    build: .
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    tty: true
    ports:
      - '3306:3306'
    environment:
      MYSQL_ROOT_PASSWORD: example
      MYSQL_DATABASE: develop

最後に docker を起動して正常に動作するか確認します。

$ docker-compose up
$ docker ps -a

4. mysql の依存関係を構築する

公式ドキュメントを目安に依存関係を構築していきます。

$ npm install --save @nestjs/typeorm typeorm mysql

上記のインストールが完了したら、app.module に必要な設定を記載していきます。お行儀が悪いですが、とりあえず動くように設定を記載していきます。

src/app.module.ts
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { TypeOrmModule } from '@nestjs/typeorm';
import { join } from 'path';

@Module({
  imports: [
    GraphQLModule.forRoot({
      typePaths: ['./**/*.graphql'],
      definitions: {
        path: join(process.cwd(), 'src/models/graphql.ts'),
        outputAs: 'class',
      },
    }),
    TypeOrmModule.forRootAsync({
      useFactory: () => ({
        type: 'mysql',
        host: 'localhost',
        port: 3306,
        username: 'root',
        password: 'example',
        database: 'develop',
        entities: [__dirname + '/**/*.entity{.ts,.js}'],
        synchronize: true,
      }),
    }),
  ],
})
export class AppModule {}

上記で簡易的な設定が完了したと思うので、あとは適宜 GraphQL を記載していけば実装できます。

レポ

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

WSL 2 デビューするときの初期設定

はじめに

こんにちは。今回 MacBook Pro を新調したのですが、最近 Windows の最新動向が気になるので、 Windows も使えるようにしたいと思い、Bootcamp をセットアップすることにしました。その際、とても詳しくて役に立ったのはこの記事です :heart_eyes:

https://ascii.jp/elem/000/004/011/4011143/

Mac で Windowsが使えるようになったその勢いで、Windows Insider Program にも登録し、話題の WSL 2 をいち早く試すことにしました :v:

今回は MacBook Pro 13 (macOS 10.15.4) + Bootcamp + Windows 10 Home の組み合わせですが、純正の Windows 端末でもやることは同じだと思いますのでご参考になさってください。

今回のゴール

WSL 2 で VS Code を起動し、SFDX コマンドを実行するまでをゴールとします :golf:

最初にやること

Windows にログインし、WSL 2 をインストールします。管理者モードの Powershell からコマンドをいくつか実行するだけです。

dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart
wsl --set-default-version 2

その後、Ubuntu の LTS をインストールして、Linux の世界へ Go :airplane:

https://docs.microsoft.com/ja-jp/windows/wsl/install-win10

ターミナルのプロンプトを整える

ターミナルのデザインは大事ですね。コーディングするときのモチベーションにかかわります。

私は時刻や改行を入れたい派なので、次の例のように ~/.bashrc~/.bash_profile を作成します。

~/.bashrc
export PS1=$'\n''\D{%Y.%m.%d} \A $ \W\[\033[31m\]\[\033[00m\]'$'\n> '
~/.bash_profile
test -r ~/.bashrc && . ~/.bashrc

開発環境を整える

余談ですが、どうやら Git は最初から Linux 内にインストールされている模様。

wsl
git --version

image.png

Node.js

Node.js LTS のインストールには nvm がおすすめ。

https://docs.microsoft.com/ja-jp/windows/nodejs/setup-on-wsl2

sudo apt-get install curl
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash
nvm install --lts
nvm ls
node -v
npm -v

image.png

Salesforce CLI

npm 経由で Salesforce CLI をインストールします。

npm install --global sfdx-cli
sfdx force

image.png

Visual Studio Code

VS Code と WSL 用の拡張機能をインストールします。Linux 内ではなく Windows 内にインストールする点に注意です。

結果発表

VS Code を起動 :point_right: 左隅の >< の緑色アイコンをクリック :point_right: Remote-WSL: New Window をクリックしましょう。

image.png

左隅に WSL: Ubuntu が光ってること、ターミナルが 1: bash になってることを確認できますか? sfdx コマンドを実行できたら無事ゴールです :tada:

image.png

おまけ

エクスプローラーで見たいときは、アドレス欄に \\wsl$\Ubuntu\home\takahito などと入力します。

image.png

これで開発生産性がますます向上しそうです。それではまた。 Happy Coding!!

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