Quickstarters
How to Build an E-Commerce Backend?
75 分
このハンズオンチュートリアルでは、バックエンド・アズ・ア・サービスを使用して、ゼロから堅牢なフルスタックのeコマースアプリケーションを作成します。その過程で、あなたはマスターします 安全なユーザー認証、認可されたショッパーと管理者のみがアクセスできるようにする 直感的なデータモデル、製品カタログ、顧客プロファイル、注文履歴を支えるように設計されています restfulおよびリアルタイムapi、フロントエンドが常に在庫、カート、チェックアウト状況と同期するようにします 自動デプロイメントパイプライン、更新を自信を持ってプッシュし、何か問題が発生した場合はロールバックできるようにします 最終的には、製品準備が整ったストアフロントだけでなく、任意のwebアプリケーションを拡張、スケール、保護するためのアーキテクチャのノウハウも得られます。 完成したeコマースアプリケーションを次のリンクで実際に体験してください web e commerce app さあ、始めましょう! 重要なポイント 迅速なバックエンドセットアップ back4appのローコードプラットフォームを使用して、数分で完全なeコマースデータモデルを立ち上げ、構成します。 安全なユーザーフロー cloud codeとセッショントークンを使用して、堅牢なサインアップ、ログイン、ログアウトを実装します。 apiファースト開発 自動生成されたrest/graphqlエンドポイントとparse sdkを介して、任意のクライアントからデータにアクセスします。 cloud codeでのビジネスロジック コア操作(製品crud、カート管理、チェックアウト)を集中管理し、権限を強制します。 コンテナ化されたフロントエンドデプロイメント 一貫性があり、スケーラブルなホスティングのために、back4app web deploymentでnext jsストアフロントをパッケージ化してデプロイします。 前提条件と環境設定 このチュートリアルに従うには、以下のものを用意してください back4appアカウント。 back4appウェブサイト https //www back4app com/signup に無料でサインアップできます。 node jsがインストールされており、npmまたはyarnで依存関係を管理できること。 カスタムクラウドコードを記述し、back4appのsdkを活用するためのjavascriptの知識。 ユーザー認証、製品カタログ管理、注文処理などのeコマースの基本概念についての理解。 バージョン管理のためのgitを使用し、変更を追跡し、必要に応じてロールバックし、シームレスにコラボレーションできること。 上記のアイテムを確保することで、back4appの機能を最大限に活用し、重要なeコマース機能を実装する際に煩わされることなく進めることができます。 back4appアカウントの作成と設定 back4appでeコマースバックエンドの設定を開始するには、 back4app https //www back4app com を訪れ、 サインアップ をクリックします。 メールアドレスを入力し、強力なパスワードを選択し、必要な詳細を提供します。メールを確認すると、back4appダッシュボードにアクセスできるようになります。 back4appサインアップページ ダッシュボード内で、 新しいアプリを作成 または 新しいアプリを構築 を選択し、プロジェクトに名前を付けます—後で簡単に見つけられるように、説明的な名前を付けてください。 選択を確認し、アプリ作成プロセスが完了するのを待ちます。プロジェクトダッシュボードには、クラス、分析、設定が表示されます。 creating a back4app app これらのステップで、あなたはeコマースプラットフォームを作成しました。次のセクションでは、データスキーマを設計し、製品管理、注文処理、ユーザー認証の準備をします。 あなたのeコマースデータスキーマの設計 スケーラブルでメンテナブルなバックエンドをeコマースプラットフォームに実装するために、7つのコアテーブルを中心にデータを整理します user , buyer , seller , product , cart , order , and orderdetails 各テーブルはシステム全体の機能をサポートするための特定の役割を果たし、共に正規化された効率的なスキーマを形成します。 最初に user テーブルを作成します。これはすべてのユーザーのアイデンティティの基盤となります。プラットフォーム上のすべての人は、その役割に関係なく、このテーブルを使用して認証します。 次のようなフィールドを含めます username , password , および email 。基本的なログインを処理するためです。検証ステータスを追跡するために emailverified を追加し、サードパーティのログインやoauthフローをサポートする予定がある場合は authdata を追加します。 購入者と販売者を区別するために、 usertype フィールドを定義します。このフィールドにより、アクセスを制御し、ビューを管理し、ユーザーの役割に基づいて機能を分離しながら、単一の一貫したログインシステムを維持できます。 すべてのユーザータイプを1つのテーブルに混在させるのではなく、 buyer と seller の別々のテーブルを作成し、 user を userid フィールドを介して参照します。これにより、コアユーザーテーブルを膨らませることなく、役割特有のデータを保存できます。 buyer テーブルには、チェックアウトおよび配送プロセスに不可欠な paymentinfo や shippingaddress などの購入関連フィールドを保持できます。それに対して、 seller テーブルは、 storename , bio , および phonenumber , などの商人に関連する属性を追跡します。 この分離により、購入者と販売者を独立して簡単に管理およびクエリでき、同時に user における共有アイデンティティにリンクできます。 あなたの product テーブルは、販売用のアイテムが保存される場所です。各製品には、名前、詳細な説明、1つ以上の画像などの基本的な説明情報を含める必要があります。 価格と在庫管理のために、価格、ブール値による可用性ステータス isactive , および quantityavailable の下に在庫数量のフィールドを含めることも必要です。 重要なのは、各製品がそれをリストした売り手にリンクされている必要があることです。その関係を確立するために、 sellerid 外部キーを追加して、 seller テーブルを参照します。 この構造は、カタログ内のすべてのアイテムが確認済みの商人に結びついていることを保証し、売り手特有のフィルタリングを簡単にします。 購入前の行動を処理するために、 cart テーブルを導入します。各カートはユーザーに属するため、 userid フィールドを含めて user に接続します。 カートの items の配列を保存し、各アイテムはユーザーが購入しようとしている製品を表し、数量や選択されたオプションなどのメタデータを必要に応じて含めます。 カートの totalprice を追跡することで、毎回値を再計算することなく、チェックアウトの概要を迅速に表示できます。 このセットアップは基本的なカート機能をサポートし、セッションの永続性を簡素化します。配列構造が制限されていると感じた場合、特により複雑なアイテムレベルのデータを記録する必要がある場合は、後でアイテムを別のテーブルにリファクタリングすることができます。 バイヤーが注文を行うと、 order テーブルに取引を記録します。各注文は buyerid に結びついており、 buyer テーブルを参照して、購入者が有効で認可されていることを確認します。 このリンクに加えて、 orderdate , 現在の orderstatus , および total フィールドの下に総金額を保存します。これにより、購入の明確な概要が得られ、注文追跡や履行などのワークフローをサポートします。 各注文には複数の製品が含まれる可能性があるため、個々のラインアイテムを管理するために orderdetails テーブルが必要です。 このテーブルは、各アイテムを orderid を通じて親注文に接続し、 productid を使用して購入された製品に接続します。 購入の状態を正確に記録するために、 数量 と 単価 を販売時に含めてください。 このように注文の詳細を分けることで、レポートを作成し、請求書を生成し、返品や調整を正確に処理することができます。 このスキーマは、関心の明確な分離と関係の整合性を持ち、あなたのeコマースシステムの強固な基盤を形成します。 データモデルがあなたのプラットフォームの実世界のワークフローに整合したので、バックエンドに実装を開始する準備が整いました。 user 、 buyer 、そして seller テーブルを設定し、そこから外に構築を始めてください。 ダッシュボードでのクラスの設定 以下の手順に従って、back4appでエンティティを作成し、リンクします。 user 、 buyer 、 seller 、 cart 、 product 、 order 、そして orderdetails のクラスを定義し、継承、一対多、多対多のような関係を表すポインタを確立します。 データベースブラウザにアクセス アプリケーションのダッシュボードを開き、「user」という新しいクラスを作成します。 左側のメニューから データベース をクリックします。 back4app dashboard 「user」クラスの作成 back4appは自動的に userクラスを作成します。 user クラスには以下の列が含まれています ユーザー名 パスワード メールアドレス など usertype 列を追加するには、「新しい列を追加」を選択します。 add new column 列の名前、データの種類、デフォルト値などを提供してください。 add usertype column 注意 parseでは、すべてのクラスにデフォルトの objectid フィールドが含まれており、これは主キーとして機能します。これは、 objectid があなたのリレーショナルモデルにおける userid として実質的に機能することを意味します。 「バイヤー」クラスの作成 バイヤークラスを作成するには、 データベースブラウザ に戻り、 クラスを追加 を選択します。 create new class 「 カスタム 」を選択し、名前を buyer にします。 add buyer class 「 pointer 」という列を追加し、 user と名付け、 user クラスを指します。 create userid class これは、ベースユーザーの objectid を参照することによって「継承」をシミュレートします。 3\ 次の列を追加します 配送先住所 (文字列) 支払い情報 (文字列) 新しいバイヤーオブジェクトを作成するときは、 buyer user をそのユーザーのobjectidに設定することで、既存のユーザーレコードにリンクします。 「セラー」クラスの作成 同じプロセスを繰り返して、 セラー クラスを作成します。 ポインタ という名前のカラムを追加し、 user を user クラスを参照するようにします。これらの追加カラムを追加します 店舗名 (文字列) バイオ (文字列) 電話番号 (数字) 「プロダクト」クラスの作成 選択する クラスを作成 再度、名前を 製品 とします。 ポインタ 列を追加し、 seller と呼び、 seller クラスを指します(これにより、一人の売り手が多くの製品を持つことができる一対多の関係が強制されます)。次の列を追加します 名前 (文字列) 説明 (文字列) 価格 (数値) 在庫数量 (数値) アクティブかどうか (ブール) 画像 (配列) 製品画像を表すオブジェクトを格納します。 「カート」クラスの作成 クリックして クラスを作成 , クラスの名前を カート ポインタ 列を追加し、 user と呼び、 user クラスを指します(これにより、各カートが特定のユーザーにリンクされます)。次に、カート内のアイテムを表すために次の列を追加します アイテム (配列) 各カートアイテムを表すオブジェクトを格納します。各アイテムは { product 製品へのポインタ, quantity 数値 } のようなオブジェクトであることができます。 合計価格 (数値) 「注文」クラスの作成 新しいクラスを作成します order buyer クラスを指す pointer 列 buyer を追加します(1人のバイヤーが多くの注文を持つことを示します)。次の列を含めます orderdate (日付または文字列、好みに応じて) orderstatus (文字列) 「orderdetails」クラスの作成 最後に、クラスを作成します orderdetails 2つの pointer 列を追加します order → order を参照 product → product を参照 次に、残りの列を追加します quantity (数値) unitprice (数値) この設定により、 order と product の間に多対多の関係が可能になります orderdetails , 各注文には複数の製品が含まれる可能性があり、各製品は複数の注文に現れることができます。 関係の視覚化 クラスとそのポインタを反映した簡略化されたエンティティ リレーションシップ図(erd)を以下に示します dbschema2 これらのクラスが整ったら、 user クラスを通じてサインアップやログインを処理し、異なるユーザータイプのために特化したバイヤーまたはセラーのレコードを作成し、明確な外部キー参照を持つ製品または注文情報を保存できます。 このようにデータベースを構造化することで、あらゆるeコマースアプリケーションに対してクリーンでメンテナブルなデザインを得ることができます。 スキーマが整ったら、誰が商品をリストしたり、注文を行ったり、管理タスクを実行できるかを制限するために、認証と認可を設定する準備が整います。 次のステップでは、あなたが定義した役割とクラスに基づいて、安全なサインアップとログインフローを構成する方法を示します。 back4appによる認証の実装 安全な認証は、ユーザーアカウント、支払い詳細、個人データを保護するために、あらゆるeコマースプラットフォームの中心的な要素です。 back4appは、従来のユーザー名/パスワードの組み合わせからソーシャルログインやトークンベースのフローまで、複数の方法でこれを簡素化し、あなたのショッパーにシンプルで安全な体験を提供できるようにします。 eコマースアプリケーションで認証を設定するには、クラウドコードを利用します。クラウドコードは、独自のサーバーをホスティングすることなく、カスタムビジネスロジックを書くことでバックエンドの機能を拡張します。 これにより、注文を検証したり、手数料を計算したり、特定のイベントで通知をトリガーしたりすることができます—すべてback4appのインフラ内で。 これは、厳密な管理と迅速な実行が必要な敏感な取引やデータプロセスを処理する理想的な方法です。 back4appでクラウドコードを有効にするには、アプリのダッシュボードを開き、 cloud code セクションを見つけます。 find cloud code 「 cloud code 」をクリックすると、カスタム関数を実装し始めることができる main js が表示されます cloud code back4appで認証フローを実装するには、ユーザーのサインアップ、ログイン、ログアウトを行うために、クラウドコードにいくつかの関数をデプロイする必要があります。 新しいユーザーを登録する(デフォルトではバイヤー) 新しいユーザーを登録するには、parseのサインアップメソッドを使用します。 username , password , および email , のような標準フィールドを設定し、役割を示すカスタムフィールドを含めます: buyer または seller デフォルトでは、新しいユーザーは buyer の役割が割り当てられ、変更することを選択するまでそのままです。まず、クラウドコードのクラウドディレクトリに auth js ファイルを作成し、以下のコードブロックを追加します parse cloud define('signupuser', async (request) => { const { name, password, confirmpassword, email, shippingaddress, paymentinfo } = request params; // validate password match if (password !== confirmpassword) { throw new error('passwords do not match'); } const user = new parse user(); user set('username', name); user set('password', password); user set('email', email); user set('usertype', "buyer"); try { // sign up the user const signedupuser = await user signup(); console log('user registered ', signedupuser id); // create a buyer record linked to the new user const buyer = parse object extend('buyer'); const buyer = new buyer(); buyer set('user', signedupuser); buyer set('shippingaddress', shippingaddress); buyer set('paymentinfo', paymentinfo); const savedbuyer = await buyer save(); console log('buyer created ', savedbuyer id); return { success true, message 'user registered and buyer created', userid signedupuser id, buyerid savedbuyer id }; } catch (error) { console error('sign up failed ', error); throw new error('sign up failed ' + error message); } }); このクラウド関数は、購入者のユーザー登録を処理します。入力を検証し、新しいparseユーザーを作成し、役割を割り当て( buyer )、購入者特有のデータを別の購入者クラスに保存します。 ユーザーのログイン ユーザーを認証するには、parseの組み込みの parse user login メソッドを呼び出し、セッショントークンをクライアントに返します。 このロジックをクラウドコードバックエンドに実装するには、次のコードを auth js ファイルに追加します parse cloud define("loginuser", async (request) => { const { email, password } = request params; try { const user = await parse user login(email, password); // example block users with disabled flag if (user get("isdisabled")) { throw new parse error(403, "account is disabled"); } return { sessiontoken user getsessiontoken(), userid user id, }; } catch (error) { throw new parse error(101, "invalid credentials"); } }); この関数は、クライアントからメールアドレスとパスワードを受け取り、ユーザーの認証を試み、成功した場合はセッショントークンを返します。アカウントが無効としてフラグ付けされている場合は、アクセスをブロックし、明確なエラーをスローします。 ログアウト バックエンドのログアウト機能を実装するには、以下のコードブロックを auth js ファイルに追加します parse cloud define('logoutuser', async (request) => { const sessiontoken = request headers\['x parse session token']; if (!sessiontoken) { throw new error('session token required'); } const sessionquery = new parse query(' session'); sessionquery equalto('sessiontoken', sessiontoken); const session = await sessionquery first({ usemasterkey true }); if (session) { await session destroy({ usemasterkey true }); } return { success true, message 'session invalidated' }; }); これにより、バックエンドでセッションが安全に終了し、トークンのさらなる使用が防止されます。 これらのcloud code関数が整備されることで、次のような安全な認証システムが構築されます 役割ベースのロジックでユーザーを登録します セッショントークンを使用して認証とアクセスの承認を行います ユーザーアカウントを購入者特有の記録にリンクします クリーンなログインとログアウトのフローをサポートします back4appの組み込みセッションおよびメール検証ツールを活用します eコマースアプリのビジネスロジックを構築する eコマースプラットフォームでユーザー、製品、カート、取引を管理するために、back4appで一連のcloud code関数を定義します。 これらの関数を使用すると、セラー、製品、注文などのリソースを作成、読み取り、更新、削除(crud)しながら、ユーザーの役割に関連付けられた安全なアクセス制御を維持できます。 以下は、主要なエンティティでの作業方法を示すcloud functionsのセットです セラー , 製品 , カート , 注文 , および 注文詳細 各関数は特定のバックエンド操作を処理し、ユーーバーの検証と役割チェックを通じて権限が強制されることを保証します。 セラー プラットフォームで商品を提供したいユーザーのために、販売者プロフィールを作成します。 販売者の作成 この機能は、現在のユーザーの役割を販売者に更新し、 seller のストア情報を持つレコードを作成します。 これをクラウドコードに実装するには、まず seller js ファイルを作成し、以下のコードブロックを追加します parse cloud define('createseller', async (request) => { const currentuser = request user; // get the current logged in user if (!currentuser) { throw new error('user is not logged in'); } try { // check and update the user type const usertype = currentuser get('usertype'); if (usertype === 'buyer') { currentuser set('usertype', 'seller'); await currentuser save(); } // create the seller object const { businessname, bio, phone } = request params; const seller = parse object extend('seller'); const seller = new seller(); seller set('user', currentuser); seller set('storename', businessname); seller set('bio', bio); seller set('phonenumber', number(phone)); const newseller = await seller save(); console log('seller created ', newseller id); return { success true, message 'seller account created', sellerid newseller id }; } catch (error) { console error('error creating seller ', error); throw new error('error creating seller ' + error message); } }); これにより、認証されたユーザーのみが販売者になることができ、 seller レコードが対応するユーザーにポインタでリンクされます。 製品 製品は販売者によってリストされ、購入者によってブラウズ、クエリ、および購入されることができます。 これらの機能により、販売者は自分の製品を作成、取得、更新、削除でき、購入者に製品へのアクセスを提供します。 製品の作成 この機能は、ログインしている販売者のために新しい製品を追加し、最大3つの製品画像を添付します。クラウドコードに実装するには、 product js ファイルを作成し、以下のコードブロックを追加します parse cloud define("addproduct", async (request) => { // destructure parameters sent from the client const { name, description, price, quantityavailable, isactive, imagefiles } = request params; // optional check if user is logged in (request user is available when an active session exists) const currentuser = request user; if (!currentuser) { throw new error("unauthorized you must be logged in to add a product "); } // ensure the user is indeed a seller by checking their usertype const usertype = currentuser get("usertype"); if (usertype != "seller") { throw new error("this user is not a seller "); } const seller = parse object extend("seller"); const query = new parse query(seller); query equalto("userid", currentuser); // match the pointer field in seller const sellerobj = await query first({ usemasterkey true }); if (!sellerobj) { throw new error("no matching 'seller' object found for this user "); } if(!array isarray(imagefiles) || imagefiles length > 3) { throw new error("a maximum of 3 images are provided"); } // create the new product object const product = parse object extend("product"); const product = new product(); product set("seller", sellerobj); // pointer to the seller product set("name", name); product set("description", description); product set("price", number(price)); product set("quantityavailable", number(quantityavailable)); product set("isactive", isactive); product set("images", imagefiles); // save the product to the database try { const savedproduct = await product save(null, { usemasterkey true }); return savedproduct; } catch (error) { throw new error(`could not create product ${error message}`); } }); この関数は、ユーザーがログインしていて、販売者の役割を持っていること、対応する seller オブジェクトが存在すること、そして3つ以上の画像が提出されていないことを確認します。検証が完了すると、製品を保存し、販売者にリンクします。 すべての製品をクエリする この関数は、システム内のすべての製品を取得し、公開ストアフロントを構築するために使用できます。これをクラウドコードに実装するには、以下のコードブロックを products js ファイルに追加します parse cloud define("fetchallproducts", async (request) => { try { const product = parse object extend("product"); const query = new parse query(product); const products = await query find({ usemasterkey true }); // return products in a json friendly format return products map((product) => ({ id product id, sellerid product get("seller")? id, name product get("name"), description product get("description"), price product get("price"), quantityavailable product get("quantityavailable"), isactive product get("isactive"), createdat product createdat, updatedat product updatedat, })); } catch (error) { throw new error(`error fetching products ${error message}`); } }); この関数は、 usemasterkey フラグを使用して製品を取得し、フロントエンドでの使用のためにフォーマットします。 単一の製品をクエリする 購入者に詳細な製品情報を表示する際にこの関数を使用します。これをクラウドコードに実装するには、以下のコードブロックをあなたの products js ファイルに追加してください parse cloud define("getsingleproduct", async (request) => { const { productid } = request params; const currentuser = request user; // ensure user is logged in if (!currentuser) { throw new error("you must be logged in to retrieve product information "); } if (!productid) { throw new error("missing required parameter productid "); } // query the product class const product = parse object extend("product"); const query = new parse query(product); query equalto("objectid", productid); try { const product = await query first({ usemasterkey true }); if (!product) { throw new error("product not found "); } // return a json friendly object return { objectid product id, name product get("name"), description product get("description"), price product get("price"), quantityavailable product get("quantityavailable"), isactive product get("isactive"), // optionally return more fields like images, category, etc }; } catch (error) { throw new error(`error fetching product ${error message}`); } }); この関数は1つのアイテムの完全な製品詳細を返します。有効な productid が渡されていることを確認し、リクエスターがログインしていることを保証します。 売り手の製品を照会する この機能は、売り手が自分の製品をすべて取得できるようにします。これをクラウドコードに実装するには、以下のコードブロックをあなたの products js ファイルに追加してください parse cloud define("getsellerproducts", async (request) => { const currentuser = request user; // ensure the user is logged in if (!currentuser) { throw new error("you must be logged in to fetch seller products "); } // if your schema depends on verifying the user is truly a seller, you can check usertype const usertype = currentuser get("usertype"); if (usertype !== "seller") { throw new error("only sellers can view their own products "); } // if you want to retrieve products for the currently logged in seller // fetch the seller record that points to currentuser const seller = parse object extend("seller"); const sellerquery = new parse query(seller); sellerquery equalto("user", currentuser); const sellerrecord = await sellerquery first({ usemasterkey true }); if (!sellerrecord) { throw new error("no matching seller record found for the current user "); } // finally, fetch all products pointing to this seller const product = parse object extend("product"); const productquery = new parse query(product); productquery equalto("seller", sellerrecord); productquery limit(1000); // adjust or paginate as needed try { const products = await productquery find({ usemasterkey true }); // return a more friendly json structure return products map((prod) => ({ objectid prod id, name prod get("name"), description prod get("description"), price prod get("price"), quantityavailable prod get("quantityavailable"), isactive prod get("isactive"), createdat prod createdat, updatedat prod updatedat, })); } catch (error) { throw new error(`error fetching seller products ${error message}`); } }); この関数は、ユーザーが販売者であることを確認し、関連する seller レコードを見つけ、販売者へのポインタを使用して product クラスをクエリします。販売者はこれを使用して、自分のすべての製品を取得します。 製品の削除 この関数は、製品を作成した販売者のみがそれを削除できることを保証します。この機能をクラウドコードに実装するには、以下のコードブロックを products js ファイルに追加します parse cloud define("deleteproduct", async (request) => { const { productid } = request params; const currentuser = request user; if (!currentuser) { throw new error("you must be logged in to delete a product "); } const usertype = currentuser get("usertype"); if (usertype !== "seller") { throw new error("only sellers can delete products "); } if (!productid) { throw new error("missing required parameter productid "); } // 1 find the seller record for the current user const seller = parse object extend("seller"); const sellerquery = new parse query(seller); sellerquery equalto("user", currentuser); const sellerrecord = await sellerquery first({ usemasterkey true }); if (!sellerrecord) { throw new error("no matching seller record found for the current user "); } // 2 fetch the product with a query that ensures the user owns it const product = parse object extend("product"); const productquery = new parse query(product); productquery equalto("objectid", productid); productquery equalto("seller", sellerrecord); // must match the correct seller const product = await productquery first({ usemasterkey true }); if (!product) { throw new error("product not found or you do not have permission to delete it "); } // 3 destroy the product try { await product destroy({ usemasterkey true }); return { message "product deleted successfully ", productid }; } catch (error) { throw new error(`error deleting product ${error message}`); } }); この関数は、ユーザーの所有権を確認し、その後データベースから製品を削除します。 製品の更新 この機能を使用して、販売者が商品の詳細を変更できるようにします。これをクラウドコードに実装するには、以下のコードブロックをあなたの products js ファイルに追加します parse cloud define("updateproduct", async (request) => { const { productid, name, description, price, quantityavailable, isactive } = request params; const currentuser = request user; if (!currentuser) { throw new error("you must be logged in to update a product "); } const usertype = currentuser get("usertype"); if (usertype !== "seller") { throw new error("only sellers can update products "); } if (!productid) { throw new error("missing required parameter productid "); } // 1 find the seller record for the current user const seller = parse object extend("seller"); const sellerquery = new parse query(seller); sellerquery equalto("user", currentuser); const sellerrecord = await sellerquery first({ usemasterkey true }); if (!sellerrecord) { throw new error("no matching seller record found for the current user "); } // 2 fetch the product const product = parse object extend("product"); const query = new parse query(product); query equalto("objectid", productid); query equalto("seller", sellerrecord); // must match the seller to ensure ownership const product = await query first({ usemasterkey true }); if (!product) { throw new error("product not found or you do not have permission to modify it "); } // 3 update product fields if (name !== undefined) product set("name", name); if (description !== undefined) product set("description", description); if (price !== undefined) product set("price", price); if (quantityavailable !== undefined) product set("quantityavailable", quantityavailable); if (isactive !== undefined) product set("isactive", isactive); // 4 save changes try { const updatedproduct = await product save(null, { usemasterkey true }); return { objectid updatedproduct id, name updatedproduct get("name"), description updatedproduct get("description"), price updatedproduct get("price"), quantityavailable updatedproduct get("quantityavailable"), isactive updatedproduct get("isactive"), }; } catch (error) { throw new error(`error updating product ${error message}`); } }); これは、ユーザーが販売者であることを確認し、商品所有権を確認し、リクエストで提供されたフィールドのみを更新します。 カート 「 カート 」機能は、購入予定のアイテムを管理するためにバイヤーを可能にします。これらの機能には、カートに追加、数量の更新、内容の表示、アイテムの削除が含まれます。 カートに追加 この関数は、購入者のカートに商品を追加するか、既に存在する場合は数量を更新します。これを実装するには、 cart js ファイルを作成し、以下のコードブロックを追加します parse cloud define("addtocart", async (request) => { const { productid, quantity } = request params; // ensure there is a currently logged in user const currentuser = request user; if (!currentuser) { throw new error("you must be logged in to modify the cart "); } if (!productid || !quantity) { throw new error("missing required parameters productid and quantity "); } // 1 fetch or create the user's cart const cart = parse object extend("cart"); const cartquery = new parse query(cart); cartquery equalto("user", currentuser); let cart = await cartquery first({ usemasterkey true }); if (!cart) { cart = new cart(); cart set("user", currentuser); cart set("items", \[]); // initialize empty array cart set("totalprice", 0); // initialize price } // 2 fetch the product for price details (or any other attributes you need) const product = parse object extend("product"); const productquery = new parse query(product); productquery equalto("objectid", productid); const product = await productquery first({ usemasterkey true }); if (!product) { throw new error("product not found "); } const productprice = product get("price") || 0; // 3 insert or update the item in the cart const cartitems = cart get("items") || \[]; // check if this product is already in the cart const existingitemindex = cartitems findindex( (item) => item product objectid === productid ); if (existingitemindex >= 0) { // if product is already in cart, update the quantity cartitems\[existingitemindex] quantity += quantity; } else { // otherwise, add a new entry cartitems push({ product { type "pointer", classname "product", objectid productid, }, quantity quantity }); } // 4 recalculate total price // e g , summation of productprice quantity for each cart item let total = 0; for (const item of cartitems) { if (item product objectid === productid) { // use productprice from the newly fetched product total += productprice item quantity; } else { // this is a simplified approach ideally, you'd also store a price attribute on each item // or re fetch each product's price // for demonstration, we'll just skip them } } cart set("items", cartitems); cart set("totalprice", total); // 5 save the cart object try { const savedcart = await cart save(null, { usemasterkey true }); return { message "cart updated successfully", cartid savedcart id, items savedcart get("items"), totalprice savedcart get("totalprice"), }; } catch (error) { throw new error(`error saving cart ${error message}`); } }); これは、ユーザーが認証されていることを確認し、商品を検証し、アイテムリストを更新し、カートの合計価格を再計算します。 カートのクエリ 現在のユーザーのカートの内容を、製品の詳細と小計を含めて取得します。これを実装するには、以下のコードブロックをあなたの cart js ファイルに追加してください parse cloud define("getcart", async (request) => { if (!request user) throw "user must be logged in "; const user = request user; const cart = parse object extend("cart"); const cartquery = new parse query(cart); cartquery equalto("user", user); cartquery include("items product"); // deep include const cart = await cartquery first(); if (!cart) return { items \[], totalprice 0 }; const items = cart get("items") || \[]; const parseditems = \[]; for (const item of items) { const product = item product; parseditems push({ productid product id, name product get("name"), price product get("price"), image product get("image")? url() || null, quantity item quantity, subtotal product get("price") item quantity, }); } return { cartid cart id, items parseditems, totalprice cart get("totalprice") || 0, }; }); この関数は include("items product") を使用して、1つのクエリでネストされた製品データを取得します。 カートの編集 カート内のアイテムの数量を更新するか、新しい数量がゼロの場合は完全に削除します。これを実装するには、以下のコードブロックをあなたの cart js ファイルに追加します parse cloud define("updatecart", async (request) => { const { productid, newquantity } = request params; const currentuser = request user; if (!currentuser) { throw new error("you must be logged in to update the cart "); } if (!productid || newquantity === undefined) { throw new error("missing required parameters productid and newquantity "); } // fetch or create the user's cart const cart = parse object extend("cart"); const cartquery = new parse query(cart); cartquery equalto("user", currentuser); let cart = await cartquery first({ usemasterkey true }); if (!cart) { // if there's no existing cart, create one cart = new cart(); cart set("user", currentuser); cart set("items", \[]); cart set("totalprice", 0); } let cartitems = cart get("items") || \[]; // find the item matching the productid const itemindex = cartitems findindex( (item) => item product id === productid ); if (itemindex === 1) { throw new error("product not found in cart please add it first "); } // if newquantity <= 0, remove the item from the cart if (newquantity <= 0) { cartitems splice(itemindex, 1); } else { // otherwise, update the quantity cartitems\[itemindex] quantity = newquantity; } // recalculate total by fetching current prices from each product let total = 0; if (cartitems length > 0) { const product = parse object extend("product"); // for each item in the cart, fetch the product to get its price for (const item of cartitems) { const productquery = new parse query(product); productquery equalto("objectid", item product objectid); const product = await productquery first({ usemasterkey true }); if (product) { const productprice = product get("price") || 0; total += productprice item quantity; } } } // update and save the cart cart set("items", cartitems); cart set("totalprice", total); try { const updatedcart = await cart save(null, { usemasterkey true }); return { message "cart updated successfully", cartid updatedcart id, items updatedcart get("items"), totalprice updatedcart get("totalprice"), }; } catch (error) { throw new error(`error updating cart ${error message}`); } }); 更新中にすべての製品価格を再検証して、合計の正確性を確保します。 カートからの削除 この関数は、ユーザーのカートから商品を削除します。これを実装するには、以下のコードブロックをあなたの cart js ファイルに追加してください parse cloud define("removefromcart", async (request) => { const { productid } = request params; const currentuser = request user; if (!currentuser) { throw new error("you must be logged in to remove items from the cart "); } if (!productid) { throw new error("missing required parameter productid "); } // fetch the user's cart const cart = parse object extend("cart"); const cartquery = new parse query(cart); cartquery equalto("user", currentuser); const cart = await cartquery first({ usemasterkey true }); if (!cart) { throw new error("no existing cart found for this user "); } let cartitems = cart get("items") || \[]; // filter out the item with the specified productid const filtereditems = cartitems filter( (item) => item product id !== productid ); // if nothing changed, the product wasn't in the cart if (filtereditems length === cartitems length) { throw new error("product not found in cart "); } // recalculate the cart total let total = 0; if (filtereditems length > 0) { const product = parse object extend("product"); for (const item of filtereditems) { const productquery = new parse query(product); productquery equalto("objectid", item product objectid); const product = await productquery first({ usemasterkey true }); if (product) { const productprice = product get("price") || 0; total += productprice item quantity; } } } // update and save the cart cart set("items", filtereditems); cart set("totalprice", total); try { const updatedcart = await cart save(null, { usemasterkey true }); return { message "item removed from cart", cartid updatedcart id, items updatedcart get("items"), totalprice updatedcart get("totalprice"), }; } catch (error) { throw new error(`error removing item from cart ${error message}`); } }); これは、カート配列からアイテムをフィルタリングし、保存する前に合計を再計算します。 注文 これらの関数は、注文の作成から売り手の注文履歴の取得まで、注文プロセスを管理します。 注文の作成 この関数は、購入者のカートを正式な注文に変換します。これを実装するには、 order js ファイルを作成し、以下のコードブロックを追加します parse cloud define("createorder", async (request) => { const currentuser = request user; if (!currentuser) { throw new error("you must be logged in to place an order "); } // ensure the user is a buyer (assumes buyer class with user pointer) const buyer = parse object extend("buyer"); const buyerquery = new parse query(buyer); buyerquery equalto("user", currentuser); const buyer = await buyerquery first({ usemasterkey true }); if (!buyer) { throw new error("only buyers can place orders "); } // retrieve the cart for the user const cart = parse object extend("cart"); const cartquery = new parse query(cart); cartquery equalto("user", buyer get("user")); // assuming the user pointer is stored in buyer const cart = await cartquery first({ usemasterkey true }); if (!cart || !cart get("items") || cart get("items") length === 0) { throw new error("cart is empty"); } const items = cart get("items"); let totalamount = 0; // adjust stock and calculate total order price const orderdetails = \[]; for (const item of items) { const productid = item product id; const quantity = item quantity; const product = parse object extend("product"); const productquery = new parse query(product); productquery equalto("objectid", productid); const product = await productquery first({ usemasterkey true }); if (!product) { throw new error(`product with id ${productid} not found`); } const availablequantity = product get("quantityavailable"); if (availablequantity < quantity) { throw new error(`not enough stock for product ${product get("name")}`); } // reduce the product quantity product set("quantityavailable", availablequantity quantity); await product save(null, { usemasterkey true }); const unitprice = product get("price"); totalamount += unitprice quantity; // prepare order detail entry orderdetails push({ product, quantity, unitprice }); } // create the new order object const order = parse object extend("order"); const order = new order(); order set("buyer", buyer); // set the buyer pointer order set("total", totalamount); // set the total amount order set("orderstatus", "pending"); // set the order status order set("orderdate", new date()); // set the order date // save the order to the database try { const savedorder = await order save(null, { usemasterkey true }); // create orderdetails for each item const orderdetails = parse object extend("orderdetails"); for (const detail of orderdetails) { const orderdetail = new orderdetails(); orderdetail set("order", savedorder); orderdetail set("product", detail product); orderdetail set("quantity", detail quantity); orderdetail set("unitprice", detail unitprice); await orderdetail save(null, { usemasterkey true }); } // optionally, clear the cart after saving the order cart set("items", \[]); await cart save(null, { usemasterkey true }); return savedorder; } catch (error) { throw new error(`could not create order ${error message}`); } }); それは購入者を検証し、在庫の可用性を確認し、製品の在庫を減らし、両方を保存します 注文 と 注文詳細 、そして成功した注文の後にカートをクリアします。 売り手の注文を取得する この機能は、現在の売り手が販売している製品を含むすべての注文を取得します。これを実装するには、以下のコードブロックをあなたの order js ファイルに追加してください parse cloud define("getsellerorders", async (request) => { const currentuser = request user; if (!currentuser) { throw new error("you must be logged in to view your orders "); } if (currentuser get("usertype") !== "seller") { throw new error("only sellers can access this endpoint "); } try { const seller = parse object extend("seller"); const seller = await new parse query(seller) equalto("userid", currentuser) first({ usemasterkey true }); if (!seller) { throw new error("seller profile not found "); } // find products by this seller const product = parse object extend("product"); const productquery = new parse query(product); productquery equalto("seller", seller); const products = await productquery find({ usemasterkey true }); if (!products || products length === 0) { return \[]; } // get product ids const productids = products map(product => product id); // find order details containing these products const orderdetails = parse object extend("orderdetails"); const orderdetailsquery = new parse query(orderdetails); orderdetailsquery containedin("product", products); const orderdetails = await orderdetailsquery find({ usemasterkey true }); if (!orderdetails || orderdetails length === 0) { return \[]; } const orderids = \[ new set(orderdetails map(detail => detail get("order") id))]; // query orders for these ids const order = parse object extend("order"); const orderquery = new parse query(order); orderquery containedin("objectid", orderids); orderquery include("buyer"); const orders = await orderquery find({ usemasterkey true }); // return the orders return orders map(order => ({ id order id, total order get("total"), status order get("orderstatus"), date order get("orderdate"), buyer order get("buyer") })); } catch (error) { throw new error(`could not fetch orders ${error message}`); } }); これは、売り手が所有する製品を特定し、 orderdetails をその製品に対してクエリし、対応する注文を取得し、購入者情報を含む簡略化された構造を返します。 注文ステータスの更新 この機能は、注文のステータスを「保留中」と「完了」の間で切り替えます。これを実装するには、以下のコードブロックをあなたの order js ファイルに追加します parse cloud define("updateorder", async (request) => { const { orderid } = request params; const currentuser = request user; if (!currentuser) { throw new error("you must be logged in to view your orders "); } if (currentuser get("usertype") !== "seller") { throw new error("only sellers can access this endpoint "); } // validate the input parameter if (!orderid) { throw new error("missing required field orderid"); } try { // find the order with the specified orderid const order = parse object extend("order"); const query = new parse query(order); query equalto("objectid", orderid); const order = await query first({ usemasterkey true }); if (!order) { throw new error("order not found"); } // check current status // update the order status if (order get("orderstatus") === "completed") { order set("orderstatus", "pending"); } else { order set("orderstatus", "completed"); } // save the updated order await order save(null, { usemasterkey true }); return { success true, message "order status updated to completed" }; } catch (error) { throw new error(`could not update order status ${error message}`); } }); これにより、売り手のみがこのアクションを実行でき、ステータスを更新する前に注文が存在することが保証されます。 これらのバックエンド機能は、あなたのeコマースアプリに、cloud codeを使用してback4app内で製品リスト、購入者カート、注文、売り手在庫などのすべてのコア操作を安全に管理する能力を提供します。 これらのクラウド機能に外部からアクセスできるようにするためには、すべてのクラウドコードファイルを main js ファイルに登録する必要があります。これを行うには、以下のコードブロックを main js ファイルに追加します //main js require(" /auth js") require(" /cart js") require(" /order js") require(" /product js") require(" /seller js") フロントエンドの作成 視覚的にeコマースのストアフロントを構築するために、v0 devを使用します。これを行うには、まず v0 dev http //v0 dev を訪問してください。 まだアカウントを持っていない場合は、アカウントを作成してください。アカウントが設定されると、フロントエンドの作成を開始できます。 フロントエンドを作成するには、以下のプロンプトを入力してください create an e commerce web application with authentication features and a buyer and seller feature このプロンプトは、要求された機能を持つnext jsのeコマースアプリを構築します。 create ecommerce app プレビュー機能を使用すると、アプリケーションをナビゲートして、すべてが期待通りに動作することを確認できます。 何か問題がある場合や、さらに機能を追加したい場合は、別のプロンプトを渡すとv0がアプリケーションを修正します。 例えば、次のプロンプトを渡します the seller can create, add, delete and update products このプロンプトはアプリを修正し、セラーがcrud操作を行うページを作成します。 modify ecommerce app 最後に、プロンプトを渡します do not sort products by categories and remove the categories page and input fields, remove the second and third step from the process of becoming a seller and add a bio input field to the first step, users should only sign in with email, and enhance the seller functionality to fully support product management (crud operations) このプロンプトはアプリケーションに最終的な編集を加えます。修正されたアプリケーションのプレビューを取得するには、上の画像のハイライトされたセクションの「 表示 」ボタンをクリックしてください。 アプリケーションが望んでいるものであることを確認したら、次のステップはプロジェクトをローカルデバイスに取得することです。 これを行うには、v0がプロンプト応答で生成するダウンロードボタンをクリックします。 download button ボタンをクリックすると、リンクと ダウンロードzip ボタンを含むドロップダウンメニューが表示されます。 dropdown menu 次に、 ダウンロードzip ボタンをクリックします。zipのダウンロードが完了したら、ターミナルを開き、希望のディレクトリにecommerce appという新しいフォルダーを作成します。 mkdir ecommerce app 次に、zipフォルダーの内容をecommerce appフォルダーに抽出します。ターミナルでecommerce appディレクトリに移動し、必要な依存関係をインストールします。これを行うには、次のコマンドを実行します cd ecommerce app npm install 依存関係のインストールが完了したら、ターミナルで npm run dev コマンドを実行して、ローカルホストサーバーでプロジェクトを表示します。 フロントエンドとバックエンドの統合 フロントエンドをback4appバックエンドのcloud code関数に接続するには、parse javascript sdkを使用します。 sdkは、ユーザーを認証し、バックエンド機能を呼び出し、アプリのデータモデルと安全かつ効率的に対話することを可能にします。 sdkをセットアップするには、ターミナルでプロジェクトのディレクトリに次のコマンドを実行します npm install parse 次に、接続を構成するために lib/parse js ファイルを作成します。このファイルに、以下のコードブロックを入力します import parse from "parse/dist/parse min js"; parse initialize("your app id", "your javascript key"); parse serverurl = "https //parseapi back4app com"; export default parse; your app id と your javascript key を、back4appダッシュボードの app settings にある資格情報で置き換えます。この基本的な構成により、sdkは特定のプロジェクトに接続する方法を知ることができます。 フロントエンドをバックエンドに接続したので、back4appアプリケーションで定義したクラウドコード関数を呼び出すための関数を書くことができます。 クライアントでの認証 このセクションでは、parse sdkを使用してフロントエンドでユーザーのサインアップ、ログイン、ログアウトを処理する方法を示します。 各関数は、back4appバックエンドで実装したクラウドコード関数またはsdkメソッドに対応しています。 アプリケーション内の app/auth/register ディレクトリには、ユーザーのサインアップのロジックが含まれています。 page tsx で formdata ステートを定義します。このステートは、ユーザーをサインアップするために必要な資格情報を保持します。 formdata ステートは次のようになります const \[formdata, setformdata] = usestate({ name "", email "", password "", confirmpassword "", shippingaddress "", paymentinfo "", }); 渡された資格情報を使用して、 formdata , あなたは back4app アプリケーションで定義した signupuser クラウド関数を呼び出すことができます。 lib/parse js から parse をインポートすることを確認してください。 import parse from "@/lib/parse"; const handlesubmit = async(e react formevent) => { e preventdefault(); try { const response = await parse cloud run("signupuser", { name formdata name, email formdata email, password formdata password, confirmpassword formdata confirmpassword, shippingaddress formdata shippingaddress, paymentinfo formdata paymentinfo, }); console log("signup successful ", response); } catch (error any) { console error("signup failed ", error); } } この関数は、サインアップフォームを送信すると実行されます。 signupuser クラウド関数を cloud code で定義します。 この関数は、ユーザーの資格情報と購入者特有のデータ(配送および支払い情報など)を渡します。 最後に、操作が成功した場合は応答をログに記録し、失敗した場合はエラーを表示します。 ユーザーのサインイン ユーザーをログインさせるには、 loginuser クラウドコード関数をバックエンドで使用します。アプリケーションの app/auth/login ディレクトリに移動します。 その中で、 page tsx ファイルでは、v0がユーザーの資格情報を含む状態を作成します。ユーザーをログインさせるには、その資格情報を loginuser 関数に渡すだけです。 例えば import parse from "@/lib/parse"; const handlesubmit = async (e react formevent) => { e preventdefault(); setisloading(true); try { const result = await parse cloud run("loginuser", { email, password }); // log in using returned session token await parse user become(result sessiontoken); console log("user logged in with session ", result sessiontoken); setisloading(false); router push("/"); // proceed to app } catch (error) { console error("login failed ", error); } }; この関数は、クラウド関数 loginuser を呼び出し、ユーザーのログイン資格情報をサーバーに送信します。有効なセッショントークンを受け取った後、 parse user become を使用してクライアントを認証し、セッションをログインしたユーザーに属するものとして確立します。 ユーザーのログアウト ユーザーを安全にログアウトするには、次の両方を呼び出します。 parse user logout() とあなたのカスタム logoutuser cloud code 関数。この操作により、セッショントークンがクライアントとサーバーの両方でクリアされます。 ログアウトボタンはヘッダーコンポーネントにあります。このコンポーネントは、ルートディレクトリの components フォルダーにあります。コンポーネント内で、この関数を定義します const handlelogout = async () => { const sessiontoken = parse user current()? get("sessiontoken"); if (sessiontoken) { await parse user become(sessiontoken); // ensures proper context await parse cloud run("logoutuser"); // delete the session on server } await parse user logout(); // clear the local session }; この関数はユーザーをログアウトし、クライアントとサーバーの両方でセッションをクリアします。コンポーネントのjsx部分で、 onclick イベントを使用してログアウトボタンに関数をバインドします。 例えば \<dropdownmenuitem onclick={handlelogout}> logout \</dropdownmenuitem> 販売者オンボーディング このセクションでは、バイヤーがアカウントをアップグレードし、販売者として登録するための関数を示します。アプリケーション内で、 app/seller/register ディレクトリに移動します。 このディレクトリには、 page tsx ファイルがあり、ここで createseller クラウド関数を呼び出す関数を定義します。この関数は、現在のユーザーの販売者プロフィールを作成する責任があります。 この page tsx ファイルで、formdataの状態を次のように変更します const \[formdata, setformdata] = usestate({ phone "", bio "", businessname "", }); v0によって作成された handlesubmit 関数のロジックを以下のロジックに置き換えます。 const handlesubmit = async (e react formevent) => { e preventdefault(); try { const result = await parse cloud run("createseller", { businessname formdata businessname, bio formdata bio, phone formdata phone, }); console log("seller created ", result sellerid); } catch (error) { console error("error creating seller ", error); } }; フォームを送信するときにこの関数を呼び出して、ユーザーのためにセラープロフィールを作成します。 製品管理 フロントエンドでこれらの関数呼び出しを使用して、セラーとして製品操作を管理します。 製品の作成 データベースに製品を追加するクラウドコード関数を呼び出すには、 app/seller/products/new ディレクトリを見つけます。このディレクトリ内には、 page tsx ファイルがあり、このファイルにはフォームがあります。このフォームは、画像を含む製品の情報を受け取ります。 次のように product ステートを変更します const \[product, setproduct] = usestate({ name "", price "", description "", stock "", status "active", }); また、画像を保持するためのステートを作成します。 const \[images, setimages] = usestate\<file\[]>(\[]); 次に、画像をデータベースに追加するための関数を書きます。この関数は次のようになります const handleimageupload = async (event) => { event preventdefault(); try { let name = "image jpg"; const file = new parse file(name, event target files\[0]); const photo = await file save(); setimages((prev) => \[ prev, photo]); console log("file saved ", file); alert(`image uploaded successfully`); } catch (error) { console error("error saving file ", error); } }; この関数をファイルアップロードを担当する入力にバインドします。次に、handlesubmit関数を定義します。この関数は、必要な情報を渡しながら addproduct クラウドコードを呼び出します。 このように const handlesubmit = async (e react formevent) => { e preventdefault(); try { interface addproductparams { name string; description string; price number; quantityavailable number; isactive boolean; } interface addproductresponse { id string; } parse cloud run\<addproductresponse, addproductparams>("addproduct", { name product name, description product description, price product price, quantityavailable product stock, isactive product status === "active", imagefiles images, }) then((response) => { console log("product created ", response id); }); } catch (error) { console error("error creating product ", error); } }; この呼び出しは、画像を含む製品データをバックエンドに送信して新しいリスティングを作成します。 すべての製品を取得 この呼び出しは、公開製品リスティングまたはホームページフィードの全カタログを取得します。 fetchallproducts クラウド関数を呼び出す関数を作成するには、 app/products ディレクトリに移動します。 「 page tsx 」ファイルに、このコードを追加します interface product { id string; name string; description string; price number; image? string; } const \[products, setproducts] = usestate\<product\[]>(\[]); async function getallproducts() { try { const response = await parse cloud run("fetchallproducts"); console log("products fetched ", response); return response; } catch (error) { console error("error fetching products ", error); throw error; } } 上記のコードブロックは、製品のインターフェースと「 products 」ステートを定義しています。「 getallproducts 」関数は、データベース上のすべての製品を取得するために、クラウド関数「 fetchallproducts 」を呼び出します。 「 getallproducts 」関数を、ページの読み込み時に製品を取得してレンダリングするために、「 useeffect() 」フック内で呼び出す必要があることに注意してください。 このように useeffect(() => { getallproducts() then((products) => { setproducts(products); }) catch((error) => { console error("error fetching products ", error); }); }, \[]); 単一製品を取得する この呼び出しは、特定の製品に関する詳細情報を取得します。 \[id] フォルダーに移動します。あなたの app/products ディレクトリ。このフォルダーには、単一製品の詳細を取得するためのロジックを含む page tsx ファイルがあります。 page tsx ファイルに、このコードを書きます interface product { id string; name string; description string; price number; images? string\[]; quantityavailable number; } const \[product, setproduct] = usestate\<product>({ id "", name "", description "", price 0, images \[], quantityavailable 0, }); async function getsingleproduct() { try { const response = await parse cloud run("getsingleproduct", {productid params id}); console log("product fetched ", response); return response; } catch (error) { console error("error fetching products ", error); throw error; } } useeffect(() => { getsingleproduct() then((product) => { setproduct(product); }) catch((error) => { console error("error fetching products ", error); }); }, \[]); このコードブロックは、productオブジェクトのインターフェースを定義し、デフォルト値を持つ状態変数 product を設定します。 getsingleproduct 関数は、ルートパラメータからの productid を使用して、あなたの getsingleproduct クラウドコード関数を呼び出します。この useeffect フックは、コンポーネントがマウントされたときに一度だけ実行されます。 製品の更新 売り手が製品を編集する際は、更新されたフィールドを使ってこの関数を呼び出します。製品を更新するには、新しいデータを収集する入力フィールドを含むフォームが必要です。このフォームは、 page tsx ファイルにあります。 app/seller/products/\[id]/edit ディレクトリにあります。 製品を更新する前に、製品を取得する必要があります。そのためには、このコードをあなたの page tsx ファイルに追加してください。 const \[product, setproduct] = usestate({ id "", name "", price "", description "", stock "", images \["", "", ""], status "active", }); useeffect(() => { async function fetchproduct() { try { const response = await parse cloud run("getsingleproduct", { productid params id, }); setproduct({ id response objectid, name response name || "", price string(response price ?? "0"), description response description || "", stock string(response quantityavailable ?? "0"), // fallback to placeholders if actual image data is not provided images \[ response image1 || `/placeholder svg?height=500\&width=500\&text=product`, response image2 || `/placeholder svg?height=500\&width=500\&text=productview+2`, response image3 || `/placeholder svg?height=500\&width=500\&text=productview+3`, ], status response isactive ? "active" "out of stock", }); } catch (error) { console error("error fetching product ", error); } } fetchproduct(); }, \[]); このコードは、あなたのback4appバックエンドから単一の製品の詳細を取得し、 getsingleproduct クラウドコード関数を使用して、詳細をコンポーネントの状態に保存します。これは、変更する前にフォームのデフォルト値として表示されます。 製品の詳細を取得した後、フォームを使用して詳細を変更できます。フォームに記入した後、フォームを送信すると、 handlesubmit 関数が呼び出され、製品を更新するためのロジックが保持されます。 あなたの handlesubmit 関数は次のようになります const handlesubmit = async (e react formevent) => { e preventdefault(); try { await parse cloud run("updateproduct", { productid product id, name product name, description product description, price parsefloat(product price), quantityavailable parseint(product stock, 10), isactive product status === "active", }); } catch (error) { console error("error updating product ", error); } }; この関数はあなたの updateproduct cloud code 関数を呼び出し、更新された商品データを含むオブジェクトを送信します。 商品を削除する この関数を使用して、売り手からの確認後に商品を削除します。商品を削除するには、商品を削除するために必要なロジックを保持する handledelete 関数を追加する必要があります。 あなたは handledelete 関数を page tsx ファイルの app/seller/products/\[id]/edit ディレクトリに定義します。 const handledelete = async () => { const confirmdelete = confirm( "are you sure you want to delete this product? this action cannot be undone " ); if (!confirmdelete) return; try { await parse cloud run("deleteproduct", { productid product id, }); } catch (error) { console error("error deleting product ", error); } }; この handledelete 関数は、バックエンドで定義された deleteproduct クラウドコード関数を呼び出す前に、ユーザーの意図を確認します。 handledelete 関数をコンポーネントの jsx 部分の削除ボタンにバインドします。 例えば \<button type="button" variant="destructive" onclick={handledelete} \> \<trash2 classname="h 4 w 4 mr 2" /> delete product \</button> 売り手の商品の取得 これを使用して、現在ログインしている売り手が所有するすべての商品を取得します。売り手の商品を取得するための関数を page tsx ファイルの app/seller/dashboard ディレクトリに定義します。 ファイルにこのコードを書いてください const \[products, setproducts] = usestate< { objectid string; name string; price number; quantityavailable number; sales number; isactive boolean; }\[] \>(\[]); useeffect(() => { async function fetchproducts() { try { const result = await parse cloud run("getsellerproducts"); setproducts(result); } catch (error) { console error("error fetching products ", error); } }; fetchproducts(); }, \[]); このコードブロックは、 products ステートを定義し、 useeffect フックを使用して、 fetchproducts 関数を一度ロード時に呼び出します。 この fetchproducts 関数は、 getsellerproducts クラウドコード関数を呼び出し、 products ステートを呼び出し結果で更新します。 カート管理 これらの呼び出しにより、購入者は注文を出す前にショッピングカートを管理できます。 カートに追加 この呼び出しは、選択した商品をユーザーのカートに追加するか、その数量を増やします。 page tsx ファイルを app/products ディレクトリに見つけて、次のコード行を追加してください async function handleaddtocart(productid string, quantity number = 1) { try { const response = await parse cloud run("addtocart", { productid, quantity, }); console log("add to cart success ", response); } catch (error) { console error("failed to add to cart ", error); } } この関数は、 handleaddtocart がproductidとオプションの数量を引数として受け取り、バックエンドで addtocart クラウドコードを呼び出します。この関数は、ユーザーが商品カードの「カートに追加」ボタンをクリックしたときに実行されるべきです。 例えば \<button classname="w full" size="sm" onclick={() => handleaddtocart(product id)}> \<shoppingcart classname="h 4 w 4 mr 2" /> add to cart \</button> ファイルにこのコードを書いてください カートを見る これを使用してカートの内容を取得し、ライブカートの概要をレンダリングします。これを行うには、 page tsx ファイルに移動し、 app/cart ディレクトリ内で、以下のコードブロックを追加します interface cartitem { id string; // product's objectid name string; price number; quantity number; image string; } const \[cartitems, setcartitems] = usestate\<cartitem\[]>(\[]); useeffect(() => { async function fetchcart() { try { const response = await parse cloud run("getcart"); const parsed = response items map((item) => ({ id item productid, name item name, price item price, quantity item quantity, image item image, })); setcartitems(parsed); } catch (error) { console error("failed to fetch cart ", error); } } fetchcart(); }, \[]); 上記のコードブロックは、インターフェース cartitem を使用してフロントエンドのカートデータの形状を定義し、カートアイテムを保持する cartitems ステートを定義します。また、 useeffect を使用して、ページロード時に一度だけ fetchcart 関数をトリガーします。 この fetchcart 関数は、クラウドコード関数 getcart を呼び出し、バックエンドからカートを取得します。 それは、バックエンド形式をフロントエンド形式に変換するために map メソッドを使用し、処理されたカートデータを cartitems ステートに保存します。 カートアイテムの更新 この呼び出しは、カート内の特定の商品の数量を変更します。 page tsx ファイルは app/cart ディレクトリ内にあり、 updatequantity 関数が見つかります。その関数を以下のものに置き換えてください const updatequantity = async (productid string, newquantity number) => { if (newquantity < 1) return; try { await parse cloud run("updatecart", { productid, newquantity, }); console log("updated the cart"); } catch (error) { console error("failed to update cart item ", error); } }; この関数は2つのパラメータを取ります。 productid とユーザーが設定したい更新された数量 newquantity この関数はあなたの updatecart クラウド関数を呼び出し、 productid と newquantity の両方をパラメータとして送信します。 この関数を +/ ボタンにバインドするには、 onclick イベントを使用します。 このように \<div classname="flex items center gap 2"> \<button variant="outline" size="icon" classname="h 8 w 8" onclick={() => updatequantity(item id, item quantity 1)} \> \<minus classname="h 3 w 3" /> \<span classname="sr only">decrease quantity\</span> \</button> \<span classname="w 8 text center">{item quantity}\</span> \<button variant="outline" size="icon" classname="h 8 w 8" onclick={() => updatequantity(item id, item quantity + 1)} \> \<plus classname="h 3 w 3" /> \<span classname="sr only">increase quantity\</span> \</button> \</div> カートから削除 アイテムをカートから完全に削除します。これを行うには、以下のコードブロックを page tsx ファイルに追加します。 app/cart ディレクトリ内のこのファイルに。 const removeitem = async (productid string) => { try { await parse cloud run("removefromcart", { productid, }); setcartitems((items) => items filter((item) => item id !== productid)); } catch (error) { console error("failed to remove item ", error); } }; この関数、 removeitem , は特定の製品をユーザーのカートから削除します。バックエンドでクラウド関数 removefromcart を呼び出すことによって。関数は、変更を反映するためにフロントエンドのローカルカート状態も更新します。 この関数を「アイテムを削除」ボタンにバインドします。次のように \<button variant="ghost" size="icon" classname="h 8 w 8 text muted foreground" onclick={() => removeitem(item id)} \> \<trash2 classname="h 4 w 4" /> \<span classname="sr only">remove item\</span> \</button> 注文管理 これらの機能は、バイヤーとセラーのための注文の作成と追跡を処理します。 注文を作成する これは、ユーザーのカートの現在の内容から注文を作成します。この関数は、 page tsx ファイルで、 app/cart ディレクトリに定義します。注文を作成するロジックを持つ関数は次のようになります const handleorder = async () => { try { const user = parse user current(); if (!user) { throw new error("you must be logged in to place an order "); } const result = await parse cloud run("createorder"); console log("order created successfully ", result); } catch (error any) { console error("failed to create order ", error); } }; この関数、 handleorder は、あなたのback4appバックエンドを通じて注文を行うことによってチェックアウトプロセスを処理します。 ユーザーがログインしているかどうかを確認し、その後、 createorder クラウド関数を呼び出して、現在のユーザーのカートを正式な注文に変換します。 チェックアウト時にこれを呼び出すには、 onclick イベントを使用して関数を「チェックアウト」ボタンにバインドします。 例えば \<button classname="w full" onclick={handleorder}>proceed to checkout\</button> 販売者の注文を表示 販売者は、この呼び出しを使用して自分の製品を含む注文を表示できます。この関数を page tsx ファイルに定義し、 app/seller/dashboard ディレクトリに追加します。次のコード行を page tsx ファイルに追加します。 const \[ordersdata, setordersdata] = usestate<{ id string; total number; buyer string; date date; status string; }\[] \>(\[]); async function fetchsellerorders() { try { const orders = await parse cloud run("getsellerorders"); console log("seller orders ", orders); setordersdata(orders); } catch (err) { console error("error fetching seller orders ", err); } }; このコードは、販売者の製品を含む注文のリストを取得し、それを販売者のダッシュボードまたは注文管理ビューに表示するためにローカルステート ordersdata に保存します。関数がページロード時に一度実行されるように、 useeffect フック内で関数を呼び出すことを確認してください。 注文を更新する これは、注文のステータスを「保留中」と「完了」の間で切り替えます。この関数を page tsx ファイルに定義し、 app/seller/dashboard ディレクトリに追加します。関数は次のようになります const handleupdate = async (id string) => { const confirmupdate = confirm( "are you sure you want to update this order?" ); if (!confirmupdate) return; try { await parse cloud run("completeorder", { orderid id, }); console log("updated order"); } catch (error) { console error("error updating order ", error); } };const handleupdate = async (id string) => { const confirmupdate = confirm( "are you sure you want to update this order?" ); if (!confirmupdate) return; try { await parse cloud run("completeorder", { orderid id, }); console log("updated order"); } catch (error) { console error("error updating order ", error); } }; この関数、 handleupdate は、あなたのback4appバックエンドで「 completeorder 」という名前のクラウド関数を呼び出すことによって、既存の注文のステータスを更新します。この関数をコンポーネントのjsx部分の「編集」ボタンにバインドします。 このように \<button variant="ghost" size="icon" onclick={() => {handleupdate(order id)}}> \<pencil classname="h 4 w 4" /> \<span classname="sr only">view\</span> \</button> back4appコンテナでのフロントエンドのデプロイ back4appコンテナは、フロントエンドアプリケーションをパッケージ化してデプロイするための効率的な方法を提供します。 別々のホスティングサービスを使う代わりに、すべての依存関係をdockerイメージ内に保存することで、一貫したパフォーマンスを確保し、メンテナンスを簡素化できます。 このコンテナベースのアプローチは、next jsアプリケーションに特に便利で、ビルドを速度と信頼性のために最適化できます。 アプリケーションをコンテナ化するには、 dockerfile を作成して、アプリがどのようにビルドされ、提供されるかを定義します。以下は、next jsアプリケーションのサンプルdockerfileです # stage 1 build the next js app from node 20 alpine as builder workdir /app copy package json package lock json / run npm install copy run npm run build \# stage 2 run the next js app from node 20 alpine workdir /app copy from=builder /app / expose 3000 cmd \["npm", "start"] dockerfileを作成した後、 dockerignore ファイルを作成し、これらのコマンドを追加します # node modules (reinstalled in docker) node modules \# next js build output next out \# logs npm debug log yarn debug log yarn error log pnpm debug log \# env files (optional — only if you handle secrets another way) env env local env development env production env test \# os / ide / editor junk ds store thumbs db vscode idea \# git git gitignore これで、アプリケーションをローカルでビルドしてテストできます。これを行うには、ターミナルで次のコマンドを実行します docker build t ecommerce app docker run p 3000 3000 ecommerce app 「 http //localhost 3000 」を開いて、サイトが正常に動作していることを確認してください。すべてが期待通りに動作していることを確認したら、コードをgithubにプッシュし、back4appを通じてデプロイする時です。 コードがコミットされ、githubリポジトリにプッシュされていることを確認してください。これにより、back4appがデプロイのためにプロジェクトにアクセスできるようになります。back4appアカウントで、ダッシュボードに移動し、画面の上部にある「 ダッシュボード 」ドロップダウンメニューをクリックします。 dashboard menu ドロップダウンから「 web deployment platform 」を選択します。 web deployment platform これにより、ウェブアプリケーションデプロイメントページに移動します: deploy web application page 「 webアプリをデプロイする 」ボタンをクリックして、デプロイプロセスを開始します。 link github repo 次に、「 githubリポジトリをインポート 」をクリックし、dockerfileを含むリポジトリを選択します。 プロジェクトに名前を付けます(例:ecommerce back4app)。 ガイドに従ってアプリを構築し、デプロイします。 デプロイメントが完了すると、back4appはこのような確認ページを表示します successful deployment 上の画像でハイライトされた部分は、あなたのアプリケーションが稼働しているurlです。これを使用して、ウェブブラウザで直接アプリを表示およびテストできます。 デプロイ後は、back4appダッシュボードを使用してビルドログを監視し、コンテナの健康状態を追跡し、必要に応じて以前のバージョンにロールバックします。 ダッシュボードはコンテナの状態に関するリアルタイムの更新を表示し、エラーを見つけやすくし、ユーザーのために安定した環境を維持するのを容易にします。 コンテナを使用することで、back4appバックエンドとシームレスに連携する柔軟でポータブルなフロントエンドソリューションを得ることができ、一貫した手間のかからないリリースへの道を開きます。このチュートリアルで構築されたeコマースウェブサイトを訪れることができます こちら https //ecommerceback4app mcphit37 b4a run/ 結論 おめでとうございます—これで、戦闘実績のある、商用準備が整ったeコマーススタックのすべてのレイヤーを組み立てました バックエンドのセットアップ back4appアカウントのプロビジョニングから、ユーザー、製品、注文のモデリングまで セキュリティとapi 強力な認証の実装と、restおよびgraphqlエンドポイントを介したデータの公開 ビジネスロジックとリアルタイム更新 cloud codeを使用したワークフローの自動化と、livequeryを通じたライブ変更のストリーミング フロントエンドのデプロイメント javascript sdkの接続と、back4appコンテナ内でのnext jsストアフロントの出荷 次は何ですか? back4appのドキュメント https //www back4app com/docs と parse platformガイド https //docs parseplatform org を深く参照するか、コミュニティフォーラムに参加して、メンテナーや他の開発者から高度なヒントやサンプルアプリを取得してください。 この基盤は始まりに過ぎません—webhookやスケジュールされたジョブの統合、決済ゲートウェイの接続、またはモバイルコンパニオンアプリの構築によってさらに拡張してください。 コードベースを繰り返し、リファクタリングし、進化させ続けてください—そして、あなたのストアフロントがあなたの野望と共に成長するのを見守ってください。