農業現場から:勤怠管理アプリを作ってます①(Next.js × Neon × Clerk × Cloudflare)

注意: 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を使ったことがあれば移行のハードルは低い。

SupabaseNeon
DBの種類PostgreSQLPostgreSQL
認証機能あり(組み込み)あり(Neon Auth・今回は不使用)
無料枠容量500MB・2プロジェクトまで500MB・10プロジェクトまで
商用利用OKOK
特徴全部入りで便利DBに特化・シンプル
今回使わない理由プロジェクト枠が埋まっている

今回は認証にClerkを使うため、Neon Authは使わない。DBの接続先が変わるだけで、SQLの書き方はSupabaseと同じように書ける。

今回の構成変更点まとめ

今まで今回
フロント配信VercelCloudflare Pages
DBSupabase(PostgreSQL)Neon(PostgreSQL)
認証Supabase AuthClerk

開発環境

分類使用技術・ツール役割・説明
OSmacOS開発環境全体のベース
パッケージ管理Homebrew / pnpmCLIツールやパッケージの管理
Node環境nvm / Node.js v24.15.0Next.jsを動かす実行基盤
フロントエンドNext.js 16(TypeScript)アプリのUI・画面部分
DBNeon(PostgreSQL)勤怠・給与データの保存
認証Clerkログイン・ユーザー管理
フロント配信Cloudflare Pagesアプリの本番公開場所
GitHub連携GitHubコードのバージョン管理
開発エディタCursorAI補助(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_...
Clarkを開くとマニュアルがあるので、基本順序通りにすればいい

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)もあるので、スタックの違いを比較する予定。

目次