3
2

【初心者OK】簡単なAPIテストアプリを作ってみよう

Last updated at Posted at 2023-11-12

api-test5.png

初心者でも簡単に作れるので、これからバックエンドの実装を勉強したい方でもおすすめです。
手順としては以下のように進めます。

  1. 新しくフォルダを作成する
  2. アプリケーションの動作環境を用意
  3. backendディレクトリにファイルを用意
  4. frontendディレクトリにファイルを作成・実装
  5. backendディレクトリにファイルを作成・実装

できる限り丁寧に説明しますので、手順通りに進めていけば実装できます。

新しくフォルダを作成する

まずは、新しくフォルダを作成し、今回のアプリの名前をつけてください。
名前は何でも良いですが、迷ったら「api-test-app」としてください。

次にVisual Studio Code(以下、VSCode)で作成したフォルダを開いてください。
以下の画面になるはずです。
api-test1.png

すでにフォルダが作成されていますが、後々このようになるので、気にしなくて良いです。
(この画面では、aws-test-appになってますが、僕がAWSを使ってapiテストをしていたためです)

最後に「backend」というフォルダを作成して手順1は完了です。

アプリケーションの動作環境を用意

前提
ここから先はNode.jsがインストールされている環境で利用できます。
もし、インストールされていない場合はNode.jsを先にインストールしてください。

VSCodeでキーボード「command + J」をするとターミナルが起動し、以下の記事を参考に進めてください。

対話形式で作業が進むので、以下のように回答してください。

bash.sh
Need to install the following packages:
  create-next-app@(バージョン)
Ok to proceed? (y) y

? What is your project named? › frontend

? Would you like to use TypeScript with this project? › Yes

? Would you like to use ESLint with this project? › Yes

✔ Would you like to use Tailwind CSS with this project? … Yes

✔ Would you like to use `src/` directory with this project? … Yes

✔ Use App Router (recommended)? … No

✔ Would you like to customize the default import alias? … No

インストールに時間がかかるので、しばらく待ちましょう。
完了すると先ほどの画像のようにフォルダが表示され、手順2が完了です。

backendディレクトリにファイルを用意

先ほどのインストールで使ったターミナルで以下のコマンドを入力し、backendディレクトリに移動しましょう。

bash.sh
cd backend

以下のようにapi-test-appからbackendに変わっていたら、移動できています。

bash.sh
backend % 

ここで以下を入力して、package.jsonを作成します。

bash.sh
npm init -y

package.jsonはNode.jsベースのJavaScriptアプリ開発で、プロジェクトファイルを管理するためのファイルと思ってください。

