NextJS Templates
Back4App、Next.js、Vercelを使用した会議室予約アプリのチュートリアル
23 分
このチュートリアルでは、 bookit という会議室予約システムを構築します。フロントエンドフレームワークとして next js を使用し、バックエンドサービスとして back4app を使用します。ユーザー認証、部屋管理、予約機能を実装し、アプリを vercel にデプロイします。 1 前提条件 このチュートリアルを完了するには、次のものが必要です: back4appアカウント( back4app https //www back4app com/ にサインアップ) back4appプロジェクト( このガイド https //www back4app com/docs/get started/welcome に従ってください) ローカルマシンにnode jsがインストールされていること( インストールガイド https //nodejs org/ ) javascript、next js、およびrest apiの基本的な知識(必要に応じて、 javascriptの基礎 https //www back4app com/docs/javascript guide を参照してください) 2 back4appの設定 back4appアカウントにログインし、プロジェクトダッシュボードに移動します。 左のサイドバーで、 データベース をクリックしてデータブラウザにアクセスします。 データをモデル化するために、次のクラスを作成する必要があります: 2 1 ユーザー クラスを作成する デフォルトでは、parseはユーザー認証を処理するため、このクラスを手動で作成する必要はありません。parseは次のフィールドを自動的に管理します: email ログイン用のメールアドレス password パスワード(ハッシュ化) username オプションのユーザー名 2 2 ルーム クラスを作成する 「 データブラウザ 」をクリックし、「 クラスを作成 」を選択し、「 カスタム 」を選択し、クラスの名前を「 ルーム 」とします。 次の列を追加します 列名 タイプ 説明 名前 文字列 部屋の名前 説明 文字列 部屋の説明 容量 番号 部屋が収容できる人数 アメニティ 配列 アメニティのリスト(テレビ、wifiなど) 価格 番号 時間あたりの価格 画像 ファイル 画像のurl オーナー ポインター ポイントは ユーザー クラスを指します 2 3 次の作成 booking クラス 別のカスタムクラスを作成 booking 次の列を追加 列名 タイプ 説明 部屋 ポインタ 次の room クラスを指します ユーザー ポインタ 次の user クラスを指します チェックイン 日付 予約の開始日時 チェックアウト 日付 予約の終了日時 3 next js と parse sdk の設定 新しい next js プロジェクトを作成します npx create next app bookit app cd bookit app parse js sdk をインストールします npm install parse 次の pages/ app js ファイルで、back4app の資格情報を使って parse を初期化します import parse from 'parse'; parse initialize('your app id', 'your javascript key'); parse serverurl = 'https //parseapi back4app com'; function myapp({ component, pageprops }) { return \<component { pageprops} /> } export default myapp; 次の 'your app id' と 'your javascript key' を back4app のアプリ資格情報に置き換えます。 4 ユーザー認証の実装 4 1 ユーザー登録 登録フォームを持つ新しいページ pages/signup js を作成します import { usestate } from 'react'; import parse from 'parse'; export default function signup() { const \[email, setemail] = usestate(''); const \[password, setpassword] = usestate(''); const handlesignup = async (e) => { e preventdefault(); const user = new parse user(); user set('username', email); user set('email', email); user set('password', password); try { await user signup(); alert('signup successful!'); } catch (error) { alert('error ' + error message); } }; return ( \<form onsubmit={handlesignup}> \<input type="email" value={email} onchange={(e) => setemail(e target value)} placeholder="email" /> \<input type="password" value={password} onchange={(e) => setpassword(e target value)} placeholder="password" /> \<button type="submit">sign up\</button> \</form> ); } 4 2 ユーザーログイン ユーザーログインのために pages/login js を作成します import { usestate } from 'react'; import parse from 'parse'; export default function login() { const \[email, setemail] = usestate(''); const \[password, setpassword] = usestate(''); const handlelogin = async (e) => { e preventdefault(); try { await parse user login(email, password); alert('login successful!'); } catch (error) { alert('error ' + error message); } }; return ( \<form onsubmit={handlelogin}> \<input type="email" value={email} onchange={(e) => setemail(e target value)} placeholder="email" /> \<input type="password" value={password} onchange={(e) => setpassword(e target value)} placeholder="password" /> \<button type="submit">login\</button> \</form> ); } 4 3 保護されたルート 保護されたルートには、next js api ルートを使用し、ユーザーが認証されているかどうかを確認できます import parse from 'parse'; export default async function handler(req, res) { const user = parse user current(); if (!user) { return res status(401) json({ message 'unauthorized' }); } // proceed with the request } 5 ルーム管理 5 1 利用可能な部屋の表示 利用可能な部屋をリストするために pages/index js を作成します import { useeffect, usestate } from 'react'; import parse from 'parse'; export default function home() { const \[rooms, setrooms] = usestate(\[]); useeffect(() => { const fetchrooms = async () => { const room = parse object extend('room'); const query = new parse query(room); const results = await query find(); setrooms(results); }; fetchrooms(); }, \[]); return ( \<div> \<h1>available rooms\</h1> {rooms map(room => ( \<div key={room id}> \<h2>{room get('name')}\</h2> \<img src={room get('image') url()} alt={room get('name')} /> \<p>capacity {room get('capacity')}\</p> \<p>price ${room get('price')} per hour\</p> \</div> ))} \</div> ); } 5 2 部屋の追加 新しい部屋を追加するために pages/add room js を作成します(認可されたユーザー用): import { usestate } from 'react'; import parse from 'parse'; export default function addroom() { const \[name, setname] = usestate(''); const \[description, setdescription] = usestate(''); const \[capacity, setcapacity] = usestate(0); const \[price, setprice] = usestate(0); const handleaddroom = async (e) => { e preventdefault(); const room = parse object extend('room'); const room = new room(); room set('name', name); room set('description', description); room set('capacity', capacity); room set('price', price); try { await room save(); alert('room added successfully!'); } catch (error) { alert('error ' + error message); } }; return ( \<form onsubmit={handleaddroom}> \<input type="text" value={name} onchange={(e) => setname(e target value)} placeholder="room name" /> \<textarea value={description} onchange={(e) => setdescription(e target value)} placeholder="description">\</textarea> \<input type="number" value={capacity} onchange={(e) => setcapacity(parseint(e target value))} placeholder="capacity" /> \<input type="number" value={price} onchange={(e) => setprice(parseint(e target value))} placeholder="price" /> \<button type="submit">add room\</button> \</form> ); } 5 3 部屋の詳細 部屋の詳細情報を表示するために pages/rooms/\[id] js を作成します: import { userouter } from 'next/router'; import { useeffect, usestate } from 'react'; import parse from 'parse'; export default function roomdetails() { const router = userouter(); const { id } = router query; const \[room, setroom] = usestate(null); useeffect(() => { if (!id) return; const fetchroom = async () => { const room = parse object extend('room'); const query = new parse query(room); query equalto('objectid', id); const result = await query first(); setroom(result); }; fetchroom(); }, \[id]); if (!room) return \<div>loading \</div>; return ( \<div> \<h1>{room get('name')}\</h1> \<img src={room get('image') url()} alt={room get('name')} /> \<p>{room get('description')}\</p> \<p>capacity {room get('capacity')}\</p> \<p>price ${room get('price')} per hour\</p> \</div> ); } 6 予約システム 6 1 部屋の予約 予約フォームを追加することで pages/rooms/\[id] js に予約機能を追加します const \[checkin, setcheckin] = usestate(''); const \[checkout, setcheckout] = usestate(''); const handlebooking = async () => { const booking = parse object extend('booking'); const booking = new booking(); booking set('room', room); booking set('user', parse user current()); booking set('checkin', new date(checkin)); booking set('checkout', new date(checkout)); // check for double booking (example logic) const query = new parse query(booking); query equalto('room', room); query greaterthanorequalto('checkin', new date(checkin)); query lessthanorequalto('checkout', new date(checkout)); const overlap = await query first(); if (!overlap) { try { await booking save(); alert('booking successful!'); } catch (error) { alert('error ' + error message); } } else { alert('room is already booked for the selected time range '); } }; 6 2 予約の確認とキャンセル 予約を確認しキャンセルするために pages/my bookings js を作成します import { useeffect, usestate } from 'react'; import parse from 'parse'; export default function mybookings() { const \[bookings, setbookings] = usestate(\[]); useeffect(() => { const fetchbookings = async () => { const booking = parse object extend('booking'); const query = new parse query(booking); query equalto('user', parse user current()); const results = await query find(); setbookings(results); }; fetchbookings(); }, \[]); const handlecancel = async (id) => { const booking = bookings find(b => b id === id); if (booking) { await booking destroy(); setbookings(bookings filter(b => b id !== id)); } }; return ( \<div> \<h1>my bookings\</h1> {bookings map(booking => ( \<div key={booking id}> \<p>room {booking get('room') get('name')}\</p> \<p>check in {new date(booking get('checkin')) tolocalestring()}\</p> \<p>check out {new date(booking get('checkout')) tolocalestring()}\</p> \<button onclick={() => handlecancel(booking id)}>cancel booking\</button> \</div> ))} \</div> ); } 7 vercelへのデプロイ vercel cliをインストールします npm install g vercel 次のコマンドでnext jsアプリをデプロイします vercel プロンプトに従ってアプリをvercelにデプロイします。デプロイが完了すると、アプリは公開urlでライブになります。 8 結論 このチュートリアルでは、 bookit アプリを next js をフロントエンドに、 back4app をバックエンドに使用して構築しました。ユーザー認証、ルーム管理、予約機能を実装しました。最後に、アプリを vercel にデプロイしました。これで、検索、支払い統合、通知などの追加機能でアプリを拡張できます。