NextJS Templates
Next.js、Vercel、および Back4app を使った請求書アプリのチュートリアル
25 分
このチュートリアルでは、完全な invoicing app を構築します。フロントエンドには next js を使用し、バックエンドには back4app を使用し、認証には clerk を、支払いには stripe を、メール通知には resend を使用します。このガイドでは、認証の設定、請求書管理、支払い処理、そして vercel へのデプロイメントについて説明します。 1\ 前提条件 始める前に、以下のものを用意してください back4appアカウント ( こちらからサインアップ https //www back4app com/ ) 認証用のclerkアカウント ( clerk io https //clerk dev/ ) 支払い処理用のstripeアカウント ( stripe com https //stripe com/ ) メール通知用のresendアカウント ( resend io https //resend io/ ) node jsとnpmがインストールされていること ( node jsのインストール https //nodejs org/ ) 2\ バックエンドの設定 (back4app) 2 1 back4appで新しいプロジェクトを作成する back4appにログインして新しいプロジェクトを作成します。 データブラウザに移動し、以下のクラスを作成します 2 2 データモデル (クラス) 2 2 1 ユーザー (clerkによって自動的に処理されます) オブジェクトid ユーザー名 メール 2 2 2 組織 「 organization 」というクラスを作成します。 次の列を追加します 名前 (string) 組織の名前。 ownerid (pointer< user>) 「 user 」クラスを指します(clerkによって自動的に作成されます)。 メンバー (array\<pointer< user>>) この組織に属するユーザーの配列。 2 2 3 顧客 「 customer 」というクラスを作成します。 次の列を追加します 名前 (string) 顧客の名前。 メール (string) 顧客のメール。 organizationid (pointer) この顧客が属する組織。 2 2 4 請求書 クラスを作成する 請求書 これらの列を追加します invoicenumber (文字列) ユニークな請求書識別子。 customerid (ポインタ) 顧客へのリンク。 organizationid (ポインタ) 組織へのリンク。 amount (数値) 請求書の合計金額。 status (文字列) 'ドラフト', '送信済み', '支払い済み', '期限切れ' などのオプション。 duedate (日付) 請求書の期限日。 items (配列) 請求書内のアイテムのリスト。 2 2 5 支払い クラスを作成する 支払い これらの列を追加します invoiceid (ポインタ) 関連する請求書へのリンク。 amount (数値) 支払い金額。 stripesessionid (文字列) stripeセッションのid。 status (文字列) 支払いのステータス(例: '保留中', '成功', '失敗')。 3\ フロントエンドの設定 (next js) 3 1 next js プロジェクトの作成 typescript、tailwind css、および shadcn/ui コンポーネントを使用して next js プロジェクトを設定します。 npx create next app\@latest invoicing app typescript cd invoicing app npm install tailwindcss postcss autoprefixer npx tailwindcss init 3 2 tailwind css の設定 tailwind を設定するには、 tailwind config js を更新し、 globals css にスタイルを追加します。 次の内容を tailwind config js module exports = { content \[ ' /pages/ / {js,ts,jsx,tsx}', ' /components/ / {js,ts,jsx,tsx}', ], theme { extend {}, }, plugins \[], }; 次の内容を styles/globals css @tailwind base; @tailwind components; @tailwind utilities; 3 3 parse sdk と追加サービスのインストール parse、clerk、stripe、および resend の必要な依存関係をインストールします。 npm install parse @clerk/clerk next stripe resend 3 4 next js で parse sdk を初期化する 次の内容を pages/ app tsx , で parse sdk を back4app の資格情報で初期化します import { clerkprovider } from '@clerk/clerk react'; import parse from 'parse'; parse initialize('your app id', 'your javascript key'); parse serverurl = 'https //parseapi back4app com'; function myapp({ component, pageprops }) { return ( \<clerkprovider> \<component { pageprops} /> \</clerkprovider> ); } export default myapp; 次の内容を置き換えます 'your app id' と 'your javascript key' を back4app の資格情報に置き換えます。 4\ ユーザー認証の実装 4 1 認証のためのclerk統合 認証のために clerk を設定します。clerkのダッシュボードに移動し、アプリケーションを作成し、apiキーを取得します。clerkキーで env local ファイルを更新します。 next public clerk frontend api=your clerk frontend api clerk api key=your clerk api key 次に、clerkコンポーネントを使用して、サインインおよびサインアップ機能のために pages/sign in tsx と pages/sign up tsx を作成します。 次に、 pages/sign in tsx import { signin } from '@clerk/clerk react'; export default function signinpage() { return \<signin />; } 次に、 pages/sign up tsx import { signup } from '@clerk/clerk react'; export default function signuppage() { return \<signup />; } 5\ 組織と顧客管理 5 1 組織の作成 ユーザーは自分が所有する組織を作成できます。新しい組織を作成するためのページ pages/organizations/new\ tsx を作成します。 import { usestate } from 'react'; import parse from 'parse'; export default function neworganization() { const \[name, setname] = usestate(''); const handlecreateorganization = async () => { const organization = parse object extend('organization'); const organization = new organization(); organization set('name', name); organization set('ownerid', parse user current()); try { await organization save(); alert('organization created successfully!'); } catch (error) { console error('error creating organization ', error); } }; return ( \<div> \<h1>create new organization\</h1> \<input type="text" value={name} onchange={(e) => setname(e target value)} placeholder="organization name" /> \<button onclick={handlecreateorganization}>create\</button> \</div> ); } 5 2 顧客の管理 新しい顧客を追加するための顧客管理ページ pages/customers/new\ tsx を作成します。 import { usestate } from 'react'; import parse from 'parse'; export default function newcustomer() { const \[name, setname] = usestate(''); const \[email, setemail] = usestate(''); const handlecreatecustomer = async () => { const customer = parse object extend('customer'); const customer = new customer(); customer set('name', name); customer set('email', email); customer set('organizationid', parse user current() get('organizationid')); try { await customer save(); alert('customer created successfully!'); } catch (error) { console error('error creating customer ', error); } }; return ( \<div> \<h1>create new customer\</h1> \<input type="text" value={name} onchange={(e) => setname(e target value)} placeholder="customer name" /> \<input type="email" value={email} onchange={(e) => setemail(e target value)} placeholder="customer email" /> \<button onclick={handlecreatecustomer}>create\</button> \</div> ); } 6\ 請求書管理 6 1 請求書の作成 新しい請求書を生成するための請求書作成ページ pages/invoices/new\ tsx を作成します。 import { usestate } from 'react'; import parse from 'parse'; export default function newinvoice() { const \[invoicenumber, setinvoicenumber] = usestate(''); const \[amount, setamount] = usestate(0); const handlecreateinvoice = async () => { const invoice = parse object extend('invoice'); const invoice = new invoice(); invoice set('invoicenumber', invoicenumber); invoice set('amount', amount); invoice set('organizationid', parse user current() get('organizationid')); try { await invoice save(); alert('invoice created successfully!'); } catch (error) { console error('error creating invoice ', error); } }; return ( \<div> \<h1>create new invoice\</h1> \<input type="text" value={invoicenumber} onchange={(e) => setinvoicenumber(e target value)} placeholder="invoice number" /> \<input type="number" value={amount} onchange={(e) => setamount(parsefloat(e target value))} placeholder="amount" /> \<button onclick={handlecreateinvoice}>create\</button> \</div> ); } 6 2 請求書の表示 ページを作成します pages/invoices/index tsx すべての請求書を表示し、ソートおよびフィルタリングオプションを提供します。 import { useeffect, usestate } from 'react'; import parse from 'parse'; export default function invoiceslist() { const \[invoices, setinvoices] = usestate(\[]); useeffect(() => { const fetchinvoices = async () => { const invoice = parse object extend('invoice'); const query = new parse query(invoice); const results = await query find(); setinvoices(results); }; fetchinvoices(); }, \[]); return ( \<div> \<h1>invoices\</h1> {invoices map(invoice => ( \<div key={invoice id}> \<h2>invoice #{invoice get('invoicenumber')}\</h2> \<p>amount ${invoice get('amount')}\</p> \<p>status {invoice get('status')}\</p> \</div> ))} \</div> ); } 7\ 支払い処理(stripe統合) 7 1 ストライプチェックアウトの設定 支払いを処理するには、stripeを統合します。 pages/invoices/\[id] tsx において、stripe checkoutを使用して支払いリンクの作成を実装します。 import { userouter } from 'next/router'; import { useeffect, usestate } from 'react'; import stripe from 'stripe'; export default function invoicedetail() { const router = userouter(); const { id } = router query; const \[invoice, setinvoice] = usestate(null); useeffect(() => { if (!id) return; const fetchinvoice = async () => { const invoice = parse object extend('invoice'); const query = new parse query(invoice); query equalto('objectid', id); const result = await query first(); setinvoice(result); }; fetchinvoice(); }, \[id]); const handlepayinvoice = async () => { const stripe = new stripe(process env stripe secret key); const session = await stripe checkout sessions create({ payment method types \['card'], line items \[ { price data { currency 'usd', product data { name `invoice #${invoice get('invoicenumber')}` }, unit amount invoice get('amount') 100, }, quantity 1, }, ], mode 'payment', success url window\ location origin + '/success', cancel url window\ location origin + '/cancel', }); window\ location href = session url; }; if (!invoice) return \<div>loading \</div>; return ( \<div> \<h1>invoice #{invoice get('invoicenumber')}\</h1> \<p>amount ${invoice get('amount')}\</p> \<button onclick={handlepayinvoice}>pay now\</button> \</div> ); } 8\ メール通知(再送信統合) 8 1 請求書メールの送信 「 resend 」を設定して、クライアントに請求書メールを送信します。 import { resend } from 'resend'; export default async function sendinvoiceemail(invoiceid) { const resend = new resend(process env resend api key); const invoice = await fetchinvoicedetails(invoiceid); // function to fetch invoice details const customer = await fetchcustomerdetails(invoice customerid); await resend sendemail({ from 'no reply\@invoicingapp com', to customer email, subject `invoice #${invoice invoicenumber}`, html `\<h1>invoice #${invoice invoicenumber}\</h1>\<p>amount ${invoice amount}\</p>`, }); } 9\ vercelへのデプロイ アプリをvercelにデプロイするには、vercel cliをインストールしてデプロイします。 npm install g vercel vercel デプロイ前に、vercelダッシュボードでclerk、stripe、resend、back4appの環境変数を設定します。 10\ 結論 このチュートリアルでは、完全な 請求書アプリ をnext jsで構築し、バックエンドに back4app を統合し、認証に clerk 、支払いに stripe 、メール通知に resend を使用しました。ユーザーと組織の管理、請求書の作成、支払い処理、デプロイについて説明しました。