MERNスタックの使用を開始する方法
序章
MERNスタックは、MongoDB、Express、React / Redux、およびNode.jsで構成されています。 MERNスタックは、最新のシングルページWebアプリケーションを構築するための最も人気のあるJavaScriptスタックの1つです。
このチュートリアルでは、 RESTfulAPIを使用するtodoアプリケーションをビルドします。これもこのチュートリアルの後半でビルドします。
前提条件
このチュートリアルを完了するには、次のものが必要です。
- Node.jsはローカルにインストールされます。これは、Node.jsのインストール方法とローカル開発環境の作成に従って実行できます。
注:このチュートリアルは、もともとmLabサービスを使用してMongoDBデータベースをホストするために作成されました。 mLabは、2019年2月以降、新しいアカウントの作成が禁止されており、MongoDBAtlasの使用を提案しています。
インストール手順に従ってMongoDBをローカルで実行することもできますが、このチュートリアルではそのプロセスについては説明せず、教育目的でそのまま使用します。
また、使い慣れたコードエディタ、できればJavaScriptコードの強調表示をサポートするコードエディタも必要です。
APIエンドポイントのテストには、Postmanなどのツールをダウンロードしてインストールすることをお勧めします。
このチュートリアルは、ノードv14.2.0、npm
v6.14.5、mongodb-community
v4.2.6、express
v4.17.1、およびmongoose
v5.9.17で検証されました。 。
ステップ1—アプリケーションのセットアップ
セットアップから始めましょう。 ターミナルを開き、ローカルマシンの任意の便利な場所に新しいファイルディレクトリを作成します。 名前は何でもかまいませんが、この例ではmern-todo
と呼ばれています。
mkdir mern-todo
次に、そのファイルディレクトリに入ります。
cd mern-todo
次のステップは、package.json
ファイルを使用してプロジェクトを初期化することです。 このファイルには、アプリと実行する必要のある依存関係に関する情報が含まれています。
次を使用できます。
npm init
プロンプトが表示されたら、指示に従います。 または、次を使用できます。
npm init -y
デフォルト値を使用します。
ステップ2—ノードサーバーのセットアップ
バックエンドでJavaScriptコードを実行するには、コードをコンパイルするサーバーを起動する必要があります。
サーバーは2つの方法で作成できます。1つは、ノードに組み込まれているhttp
モジュールを使用する方法です。 2つ目は、Express.jsフレームワークを利用することです。
このチュートリアルでは、Express.jsを使用します。 これはNode.jsHTTPフレームワークであり、箱から出してすぐに多くのことを処理し、完全に機能するRESTfulAPIを作成するためのコードをほとんど必要としません。 Expressを使用するには、npmを使用してインストールします。
npm install express
次に、ファイルindex.js
を作成し、次のコードを入力して保存します。
index.js
const express = require('express'); const app = express(); const port = process.env.PORT || 5000; app.use((req, res, next) => { res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept'); next(); }); app.use((req, res, next) => { res.send('Welcome to Express'); }); app.listen(port, () => { console.log(`Server running on port ${port}`); });
前のコードのこのスニペットは、開発およびテスト中にさまざまなドメインからAPIにアクセスしようとしたときに直面する可能性のあるCORS関連の問題を処理するのに役立ちます。
app.use((req, res, next) => { res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept'); next(); });
サーバーを起動して、サーバーが機能するかどうかを確認します。 index.js
ファイルと同じディレクトリで端末を開き、次のように入力します。
node index.js
すべてがうまくいけば、端末のポート5000でサーバーが実行されていることがわかります。
ステップ3—ルートを作成する
アプリが実行する必要があることは3つあります。
- タスクを作成する
- すべてのタスクを表示
- 完了したタスクを削除する
タスクごとに、todoアプリが依存する複数のエンドポイントを定義するルートを作成する必要があります。 それでは、フォルダroutes
を作成し、次のコードを含むファイルapi.js
を作成しましょう。
mkdir routes
api.js
を編集して、次のコードを貼り付けます。
ルート/api.js
const express = require('express'); const router = express.Router(); router.get('/todos', (req, res, next) => { // get placeholder }); router.post('/todos', (req, res, next) => { // post placeholder }); router.delete('/todos/:id', (req, res, next) => { // delete placeholder }); module.exports = router;
これにより、GET、POST、およびDELETEのプレースホルダールートが提供されます。
ステップ4—モデルの定義
さて、興味深い部分があります。 アプリはNoSQLデータベースであるMongoDBを利用するため、モデルとスキーマを作成する必要があります。 モデルは、スキーマインターフェイスを使用して定義されます。 スキーマを使用すると、検証要件とデフォルト値とともに、各ドキュメントに格納されているフィールドを定義できます。 本質的に、スキーマはデータベースがどのように構築されるかの青写真です。 さらに、静的メソッドとインスタンスヘルパーメソッドを定義して、データ型の操作を容易にしたり、他のフィールドと同じように使用できるがデータベースには保存されない仮想プロパティを定義したりできます。
スキーマとモデルを作成するには、MongoDBでの作業を容易にするノードパッケージであるMongooseをインストールします。
# ensure that you are in the `mern-todo` project directory npm install mongoose
ルートディレクトリに新しいフォルダを作成し、models
という名前を付けます。 その中にファイルを作成し、次のコードを含むtodo.js
という名前を付けます。
mkdir models
テキストエディタを使用して、以下をtodo.js
に貼り付けます。
models / todo.js
const mongoose = require('mongoose'); const Schema = mongoose.Schema; // Create schema for todo const TodoSchema = new Schema({ action: { type: String, required: [true, 'The todo text field is required'], }, }); // Create model for todo const Todo = mongoose.model('todo', TodoSchema); module.exports = Todo;
次に、新しいモデルを使用するためにルートを更新する必要があります。
ルート/api.js
const express = require('express'); const router = express.Router(); const Todo = require('../models/todo'); router.get('/todos', (req, res, next) => { // This will return all the data, exposing only the id and action field to the client Todo.find({}, 'action') .then((data) => res.json(data)) .catch(next); }); router.post('/todos', (req, res, next) => { if (req.body.action) { Todo.create(req.body) .then((data) => res.json(data)) .catch(next); } else { res.json({ error: 'The input field is empty', }); } }); router.delete('/todos/:id', (req, res, next) => { Todo.findOneAndDelete({ _id: req.params.id }) .then((data) => res.json(data)) .catch(next); }); module.exports = router;
ステップ5—データベースへの接続
データを保存するデータベースが必要になります。 このために、mLabを利用します。 ドキュメントに従って、mLabの使用を開始してください。
データベースを設定した後、index.js
ファイルを次のコードで更新する必要があります。
index.js
const express = require('express'); const bodyParser = require('body-parser'); const mongoose = require('mongoose'); const routes = require('./routes/api'); require('dotenv').config(); const app = express(); const port = process.env.PORT || 5000; // Connect to the database mongoose .connect(process.env.DB, { useNewUrlParser: true }) .then(() => console.log(`Database connected successfully`)) .catch((err) => console.log(err)); // Since mongoose's Promise is deprecated, we override it with Node's Promise mongoose.Promise = global.Promise; app.use((req, res, next) => { res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept'); next(); }); app.use(bodyParser.json()); app.use('/api', routes); app.use((err, req, res, next) => { console.log(err); next(); }); app.listen(port, () => { console.log(`Server running on port ${port}`); });
注: Express 4.16以降のバージョンでは、body-parser
などのミドルウェアに依存する必要がありました。 ただし、組み込みのパーサーを使用できるようになりました。
古いバージョンのExpressを使用している場合は、npmを使用してbody-parser
をインストールします。
npm install body-parser
前のコードでは、process.env
を使用して、作成する必要のある環境変数にアクセスしました。 ルートディレクトリに.env
という名前のファイルを作成し、次のように編集します。
.env
DB = 'mongodb://<USER>:<PASSWORD>@example.mlab.com:port/todo'
データベースとユーザーを作成した後は、mLabから独自のMongoDBURLを使用してください。 <USER>
をユーザー名に置き換え、<PASSWORD>
を作成したユーザーのパスワードに置き換えます。
環境変数を操作するには、dotenv
というノードパッケージをインストールする必要があります。これにより、.env
ファイルに保存されている環境変数にアクセスできるようになります。
# ensure that you are in the `mern-todo` project directory npm install dotenv
次に、index.js
で要求して構成します。
require('dotenv').config()
アプリケーションコードにクレデンシャルを直接書き込む代わりに環境変数を使用すると、バージョニングシステムから機密情報を隠すことができます。 この方法で、構成データとシークレットデータをアプリケーションコードから分離することがベストプラクティスと見なされます。
ステップ6—APIをテストする
これは、RESTfulAPIが機能していることを確認するために私たちが試し始める部分です。 フロントエンドはまだ準備ができていないため、一部のAPI開発クライアントを使用してコードをテストできます。
APIのテストには、PostmanまたはInsomnia、またはお好みのクライアントを使用できます。
次のコマンドを使用してサーバーを起動します。
node index.js
次に、クライアントを開き、GETメソッドを作成して、http://localhost:5000/api/todos
に移動します。
すべてのAPIエンドポイントをテストし、それらが機能していることを確認します。 body
を必要とするエンドポイントの場合、コードで設定したものであるため、必要なフィールドを含むJSONを返送します。
POSTリクエストのサンプル:
POST localhost:5000/api/todos Body raw
サンプルPOST値:
{ "action": "build a mern stack application" }
GETリクエストのサンプル:
GET localhost:5000/api/todos
GET応答の例:
Output[ { "id": "5bd4edfc89d4c3228e1bbe0a", "action": "build a mern stack application" } ]
サンプルのDELETEリクエスト:
DELETE localhost:5000/api/todos/5bd4edfc89d4c3228e1bbe0ad
GET、POST、およびDELETEの結果をテストして観察します。
ステップ7—フロントエンドを作成する
APIに必要な機能が完了したので、クライアントがAPIと対話するためのインターフェイスを作成します。 ToDoアプリのフロントエンドから始めるには、create-react-appコマンドを使用してアプリをスキャフォールディングします。
バックエンドコードと同じルートディレクトリであるmern-todo
ディレクトリで、次のコマンドを実行します。
npx create-react-app client
これにより、mern-todo
ディレクトリにclient
という新しいフォルダが作成され、そこにすべてのReactコードが追加されます。
ステップ8—Reactアプリを実行する
Reactアプリをテストする前に、プロジェクトのルートディレクトリにインストールする必要のある多くの依存関係があります。
まず、開発依存関係として同時にをインストールします。
npm install concurrently --save-dev
同時に、同じターミナルウィンドウから複数のコマンドを同時に実行するために使用されます。
次に、nodemonを開発依存関係としてインストールします。
npm install nodemon --save-dev
Nodemonは、サーバーの実行と監視にも使用されます。 サーバーコードに変更があった場合、Nodemonは新しい変更で自動的に再起動します。
次に、アプリプロジェクトのルートフォルダーにあるpackage.json
ファイルを開き、次のコードを貼り付けます。
package.json
{ // ... "scripts": { "start": "node index.js", "start-watch": "nodemon index.js", "dev": "concurrently \"npm run start-watch\" \"cd client && npm start\"" }, // ... }
クライアントフォルダに入り、package.json
ファイルを見つけて、その中に次のキーと値のペアを追加します。
client / package.json
{ // ... "proxy": "http://localhost:5000" }
package.json
ファイルのこのプロキシ設定により、完全なURLを入力しなくてもAPI呼び出しを行うことができ、/api/todos
だけですべてのタスクを取得できます。
ターミナルを開いてnpm run dev
を実行し、client
ディレクトリではなくtodo
ディレクトリにいることを確認します。
アプリはlocalhost:3000
で開いて実行されます。
ステップ9—Reactコンポーネントの作成
Reactの利点の1つは、再利用可能でコードをモジュール化できるコンポーネントを利用することです。 ToDoアプリには、2つのステートコンポーネントと1つのステートレスコンポーネントがあります。
src
フォルダー内に、components
という別のフォルダーを作成し、その中に3つのファイルInput.js
、ListTodo.js
、およびTodo.js
を作成します。
Input.js
ファイルを開き、以下を貼り付けます。
client / src / components / Input.js
import React, { Component } from 'react'; import axios from 'axios'; class Input extends Component { state = { action: '', }; addTodo = () => { const task = { action: this.state.action }; if (task.action && task.action.length > 0) { axios .post('/api/todos', task) .then((res) => { if (res.data) { this.props.getTodos(); this.setState({ action: '' }); } }) .catch((err) => console.log(err)); } else { console.log('input field required'); } }; handleChange = (e) => { this.setState({ action: e.target.value, }); }; render() { let { action } = this.state; return ( <div> <input type="text" onChange={this.handleChange} value={action} /> <button onClick={this.addTodo}>add todo</button> </div> ); } } export default Input;
ブラウザとNode.js用のPromiseベースのHTTPクライアントであるaxiosを利用するには、ターミナルからclient
ディレクトリに移動する必要があります。
cd client
そして、npm install axios
を実行します。
npm install axios
その後、ListTodo.js
ファイルを開き、次のコードを貼り付けます。
client / src / components / ListTodo.js
import React from 'react'; const ListTodo = ({ todos, deleteTodo }) => { return ( <ul> {todos && todos.length > 0 ? ( todos.map((todo) => { return ( <li key={todo._id} onClick={() => deleteTodo(todo._id)}> {todo.action} </li> ); }) ) : ( <li>No todo(s) left</li> )} </ul> ); }; export default ListTodo;
次に、Todo.js
ファイルに次のコードを記述します。
client / src / components / Todo.js
import React, { Component } from 'react'; import axios from 'axios'; import Input from './Input'; import ListTodo from './ListTodo'; class Todo extends Component { state = { todos: [], }; componentDidMount() { this.getTodos(); } getTodos = () => { axios .get('/api/todos') .then((res) => { if (res.data) { this.setState({ todos: res.data, }); } }) .catch((err) => console.log(err)); }; deleteTodo = (id) => { axios .delete(`/api/todos/${id}`) .then((res) => { if (res.data) { this.getTodos(); } }) .catch((err) => console.log(err)); }; render() { let { todos } = this.state; return ( <div> <h1>My Todo(s)</h1> <Input getTodos={this.getTodos} /> <ListTodo todos={todos} deleteTodo={this.deleteTodo} /> </div> ); } } export default Todo;
Reactコードを少し調整する必要があります。 ロゴを削除し、App.js
を次のように調整します。
client / src / App.js
import React from 'react'; import Todo from './components/Todo'; import './App.css'; const App = () => { return ( <div className="App"> <Todo /> </div> ); }; export default App;
次に、次のコードをApp.css
に貼り付けます。
client / src / App.css
.App { text-align: center; font-size: calc(10px + 2vmin); width: 60%; margin-left: auto; margin-right: auto; } input { height: 40px; width: 50%; border: none; border-bottom: 2px #101113 solid; background: none; font-size: 1.5rem; color: #787a80; } input:focus { outline: none; } button { width: 25%; height: 45px; border: none; margin-left: 10px; font-size: 25px; background: #101113; border-radius: 5px; color: #787a80; cursor: pointer; } button:focus { outline: none; } ul { list-style: none; text-align: left; padding: 15px; background: #171a1f; border-radius: 5px; } li { padding: 15px; font-size: 1.5rem; margin-bottom: 15px; background: #282c34; border-radius: 5px; overflow-wrap: break-word; cursor: pointer; } @media only screen and (min-width: 300px) { .App { width: 80%; } input { width: 100% } button { width: 100%; margin-top: 15px; margin-left: 0; } } @media only screen and (min-width: 640px) { .App { width: 60%; } input { width: 50%; } button { width: 30%; margin-left: 10px; margin-top: 0; } }
また、index.css
に次のルールを追加します。
client / src / index.css
body { margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; box-sizing: border-box; background-color: #282c34; color: #787a80; } code { font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; }
これらすべてのファイルを保存するときにエラーがないと仮定すると、todoアプリは準備ができており、前述の機能(タスクの作成、タスクの削除、すべてのタスクの表示)で完全に機能します。
結論
このチュートリアルでは、MERNスタックを使用してtodoアプリを作成しました。 Express.jsを使用して作成されたバックエンドアプリケーションと通信するReactを使用してフロントエンドアプリケーションを作成しました。 また、データベースにタスクを保存するためのMongoDBバックエンドを作成しました。