注意: 2026年4月時点の情報に基づいています。各サービスの仕様は変更される可能性があります。
はじめに
農業の現場では、タイムカード等採用している農家さんは少ない。
よくあるのはこういう形だ。雇用主は例えば「7時から10時まで」と雇っているが、自分自身は別の農作業をしている。雇われた側は収穫補助などの作業を終えたら、10時を待たずに(早退で)帰ることもある。結果として「何時間働いたか」を正確に把握するのがややこしくなり、給与の計算が毎回手間になっている。
勤務時間も週20時間以内に収まることが多く、雇用保険や社会保険は発生しないシンプルな雇用形態なのに、給与計算だけが手作業になっている——みたいな農家さんが多いような気がする(私の周りだけかもしれないが)
自分自身はまだアルバイトを雇う段階ではないが、知り合いの農家さんを助ける意味で勤怠管理アプリを自作してみることにした。
なお、栽培管理アプリも作成し運用しているが、入力等については落ち着いてきている。
ただ、今年から別の作物の栽培も検討しているため、そのタイミングで運用上の課題が生じる可能性がある。
その都度、状況を見ながら更新していく予定である。
今回、ちょっとスタックを変えてみました。
これまでの開発では Supabase + Vercel の組み合わせを使ってきた。栽培管理アプリはこの構成で動いている。
今回新しいスタックを試してみようと思った理由として、
1つ目はSupabaseの無料枠の問題。無料プランで作れるプロジェクトは2つまで(デモ版と自分の栽培記録版)で、すでに枠が埋まっている。新しいアプリを作るためには別のDBサービスを検討する必要があった。
2つ目はVercelの代替を試したいという気持ち。最近内部システム侵害があったわけだが、それが原因ではなく(栽培アプリは環境変数変えて、そのまま使用中)、単純に別のサービスも触れてみたいという理由から。今回Cloudflare Pagesを試すことにした。
NeonとSupabaseの違い
DBの代替として選んだのがNeonだ。SupabaseもNeonもどちらもPostgreSQLベースなので、SQLの書き方はほぼ同じ。Supabaseを使ったことがあれば移行のハードルは低い。
| Supabase | Neon | |
|---|---|---|
| DBの種類 | PostgreSQL | PostgreSQL |
| 認証機能 | あり(組み込み) | あり(Neon Auth・今回は不使用) |
| 無料枠容量 | 500MB・2プロジェクトまで | 500MB・10プロジェクトまで |
| 商用利用 | OK | OK |
| 特徴 | 全部入りで便利 | DBに特化・シンプル |
| 今回使わない理由 | プロジェクト枠が埋まっている | — |
今回は認証にClerkを使うため、Neon Authは使わない。DBの接続先が変わるだけで、SQLの書き方はSupabaseと同じように書ける。
今回の構成変更点まとめ
| 今まで | 今回 | |
|---|---|---|
| フロント配信 | Vercel | Cloudflare Pages |
| DB | Supabase(PostgreSQL) | Neon(PostgreSQL) |
| 認証 | Supabase Auth | Clerk |
開発環境
| 分類 | 使用技術・ツール | 役割・説明 |
|---|---|---|
| OS | macOS | 開発環境全体のベース |
| パッケージ管理 | Homebrew / pnpm | CLIツールやパッケージの管理 |
| Node環境 | nvm / Node.js v24.15.0 | Next.jsを動かす実行基盤 |
| フロントエンド | Next.js 16(TypeScript) | アプリのUI・画面部分 |
| DB | Neon(PostgreSQL) | 勤怠・給与データの保存 |
| 認証 | Clerk | ログイン・ユーザー管理 |
| フロント配信 | Cloudflare Pages | アプリの本番公開場所 |
| GitHub連携 | GitHub | コードのバージョン管理 |
| 開発エディタ | Cursor | AI補助(Claude)込みで開発 |
初期セットアップ手順
Node.jsのバージョン確認
Next.js 16の最小要件はNode.js 20.9以上。まず現在のバージョンを確認する。
node -v
# v24.15.0(LTS版)
# バージョンが古い場合はアップデート
nvm install --lts
nvm use --lts
Next.jsプロジェクト作成
フォルダを作成してその中でコマンドを実行する。--src-dirオプションでsrc/ディレクトリありの構成にしている。
pnpm create next-app . --typescript --src-dir
今回はオプションで指定しているため、途中の質問は省略されてデフォルト設定で進む。
GitHubへpush
GitHubでprivateリポジトリを作成してからpushする。
git add .
git commit -m "Initial commit"
git remote add origin https://github.com/ユーザー名/レポジトリ名.git
git push -u origin main
NeonでDBを作成
neon.tech でアカウントを作成(今回はGitHubのアカウントを利用)し、プロジェクトを新規作成する。GitHubアカウントでサインアップすると、Organization nameにGitHubのアカウント情報が自動で入るのでそのままでよい。用途は「Personal projects」を選択。
次にプロジェクトを新規作成する。設定内容は以下の通りだ。