package.json
{
  "name": "backend",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

上記のファイルが確認できたら、以下のコマンドを実行しましょう。

bash.sh
npm install express
npm install typescript -D

ここでは、Node.jsのWebアプリケーションフレームワークであるExpressをインストールします。
Expressがあると、JabaScriptでサーバーサイド開発がやりやすくなります。

次にTypeScriptをインストールします。
「-D」は開発のときのみ利用することを示すと思っていてください。

これだけでは、Node.jsやExpressについての型定義ファイルがないのでエラーが発生します。
そのため、以下もインストールします。

bash.sh
npm install @types/node -D
npm install @types/express -D

次に、TypeScriptを利用できるようにするためにtsconfig.jsonファイルを作成します。
以下のコマンドを実行してください。

bash.sh
npx tsc --init

TypeScriptをJavaScriptにどのようにコンパイルするかなどを設定できますが、今回は初期状態のまま進めます。

これで、手順3が完了です。

frontendディレクトリにファイルを作成・実装

手順3はバックエンドの作業ですが、ここからはフロントエンドの作業に移ります。

まずは、srcディレクトリにcomponentsディレクトリを作成しましょう。

api-test2.png

背景色を変えたいので、以下のようにしましょう。

globals.css
@tailwind base;
@tailwind components;
@tailwind utilities;

body {
    background-color: #e1e1e1;
}

次に、componentsディレクトリで以下のディレクトリを作成してください。

  • plusComponent
  • minusComponent
  • multipleComponent
  • devideComponent

各ディレクトリの直下にindex.tsxファイルを作成すると、以下の画面となります。

api-test3.png

まずは、plusComponentのindex.tsxを編集します。
「rafce」と入力すると、自動で雛形を作成できますので、入力したらEnterを押下してください。
そうすると、以下が入力されます。

index.tsx
import React from 'react';

const index = () => {
  return (
    <div>index</div>
  )
};

export default index;

これを以下に書き換えてください。

index.tsx
import React from 'react';

const PlusComponent = () => {
  return (
    <div>PlusComponent</div>
  )
};

export default PlusComponent;

次に、pagesディレクトリにindex.tsxがあるので、デフォルトで記入されている内容を全て消去し、書き換えます。

index.tsx
import React, { useState } from "react";

//Component
import PlusComponent from "@/components/plusComponent";

export default function Home() {

  return (
    <>    
    <header className="h-14 w-full">
      <h1 className="text-3xl font-bold text-center h-full items-center justify-center flex">
        四則演算アプリ
      </h1>
    </header>
    <PlusComponent />
    </>
  );
};

ターミナルをfrontend(以下参照)にして、コマンドを入力しましょう。

bash.sh
frontend % npm run dev

しばらくすると、http://localhost:3000 が立ち上がるので、ブラウザにURLを入力してください。
「PlusComponent」という文字が確認できたら成功です。

キーボード「ctrl + C」でプロセスを終了しましょう。

アプリを確認すれば理解できると思いますが、本アプリは四則演算ができます。
その処理をフロントエンドとバックエンドのどちらか一方に切り替えられるアプリです。

この手順では、フロントエンドでの四則演算の処理を実装します。

まずは、ユーザーが数字を指定できるようにテキストフィールドを用意します。
以下のように書いてください。

index.tsx
    ...省略
    
  return (
    <section className='mt-8 px-8'>
        <h2 className='mb-2'>足し算</h2>
        <div className='flex'>
            <input 
            className='rounded p-2'
            type="number" 
            name="足し算1" 
            />
            <p className='mx-4 flex items-center justify-center w-3'>+</p>
            <input 
            className='rounded p-2'
            type="number" 
            name="足し算2" 
            />
            <p className='mx-4 flex items-center'>=</p>
            <p className='flex items-center'>0</p>
        </div>
    </section>
  );

これで、テキストフィールドと答えを出力する部分が表示できました。
npm run devでどのように表示されているかを確認できます。

次に、useStateを使って値を管理できるようにします。

index.tsx
import React, { useState, useEffect } from 'react';

const PlusComponent = () => {
  const [value1, setValue1] = useState<string>("");
  const [value2, setValue2] = useState<string>("");
  const [result, setResult] = useState<string>("");

  ...省略
}

入力された値になるように更新する必要があるので、以下を追加します。

index.tsx
const PlusComponent = () => {

    ...省略
    
    const changeValue1 = (e: React.ChangeEvent<HTMLInputElement>) => setValue1(e.target.value);
    const changeValue2 = (e: React.ChangeEvent<HTMLInputElement>) => setValue2(e.target.value);

    ...省略
}

実際に入力された値を出力するために、以下のように修正しましょう。

index.tsx
    ...省略
    
  return (
    <section className='mt-8 px-8'>
        <h2 className='mb-2'>足し算</h2>
        <div className='flex'>
            <input 
            className='rounded p-2'
            type="number" 
            name="足し算1" 
            onChange={(e) => changeValue1(e)} //追加
            value={value1} //追加
            />
            <p className='mx-4 flex items-center justify-center w-3'>+</p>
            <input 
            className='rounded p-2'
            type="number" 
            name="足し算2" 
            onChange={(e) => changeValue2(e)} //追加
            value={value2} //追加
            />
            <p className='mx-4 flex items-center'>=</p>
            <p className='flex items-center'>{result}</p> //修正
        </div>
    </section>
  );

changeValue2の下にvaluesSend関数を作成し、以下のように書きます。

index.tsx
  ...省略
  
  const calculate = () => {
      const numValue1 = Number(value1);
      const numValue2 = Number(value2);
    
      const result: number = numValue1 + numValue2;
      setResult(String(result));
  };

  ...省略

入力された値はString型なのでこのままでは計算できません。
そのため、一旦Number型にキャストし、計算後はString型に戻しています。

入力の度に計算する必要があるので、useEffectで実装します。

index.tsx
  useEffect(() => {
    calculate();
  }, [value1, value2]);

このように書くことで、value1とvalue2に新しい値が代入される度に計算処理(calculate)が入ります。
これでフロントエンドで足し算ができるようになりました。

残りの引き算(minusComponent)、かけ算(multipleComponent)、割り算(divideComponent)も同じように実装できるので、チャレンジしてみてください。

ここまでのコードはこちら。

足し算のコード
index.tsx
import React, { useState, useEffect } from 'react';
const PlusComponent = () => {
  const [value1, setValue1] = useState<string>("");
  const [value2, setValue2] = useState<string>("");
  const [result, setResult] = useState<string>("");

  const changeValue1 = (e: React.ChangeEvent<HTMLInputElement>) => setValue1(e.target.value);
  const changeValue2 = (e: React.ChangeEvent<HTMLInputElement>) => setValue2(e.target.value);

  const calculate = async () => {
  
  const numValue1 = Number(value1);
  const numValue2 = Number(value2);

  const result: number = numValue1 + numValue2;
  setResult(String(result));

  useEffect(() => {
    calculate();
  }, [value1, value2]);

  return (
    <section className='mt-8 px-8'>
        <h2 className='mb-2'>足し算</h2>
        <div className='flex'>
            <input 
            className='rounded p-2'
            type="number" 
            name="足し算1" 
            onChange={(e) => changeValue1(e)}
            value={value1}
            />
            <p className='mx-4 flex items-center justify-center w-3'>+</p>
            <input 
            className='rounded p-2'
            type="number" 
            name="足し算2" 
            onChange={(e) => changeValue2(e)}
            value={value2}
            />
            <p className='mx-4 flex items-center'>=</p>
            <p className='flex items-center'>{result}</p>
        </div>
    </section>
  );
};

export default PlusComponent;
引き算のコード
index.tsx
import React, { useState, useEffect } from 'react';
const MinusComponent = () => {
  const [value1, setValue1] = useState<string>("");
  const [value2, setValue2] = useState<string>("");
  const [result, setResult] = useState<string>("");

  const changeValue1 = (e: React.ChangeEvent<HTMLInputElement>) => setValue1(e.target.value);
  const changeValue2 = (e: React.ChangeEvent<HTMLInputElement>) => setValue2(e.target.value);

  const calculate = async () => {
  
  const numValue1 = Number(value1);
  const numValue2 = Number(value2);

  const result: number = numValue1 - numValue2;
  setResult(String(result));

  useEffect(() => {
    calculate();
  }, [value1, value2]);

  return (
    <section className='mt-8 px-8'>
        <h2 className='mb-2'>引き算</h2>
        <div className='flex'>
            <input 
            className='rounded p-2'
            type="number" 
            name="引き算1" 
            onChange={(e) => changeValue1(e)}
            value={value1}
            />
            <p className='mx-4 flex items-center justify-center w-3'>-</p>
            <input 
            className='rounded p-2'
            type="number" 
            name="引き算2" 
            onChange={(e) => changeValue2(e)}
            value={value2}
            />
            <p className='mx-4 flex items-center'>=</p>
            <p className='flex items-center'>{result}</p>
        </div>
    </section>
  );
};

export default MinusComponent;
かけ算のコード
index.tsx
import React, { useState, useEffect } from 'react';
const MultipleComponent = () => {
  const [value1, setValue1] = useState<string>("");
  const [value2, setValue2] = useState<string>("");
  const [result, setResult] = useState<string>("");

  const changeValue1 = (e: React.ChangeEvent<HTMLInputElement>) => setValue1(e.target.value);
  const changeValue2 = (e: React.ChangeEvent<HTMLInputElement>) => setValue2(e.target.value);

  const calculate = async () => {
  
  const numValue1 = Number(value1);
  const numValue2 = Number(value2);

  const result: number = numValue1 * numValue2;
  setResult(String(result));

  useEffect(() => {
    calculate();
  }, [value1, value2]);

  return (
    <section className='mt-8 px-8'>
        <h2 className='mb-2'>かけ算</h2>
        <div className='flex'>
            <input 
            className='rounded p-2'
            type="number" 
            name="かけ算1" 
            onChange={(e) => changeValue1(e)}
            value={value1}
            />
            <p className='mx-4 flex items-center justify-center w-3'>×</p>
            <input 
            className='rounded p-2'
            type="number" 
            name="かけ算2" 
            onChange={(e) => changeValue2(e)}
            value={value2}
            />
            <p className='mx-4 flex items-center'>=</p>
            <p className='flex items-center'>{result}</p>
        </div>
    </section>
  );
};

export default MultipleComponent;
割り算のコード
index.tsx
import React, { useState, useEffect } from 'react';
const DevideComponent = () => {
  const [value1, setValue1] = useState<string>("");
  const [value2, setValue2] = useState<string>("");
  const [result, setResult] = useState<string>("");

  const changeValue1 = (e: React.ChangeEvent<HTMLInputElement>) => setValue1(e.target.value);
  const changeValue2 = (e: React.ChangeEvent<HTMLInputElement>) => setValue2(e.target.value);

  const calculate = async () => {
  
  const numValue1 = Number(value1);
  const numValue2 = Number(value2);

  const result: number = Math.floor((numValue1 / numValue2) * 100) / 100;
  setResult(String(result));

  useEffect(() => {
    calculate();
  }, [value1, value2]);

  return (
    <section className='mt-8 px-8'>
        <h2 className='mb-2'>わり算</h2>
        <div className='flex'>
            <input 
            className='rounded p-2'
            type="number" 
            name="わり算1" 
            onChange={(e) => changeValue1(e)}
            value={value1}
            />
            <p className='mx-4 flex items-center justify-center w-3'>÷</p>
            <input 
            className='rounded p-2'
            type="number" 
            name="わり算2" 
            onChange={(e) => changeValue2(e)}
            value={value2}
            />
            <p className='mx-4 flex items-center'>=</p>
            <p className='flex items-center'>{result === "NaN" ? "0" : result}</p>
        </div>
    </section>
  );
};

export default DevideComponent;

割り算の場合は、小数点以下に数字が数多く出現したり、無限大になる場合があります。
その場合は、切り捨てや便宜上の数字を用意するなどで工夫すると良いでしょう。

すべて作成できたら、srcディレクトリにlibディレクトリを作成します。
その直下にapiClient.tsファイルを作成します。

api-test4.png

このファイルにはAxiosについての記述をします。
Axiosの公式によると以下の説明があります。

Axios は、ブラウザと Node.js のための、シンプルなプロミスベースの HTTP クライアントです。 Axios は、非常に拡張性の高いインターフェイスを持つ小さなパッケージで、 シンプルに使えるライブラリを提供します。

引用:https://axios-http.com/ja/

簡単に言い換えますと、HTTP通信を簡単に行えるJavaScriptライブラリのことです。
初めから付属しているFetch関数を利用すればサーバーとの通信が可能ですが、Axiosを利用すると記述が楽です。

Axiosをインストールするために、以下を実行してください。

bash.sh
npm install axios
npm install @types/axios -D

これでAxiosが利用できるので、apiClient.tsに以下を書きます。

apiClient.ts
import axios from "axios";

export const apiClient = axios.create({
    baseURL: "http://localhost:8080/api/v1",
    headers: {
        "Content-Type": "application/json"
    }
});

何をしているかというと「http://localhost:8080/api/v1 」という名前がつくエンドポイントに対してapplication/json形式でデータを取得や送信したりすることを示しています。

次に、フロントエンドとバックエンドの処理を切り替える機能を作成します。
pagesディレクトリにあるindex.tsxを開いて、以下のように編集してください。

index.tsx
import React, { useState } from "react";

//Component
import PlusComponent from "@/components/plusComponent";
import MinusComponent from "@/components/minusComponent";
import MultipleComponent from "@/components/multipleComponent";
import DevideComponent from "@/components/devideComponent";

export default function Home() {
  const [apiSwitch, setApiSwitch] = useState<boolean>(false);

  const handleApi = () => setApiSwitch(!apiSwitch);

  return (
    <>    
    <header className="h-14 w-full">
      <h1 className="text-3xl font-bold text-center h-full items-center justify-center flex">
        四則演算アプリ
      </h1>
    </header>
    <PlusComponent status={apiSwitch} />
    <MinusComponent status={apiSwitch} />
    <MultipleComponent status={apiSwitch} />
    <DevideComponent status={apiSwitch} />
    <div className="mt-6 px-8 flex items-center">
      <button 
      className="w-30 bg-orange-500 px-3 py-1 rounded text-white mr-4"
      onClick={handleApi}
      >
        APIモード
      </button>
      <p>{ apiSwitch ? "オン" : "オフ" }</p>
    </div>
    </>
  );
};

フロントエンドとサーバーサイドのどちらに計算処理をさせるかを切り替えるボタンを用意し、useStateで状態を管理します。
それだけでは、現在どっちの状態かが分からないのでそれを表示するようにします。

おそらく各Componentでエラーが発生しているので、plusComponentのindex.tsxに移動してください。
PlusComponent関数を以下のようにして、親コンポーネントから値を受け取れるようにします。

index.tsx
const PlusComponent = ({ status }: { status: boolean }) => {
    ...省略
}

残りの引き算(minusComponent)、かけ算(multipleComponent)、割り算(divideComponent)も同じように修正します。

次に、足し算Componentのindex.tsxにあるcalculate関数を以下のように修正します。

index.tsx
const calculate = async () => {
    if (status) {
      const response = await apiClient.post("/plus", { value1: value1, value2: value2 });
    
      try {
        setResult(response.data);
      } catch (err) {
        console.error(err);
      };
    } else {
      const numValue1 = Number(value1);
      const numValue2 = Number(value2);
    
      const result: number = numValue1 + numValue2;
      setResult(String(result));
    };
};

statusがtrueのときはAPIモードがオンになっているので、バックエンド側に値を送信します。
一方、falseのときはAPIモードがオフなのでフロントエンド側で計算を行う流れです。

残りの引き算やかけ算も同じように修正できます。
ただし、エンドポイントは以下のように設定してください。

  • 引き算:/minus
  • かけ算:/multiplication
  • わり算:/division

これでフロントエンドの実装が完了です。
以下にあるここまでのコードが間違ってないかを確認すれば、手順4が完了です。

ここまでのコードです。

足し算のコード
index.tsx
import React, { useState, useEffect } from 'react';
import { apiClient } from '../../lib/apiClients';

const PlusComponent = ({ status }: { status: boolean }) => {
  const [value1, setValue1] = useState<string>("");
  const [value2, setValue2] = useState<string>("");
  const [result, setResult] = useState<string>("");

  const changeValue1 = (e: React.ChangeEvent<HTMLInputElement>) => setValue1(e.target.value);
  const changeValue2 = (e: React.ChangeEvent<HTMLInputElement>) => setValue2(e.target.value);

  const calculate = async () => {
    if (status) {
      const response = await apiClient.post("/plus", { value1: value1, value2: value2 });
  
      try {
        setResult(response.data);
      } catch (err) {
        console.error(err);
      };
    } else {
      const numValue1 = Number(value1);
      const numValue2 = Number(value2);

      const result: number = numValue1 + numValue2;
      setResult(String(result));
    };
  };

  useEffect(() => {
    calculate();
  }, [value1, value2]);

  return (
    <section className='mt-8 px-8'>
        <h2 className='mb-2'>足し算</h2>
        <div className='flex'>
            <input 
            className='rounded p-2'
            type="number" 
            name="足し算1" 
            onChange={(e) => changeValue1(e)}
            value={value1}
            />
            <p className='mx-4 flex items-center justify-center w-3'>+</p>
            <input 
            className='rounded p-2'
            type="number" 
            name="足し算2" 
            onChange={(e) => changeValue2(e)}
            value={value2}
            />
            <p className='mx-4 flex items-center'>=</p>
            <p className='flex items-center'>{result}</p>
        </div>
    </section>
  );
};

export default PlusComponent;
引き算のコード
index.tsx
import React, { useState, useEffect } from 'react';

//lib
import { apiClient } from '../../lib/apiClients';

const MinusComponent = ({ status }: { status: boolean }) => {
  const [value1, setValue1] = useState<string>("");
  const [value2, setValue2] = useState<string>("");
  const [result, setResult] = useState<string>("");

  const changeValue1 = (e: React.ChangeEvent<HTMLInputElement>) => setValue1(e.target.value);
  const changeValue2 = (e: React.ChangeEvent<HTMLInputElement>) => setValue2(e.target.value);

  const valuesSend = async () => {
    if (status) {
      const response = await apiClient.post("/minus", { value1: value1, value2: value2 });
  
      try {
        setResult(response.data);
      } catch (err) {
        console.error(err);
      };
    } else {
      const numValue1 = Number(value1);
      const numValue2 = Number(value2);

      const result: number = numValue1 - numValue2;
      setResult(String(result));
    };
  };

  useEffect(() => {
    valuesSend();
  }, [value1, value2]);

  return (
    <section className='mt-8 px-8'>
        <h2 className='mb-2'>引き算</h2>
        <div className='flex'>
            <input 
            className='rounded p-2'
            type="number" 
            name="引き算1" 
            onChange={(e) => changeValue1(e)}
            value={value1}
            />
            <p className='mx-4 flex items-center justify-center w-3'>-</p>
            <input 
            className='rounded p-2'
            type="number" 
            name="引き算2" 
            onChange={(e) => changeValue2(e)}
            value={value2}
            />
            <p className='mx-4 flex items-center'>=</p>
            <p className='flex items-center'>{result}</p>
        </div>
    </section>
  );
};

export default MinusComponent;
かけ算のコード
index.tsx
import React, { useState, useEffect } from 'react';
import { apiClient } from '../../lib/apiClients';

const MultipleComponent = ({ status }: { status: boolean }) => {
  const [value1, setValue1] = useState<string>("");
  const [value2, setValue2] = useState<string>("");
  const [result, setResult] = useState<string>("");

  const changeValue1 = (e: React.ChangeEvent<HTMLInputElement>) => setValue1(e.target.value);
  const changeValue2 = (e: React.ChangeEvent<HTMLInputElement>) => setValue2(e.target.value);

  const valuesSend = async () => {
    if (status) {
      const response = await apiClient.post("/multiplication", { value1: value1, value2: value2 });
  
      try {
        setResult(response.data);
      } catch (err) {
        console.error(err);
      };
    } else {
      const numValue1 = Number(value1);
      const numValue2 = Number(value2);

      const result: number = numValue1 * numValue2;
      setResult(String(result));
    }
  }

  useEffect(() => {
    valuesSend();
  }, [value1, value2]);

  return (
    <section className='mt-8 px-8'>
        <h2 className='mb-2'>かけ算</h2>
        <div className='flex'>
            <input 
            className='rounded p-2'
            type="number" 
            name="かけ算1" 
            onChange={(e) => changeValue1(e)}
            />
            <p className='mx-4 flex items-center justify-center w-3'>×</p>
            <input 
            className='rounded p-2'
            type="number" 
            name="かけ算2" 
            onChange={(e) => changeValue2(e)}
            />
            <p className='mx-4 flex items-center'>=</p>
            <p className='flex items-center'>{result}</p>
        </div>
    </section>
  );
};

export default MultipleComponent;
わり算のコード
index.tsx
import React, { useState, useEffect } from 'react';

//lib
import { apiClient } from '../../lib/apiClients';

const DevideComponent = ({ status }: { status: boolean }) => {
  const [value1, setValue1] = useState<string>("");
  const [value2, setValue2] = useState<string>("");
  const [result, setResult] = useState<string>("");

  const changeValue1 = (e: React.ChangeEvent<HTMLInputElement>) => setValue1(e.target.value);
  const changeValue2 = (e: React.ChangeEvent<HTMLInputElement>) => setValue2(e.target.value);

  const valuesSend = async () => {
    if (status) {
      const response = await apiClient.post("/division", { value1: value1, value2: value2 });
  
      try {
        setResult(response.data);
      } catch (err) {
        console.error(err);
      };
    } else {
      const numValue1 = Number(value1);
      const numValue2 = Number(value2);

      const result: number = Math.floor((numValue1 / numValue2) * 100) / 100;
      setResult(String(result));
    };
  };

  useEffect(() => {
    valuesSend();
  }, [value1, value2]);

  return (
    <section className='mt-8 px-8'>
        <h2 className='mb-2'>割り算</h2>
        <div className='flex'>
            <input 
            className='rounded p-2'
            type="number" 
            name="割り算1" 
            onChange={(e) => changeValue1(e)}
            value={value1}
            />
            <p className='mx-4 flex items-center justify-center w-3'>÷</p>
            <input 
            className='rounded p-2'
            type="number" 
            name="割り算2" 
            onChange={(e) => changeValue2(e)}
            value={value2}
            />
            <p className='mx-4 flex items-center'>=</p>
            <p className='flex items-center'>{result === "NaN" ? "0" : result}</p>
        </div>
    </section>
  );
};

export default DevideComponent;

backendディレクトリにファイルを作成・実装

ここからはバックエンドの開発をしていきます。

手順通りに開発を進めてきた方はすでにExpressとTypeScriptが使えるので、開発を進めていきましょう。
そうでない方は、手順3を完了させてから開発を進めてください。

backendディレクトリに以下の構成となるようにroutersディレクトリとserver.ts、calculate.ts、.envを作成しましょう。

api-test6.png

server.tsに以下を記載します。

server.ts
import express, { Express } from "express";
import cors from "cors";
import "dotenv/config";

corsミドルウェアとdotenvが使えないので、以下を実行してインストールします。

bash.sh
npm install cors
npm install @types/cors -D
npm install dotenv

CORS(コルス)とは、あるオリジン上にあるWebアプリケーションに対して別のサーバーへのアクセスを許可する仕組みのことを言います。

CORSについてさらに知りたい方は、以下の記事を読むことをおすすめします。

.envファイルに以下を記載してください。

.env
PORT="8080"

次にサーバーのポートを指定して起動するために、以下のコードを書きましょう。

server.ts
const app: Express = express();
const PORT = process.env.PORT || "8080";

app.listen(PORT, () => console.log(`Server is running on ${PORT}`));

起動の前に、以下をインストールしましょう。

bash.sh
npm install nodemon
npm install ts-node

インストールが完了したら、package.jsonのscriptsが以下であることを確認しましょう。

package.json
"scripts": {
    "dev": "nodemon server.ts"
  }

以下のコマンドを実行し、Server is running on 8080が表示されたら成功です。

bash.sh
npm run dev

次に、corsとjson形式のデータの送受信を許可するために、以下を追記します。

server.ts
app.use(cors({ origin: "http://localhost:3000" }));
app.use(express.json());

app.use("/api/v1", calculateRouter);

server.tsに直接APIの処理を書いても良いですが、なるべくコードを分けたいので、ファイルを参照するように書きます。
以下を追記してください。

server.ts
import { calculateRouter } from "./routers/calculate";

エラーが出ますが、すぐに解消しますので気にせず進めます。

最終的にserver.tsは以下のようになっていれば大丈夫です。

server.tsのコード
server.ts
import express, { Express } from "express";
import cors from "cors";
import "dotenv/config";

//routers
import { calculateRouter } from "./routers/calculate";


const app: Express = express();
const PORT = process.env.PORT || "8080";

app.use(cors({ origin: "http://localhost:3000" }));
app.use(express.json());

app.use("/api/v1", calculateRouter);

app.listen(PORT, () => console.log(`Server is running on ${PORT}`));

最後は、calculate.tsファイルを編集します。
以下を追記しましょう。

 calculate.ts
import { Router, Request, Response } from "express";

//type
type valuesType = {
    value1: string;
    value2: string;
};

//routerの設定
export const calculateRouter: Router = Router();

importは型定義のためにインポートしており、typeは送受信するデータの型を定義しています。
Routerはserver.tsでも参照できるようにexportしています。

次に、足し算の処理を書きます。

calculate.ts
//足し算
calculateRouter.post("/plus", (req: Request, res: Response) => {
    const values: valuesType = req.body;

    try {
        const numResult: number = Number(values.value1) + Number(values.value2);
        const result = String(numResult);
        return res.status(200).json(result); 
    } catch (err) {
        console.error(err);
        return res.status(500).json({ error: "計算に失敗しました" });
    };
});

req.bodyにはフロントエンドから送信されたデータが含まれているので、valuesに代入します。
String型なのでこのままでは足し算できません。

そのためNumber型にして計算し、結果はString型に戻します。

エラーが起こる可能性があるので、catch文の後には計算できない旨をフロントエンド側に返す処理を書きます。

これで計算処理の実装が完了です。

残りの引き算、かけ算、割り算は同じように実装できますので、チャレンジしてみましょう。
コードは以下のようになっていれば大丈夫です。

引き算
calculate.ts
//引き算
calculateRouter.post("/minus", (req: Request, res: Response) => {
    const values: valuesType = req.body;

    try {
        const numResult: number = Number(values.value1) - Number(values.value2);
        const result = String(numResult);
        return res.status(200).json(result); 
    } catch(err) {
        console.error(err);
        return res.status(500).json({ error: "計算に失敗しました" });
    };
});
かけ算
calculate.ts
//かけ算
calculateRouter.post("/multiplication", (req: Request, res: Response) => {
    const values: valuesType = req.body;

    try {
        const numResult: number = Number(values.value1) * Number(values.value2);
        const result = String(numResult);
        return res.status(200).json(result); 
    } catch(err) {
        console.error(err);
        return res.status(500).json({ error: "計算に失敗しました" });
    };
});
割り算
calculate.ts
//割り算
calculateRouter.post("/division", (req: Request, res: Response) => {
    const values: valuesType = req.body;

    try {
        const numResult: number = Math.floor((Number(values.value1) / Number(values.value2)) * 100) / 100;
        const result = String(numResult);
        return res.status(200).json(result); 
    } catch(err) {
        console.error(err);
        return res.status(500).json({ error: "計算に失敗しました" });
    };
});

最後にコードを掲載しますので、確認が完了したら手順5が完了です。

calculate.ts
calculate.ts
import { Router, Request, Response } from "express";

//type
type valuesType = {
    value1: string;
    value2: string;
};

//routerの設定
export const calculateRouter: Router = Router();

//足し算
calculateRouter.post("/plus", (req: Request, res: Response) => {
    const values: valuesType = req.body;

    try {
        const numResult: number = Number(values.value1) + Number(values.value2);
        const result = String(numResult);
        return res.status(200).json(result); 
    } catch (err) {
        console.error(err);
        return res.status(500).json({ error: "計算に失敗しました" });
    };
});

//引き算
calculateRouter.post("/minus", (req: Request, res: Response) => {
    const values: valuesType = req.body;

    try {
        const numResult: number = Number(values.value1) - Number(values.value2);
        const result = String(numResult);
        return res.status(200).json(result); 
    } catch(err) {
        console.error(err);
        return res.status(500).json({ error: "計算に失敗しました" });
    };
});

//かけ算
calculateRouter.post("/multiplication", (req: Request, res: Response) => {
    const values: valuesType = req.body;

    try {
        const numResult: number = Number(values.value1) * Number(values.value2);
        const result = String(numResult);
        return res.status(200).json(result); 
    } catch(err) {
        console.error(err);
        return res.status(500).json({ error: "計算に失敗しました" });
    };
});

//割り算
calculateRouter.post("/division", (req: Request, res: Response) => {
    const values: valuesType = req.body;

    try {
        const numResult: number = Math.floor((Number(values.value1) / Number(values.value2)) * 100) / 100;
        const result = String(numResult);
        return res.status(200).json(result); 
    } catch(err) {
        console.error(err);
        return res.status(500).json({ error: "計算に失敗しました" });
    };
});

最後に

簡単なアプリでしたが、このような小さなアプリを作成することで実装の手順や達成感が出ます。

バックエンドを実装したいけど、まずは簡単なものからチャレンジしたい人はぜひ実装してみてください。

3
2
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
3
2