LoginSignup
1
1

More than 1 year has passed since last update.

Next.jsとPostgresを使って郵便番号検索フォームを作成

Last updated at Posted at 2021-05-11

前回はデータベースに全国郵便番号データをCSV形式で入れる事ができたので、今回は実際にサーバーで郵便番号が検索できるフォームを作成します。前回の記事でPostgres(データベース)を確認してみるとencoding collate ctypeがen_US.UTF-8になっていて、ログ確認時にブラウザで文字化けを起こしている挙動を得ました。なので、新たにaddressというデータベースを作りエンコーディングをja_JP.UTF-8に書き換えることにしました。

create database address template template0 ENCODING = 'UTF8' LC_COLLATE = 'ja_JP.UTF-8' LC_CTYPE = 'ja_JP.UTF-8';

同時にテーブルも作り直し、以下の通り作成し直しました。

                Table "public.address"
     Column     |       Type        | Collation | Nullable | Default 
----------------+-------------------+-----------+----------+---------
 post           | character(21)     |           | not null | 
 prefectures    | character varying |           | not null | 
 municipalities | character varying |           | not null | 
 address        | character varying |           | not null | 

\COPYでCSVファイルをデータベースに保存し、select * from address;で確認。

post          | prefectures | municipalities     | address                                    
--------------+-------------+--------------------+----------------------------------------------
 600000       | 北海道       | 札幌市中央区         | 以下に掲載がない場合
 640941       | 北海道       | 札幌市中央区         | 旭ケ丘
 600041       | 北海道       | 札幌市中央区         | 大通東
 600042       | 北海道       | 札幌市中央区         | 大通西(1~19丁目)
 640820       | 北海道       | 札幌市中央区         | 大通西(20~28丁目)
 600031       | 北海道       | 札幌市中央区         | 北一条東
 600001       | 北海道       | 札幌市中央区         | 北一条西(1~19丁目)
 640821       | 北海道       | 札幌市中央区         | 北一条西(20~28丁目)
:

結局、ブラウザ確認時の文字化けは解消されずでしたが。細かな検証は今後の課題として、進んで行きたいと思います。今回は、React(JavaScriptのライブラリ)のSSR(サイバーサイドレンダリング)を可能にするJavaScriptのフレームワークであるNext.jsを導入し実装していきたいと思います。node.jsがインストールされていない方はこちらをインストールして下さい。チュートリアルに従って、ターミナルから次のコマンドを実行します。

npx create-next-app nextjs-blog --use-npm --example "https://github.com/vercel/next-learn-starter/tree/master/learn-starter"

ディレクトリであるNESTJS-BLOGが作成され、各パッケージが入りました。
スクリーンショット 2021-05-08 13.36.20.png
続けてIDE(統合開発環境)を整えます。開発時からエラーに気づけるように設定を書く為、タッチコマンドでtsconfig.jsonを入れ、npmやtypescriptなどのパッケージをsave-devの方にインストール。一度npm run devを実行します。

touch tsconfig.json
npm install --save-dev typescript @types/react
npm run dev

の準にターミナルで実行すると、以下の画面になり、
スクリーンショット 2021-05-08 14.03.23.png
http://localhost:3000で確認すると、無事ローカル上で挙動を確認できました。
スクリーンショット 2021-05-08 14.12.18.png
ここまで来たら、いよいよ本題であるページのレンダリング(データをもとに表示内容を作る)やルーティング(pagesディレクトリ内のファイルの結び付け)、Reactフック(クラスを作成せずに、状態やその他のReact機能を使用を可能にするもの)を使ってコードを書いていきましょう。pagesディレクトリ内にapiディレクトリを作ってaddress.tsファイルを作成し記述していきます。まずはSequelizeを使用してデータベースを呼び出します。Sequelizeとは、Node.js上でORMを提供するライブラリです。以下のコマンドでインストールしましょう。

npm install --save sequelize

構文・継承を用いたclass 子クラス extends 親クラスでAddressとModelを定義し、データベースのテーブルに文字列で値を渡します。init()内に各テーブルの要素を入れます。Sequelizeでデータベースのカラムを変更せずModelを定義するために、主キーに対応するpost(カラム)にprimaryKey:trueを追加。createdAt,updatedAtを追加しないfreezeTableName:truetimestamps:falseを利用してテーブルのスキーマを現状の形で使えるようにします。郵便番号データをasyncで以下のように記述。

pages/api/address.ts
import { Sequelize, Model, DataTypes } from 'sequelize';

const sequelize = new Sequelize({
  database: 'address',
  password: null,
  dialect: 'postgres',
  port: 5432,
  host: 'localhost',
  timezone: 'Asia/Tokyo',
});


class Address extends Model {}
  Address.init({
    post: {type:DataTypes.STRING,primaryKey:true},
    prefectures: DataTypes.STRING,
    municipalities: DataTypes.STRING,
    address: DataTypes.STRING
  }, { sequelize, modelName: 'address',freezeTableName:true,timestamps:false });

export default async(req, res) => {
  await sequelize.sync();
  console.log(await Address.count());
  const address = await Address.findOne({
    where: {
     post: req.query['post'],
    }
  });
  console.log(address)
  res.status(200).send(JSON.stringify(address))
}

pagesディレクトリ内にあるindex.jsファイルindex.tsxに書き換えてコードを以下のように記述します。

pages/index.tsx
import Head from 'next/head'
import React, { Props } from "react";

const Home = (props: Props) => {
  const [value, setValue] = React.useState("");
  const [prefectures, setPrefectures] = React.useState("");
  const [municipalities, setMunicipalities] = React.useState("");
  const [address, setAddress] = React.useState("");

  React.useEffect(()=>{
    fetch('/api/address?post='+value)
    .then(response => response.json())
    .then(data => {
      if(data !== null){ 
        setPrefectures(data.prefectures)
        setMunicipalities(data.municipalities)
        setAddress(data.address)}});
  },[value])

  return (
    <div>
      <h3>郵便番号を入力して下さい</h3>
      <label style={{ display: "block" }}> 
        <input type='text'
          placeholder="半角数字で入力"
          maxLength={8}
          size={14}
          value={value}
          onChange={event => setValue(event.target.value.replace("-",""))} />
      </label>
      {prefectures!=""&& <h3>住所</h3>}
      {prefectures!=""&& 

      <div><label style={{ display: "block" }}> 
          <input type='text'
          value={prefectures}
          onChange={event => setValue(event.target.value)} /><br/>
       </label>
          <label style={{ display: "block" }}> 
          <input type='text'
          value={municipalities}
          onChange={event => setValue(event.target.value)} /><br/>
       </label>
       <label style={{ display: "block" }}> 
          <input type='text'
          value={address}
          onChange={event => setValue(event.target.value)} /><br/>
          </label>
      </div>}
    </div>
  )
}

export default Home;

ターミナルで実行してみます。

nmp run dev

スクリーンショット 2021-05-08 17.48.12.png
http://localhost:3000/にて挙動を検証。
スクリーンショット 2021-05-08 17.53.00.png
郵便番号を入力してみます!
スクリーンショット 2021-05-08 17.53.20.png
なんとか、それらしきものが出来上がりました。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1