- Project name:アプリ名を入力(例:salary-app)
- Postgres version:17のまま
- Region:AWS Asia Pacific 1 (Singapore)(東京リージョンは現時点でなし)
- Neon Auth:オフのまま(今回はClerkを使うため不要)
作成が完了すると接続文字列が表示される。「Connection string」タブの「Copy snippet」でコピーしておく。

Clerkでアプリを作成
clerk.com も同様にGitHubのアカウントで作成し、アプリを新規作成する。サインイン方法はEmail・Google・Usernameを有効にした。

ClerkはログインUIコンポーネントが最初から用意されているため、ログイン画面を自前で作る必要がない。Supabase Authで認証UIを自前実装していた手間がなくなるのが大きなメリットだ。
必要なパッケージをインストール
pnpm add @neondatabase/serverless
pnpm add @clerk/nextjs
環境変数の設定
ルート直下に.env.localを作成して以下を記載する。
DATABASE_URL=postgresql://...(NeonのConnection string)
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
CLERK_SECRET_KEY=sk_test_...

Clerkの設定ファイルを追加
Next.js 16ではmiddleware.tsが非推奨になり、proxy.tsに変わった。src/proxy.tsを作成する。
import { clerkMiddleware } from '@clerk/nextjs/server'
export default clerkMiddleware()
export const config = {
matcher: [
'/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
'/(api|trpc)(.*)',
],
}
src/app/layout.tsxでアプリ全体をClerkProviderでラップする。
import type { Metadata } from 'next'
import { ClerkProvider } from '@clerk/nextjs'
import './globals.css'
export const metadata: Metadata = {
title: '勤怠管理アプリ',
description: '農業向け勤怠管理アプリ',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<ClerkProvider>
<html lang="ja">
<body>{children}</body>
</html>
</ClerkProvider>
)
}
ハマったこと
192.168.x.xからアクセスするとClerkが動かない
開発中はスマホやサブPCから192.168.x.x:3000でアクセスして動作確認しているが、Clerkのセッションが正しく機能しなかった。
原因はClerkの開発モードがlocalhost以外からのアクセスを想定していないことと、IPアドレス経由ではCookieが正しく送受信されないことにあるようだ。
スマホや別のPCから動作確認する場合もあるからとlocalhost:3000でのアクセスを採用してなかったので、原因を見つけるのに苦労した。next.config.tsに以下を追加することで解決した。
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
allowedDevOrigins: ['192.168.11.30'],
}
export default nextConfig
今後の方針
初期セットアップとClerk認証の動作確認ができた。次回からは実際の機能を作っていく。
- 経営者(A)の登録フロー
- 従業員(B)の登録・QRコード発行
- 出退勤打刻画面
- 給与計算ロジックの実装
まずは動くものを作ることを優先する。アプリが完成したら、他にも興味のあるスタック(Cloudflare D1やTiDB Cloud)もあるので、スタックの違いを比較する予定。
