Skip to Content
ドキュメントWebコース自作アプリ開発自分のアプリを実装しよう

自分のアプリを実装しよう

このページで学ぶこと

  • 設計が終わった後、どこから実装を始めればいいかわかる
  • 機能を小さく分けて進める方法がわかる
  • 詰まったときの対処法がわかる

実装の進め方

「設計が終わった。さあ実装しよう!」……でも、何から始めればいいか迷いますよね。
ここでは「読書記録アプリ」を例に、実装を進める順番を説明します。

基本的な順番

「バックエンドを全部作ってからフロントエンドを作る」よりも、
1機能ずつ縦に貫通させる(APIも画面も一緒に作る)方が動く部分が増えてモチベーションが続きます。


ステップ1: プロジェクトをセットアップする

why_react.md でセットアップした my-app プロジェクトをそのまま使うか、
新しいプロジェクトを作ります。

npx create-next-app@latest 自分のアプリ名
cd 自分のアプリ名
npm install @supabase/supabase-js @prisma/client prisma @mui/material @mui/icons-material @emotion/react @emotion/styled
bash

.env ファイルを作って、Supabase の接続情報を設定しておきましょう(supabase_setup.md を参照)。


ステップ2: DBを作る

設計したスキーマを prisma/schema.prisma に書いて、マイグレーションします。

npx prisma migrate dev --name init
bash

Supabase の「Table Editor」でテーブルが作成されていれば成功です。


ステップ3: 最初の機能を1つ完成させる

最初から全機能を作ろうとしないでください。
「一覧を表示する」という1機能を、APIから画面まで完全に動かすことを目標にしましょう。

例: 読書記録の一覧を表示する

まずAPIを作る

// app/api/books/route.js
import { prisma } from '@/lib/prisma'
 
export async function GET() {
  const books = await prisma.book.findMany({
    orderBy: { createdAt: 'desc' },
  })
  return Response.json(books)
}
javascript

ブラウザでAPIを確認する

http://localhost:3000/api/books
plaintext

[] (空の配列)が返ってくれば成功。次に画面を作ります。

画面を作る

// app/books/page.js
'use client'
import { useEffect, useState } from 'react'
 
export default function BooksPage() {
  const [books, setBooks] = useState([])
 
  useEffect(() => {
    fetch('/api/books')
      .then((res) => res.json())
      .then((data) => setBooks(data))
  }, [])
 
  return (
    <main>
      <h1>読書記録</h1>
      {books.length === 0 && <p>まだ本が登録されていません。</p>}
      {books.map((book) => (
        <div key={book.id}>
          <h2>{book.title}</h2>
          <p>{book.author}</p>
        </div>
      ))}
    </main>
  )
}
javascript

http://localhost:3000/books を開いて「まだ本が登録されていません。」と表示されれば成功。


ステップ4: 登録機能を追加する

一覧が表示できたら、次は「追加できるようにする」です。

APIを追加する

// app/api/books/route.js に追加
export async function POST(request) {
  const body = await request.json()
  const book = await prisma.book.create({
    data: {
      title: body.title,
      author: body.author,
      memo: body.memo,
    },
  })
  return Response.json(book, { status: 201 })
}
javascript

フォームを作る

// app/books/new/page.js
'use client'
import { useState } from 'react'
import { useRouter } from 'next/navigation'
 
export default function NewBookPage() {
  const router = useRouter()
  const [title, setTitle] = useState('')
  const [author, setAuthor] = useState('')
 
  const handleSubmit = async (e) => {
    e.preventDefault()
    await fetch('/api/books', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ title, author }),
    })
    router.push('/books') // 登録後に一覧へ戻る
  }
 
  return (
    <main>
      <h1>本を登録する</h1>
      <form onSubmit={handleSubmit}>
        <input
          placeholder="タイトル"
          value={title}
          onChange={(e) => setTitle(e.target.value)}
          required
        />
        <input
          placeholder="著者"
          value={author}
          onChange={(e) => setAuthor(e.target.value)}
        />
        <button type="submit">登録する</button>
      </form>
    </main>
  )
}
javascript

ステップ5: 削除を追加する

// app/api/books/[id]/route.js
import { prisma } from '@/lib/prisma'
 
export async function DELETE(request, { params }) {
  const { id } = await params
  await prisma.book.delete({ where: { id: Number(id) } })
  return new Response(null, { status: 204 })
}
javascript

一覧ページに削除ボタンを追加する。

const handleDelete = async (id) => {
  await fetch(`/api/books/${id}`, { method: 'DELETE' })
  setBooks(books.filter((b) => b.id !== id))
}
 
// JSXの中に追加
<button onClick={() => handleDelete(book.id)}>削除</button>
javascript

ステップ6: 認証を追加する

CRUD が動いたら auth.md を参考に Supabase Auth を組み込みます。

追加する主な手順

  1. ログイン・サインアップページを作る
  2. ログアウトボタンを追加する
  3. ミドルウェアで「ログインしていないと /books にアクセスできない」ようにする
  4. 本の userId にログイン中のユーザーのIDをセットする(自分のデータだけ表示されるように)
// 本を登録するAPIにユーザーIDを追加する例
import { createServerClient } from '@supabase/ssr'
import { cookies } from 'next/headers'
 
export async function POST(request) {
  const cookieStore = await cookies()
  const supabase = createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
    { cookies: { getAll: () => cookieStore.getAll() } }
  )
  const { data: { user } } = await supabase.auth.getUser()
 
  const body = await request.json()
  const book = await prisma.book.create({
    data: {
      title: body.title,
      author: body.author,
      userId: user.id, // ログインユーザーのID
    },
  })
  return Response.json(book, { status: 201 })
}
javascript

詰まったときの対処法

まずこれを確認する

  1. コンソールを見る — ブラウザの F12 → Console タブにエラーが出ていないか
  2. ターミナルを見るnpm run dev を実行しているターミナルにエラーが出ていないか
  3. APIを単体で確認するhttp://localhost:3000/api/xxx に直接アクセスしてエラーがないか

AIに聞くテンプレート

【状況】
読書記録アプリで○○を実装しようとしています。

【やったこと】
・app/api/books/route.js に POST を実装した
・app/books/new/page.js にフォームを作った

【エラー】
(エラーメッセージをそのまま貼る)

【コード】
(関係しそうなコードを貼る)

【聞きたいこと】
原因と修正方法を教えてください。
plaintext

30分ルール

30分悩んでも解決しなかったら、すぐメンターに相談してください。
「詰まっている」と報告すること自体がエンジニアの大事なスキルです。


実装の進め方まとめ

フェーズやること
プロジェクト・DB作成
一覧表示(API → 画面)を動かす
登録機能を追加する
削除・編集を追加する
認証を組み込む
デザインを整える(MUI)
デプロイする

「全部できてから動かす」ではなく、小さく動かしながら積み上げるのがコツです。


確認しよう

  • API(GET)を作ってブラウザで確認した
  • 一覧画面を作ってAPIとつなげた
  • 登録フォームを作って、登録後に一覧に追加されるようになった
  • 削除ができるようになった

AIに聞いてみよう

「Next.js 16 の App Router で、フォームのバリデーション(必須チェック、文字数制限)を実装する方法を教えてください」


次のステップ

Vercelで公開しよう

Last updated on