はじめに
いよいよアドベントカレンダーも21日目ですね!さて、私はこの2025年に新卒で入社し、プロジェクト参画にあたって初めてフロントエンド技術に触れた初心者でございます。フロントエンドを触っていると「自分で何か作りたいなぁ」と思うわけです。そんな時の強い味方がVercelでございます。今回は、Vercelの無料枠の範囲内で簡単なアプリを作成し、私自身の備忘録も兼ねて主要な機能をまとめていきたいと思います!
想定する読者の方々
本記事は「Vercel触ったことないけど、これから始めてみたい」という方へ読んでいただきたい記事になっております。網羅的に機能を紹介するというよりは、Webアプリを構築するにあたり高い確率で使用するであろうものを絞ってお伝えしていきます。
Vercelとはなんぞ
Vercelを一言で表せば、
「フロントエンド開発者のためのクラウド開発基盤」
です。
まずは「クラウド開発基盤」という言葉について。Vercelは単なるホスティングサービスではなく、開発・デプロイ・メトリクスにまつわるクラウドネイティブツール群一式をまとめ上げた開発基盤を提供しています。Webアプリケーションの構築・運用をトータルで支えていただける大変ありがたい存在なわけです。
これだけでも十分ありがたいのですが、わざわざ先頭に「フロントエンド開発者のため」とつけた理由は、Vercelが提唱する「フロントエンドクラウド」という概念を踏まえてのことです。
彼らは近年のフロントエンド開発者を取り巻く多くの課題について憂慮しています。ニーズの多様さ(UXの向上に加えて高速であること等)、急速に進化する技術やベストプラクティスが複雑に絡まる環境、厳格なセキュリティとコンプライアンスetc...
とにかく考えることが多いのです。これを解決するために登場したのがフロントエンドクラウドであり、Vercelの存在意義そのものでもあります。その主な要素は以下の3つにまとめられます。
1. グローバルインフラストラクチャとキャッシュ
CDNなどを通じて、Webアプリケーションの低遅延・高可用性を実現している。
2. 統一された開発者ツールとワークフロー
Vercelは主要なフロントエンドのフレームワーク・ライブラリを広くカバーしている。これによって開発者は統一されたワークフローの下で構築→テスト→デプロイをシームレスに行うことができる。
3. アプリケーションの可観測性(Observability)と監視(Monitoring)
開発者はVercelが持つ高度な分析ツールによって、アプリケーションのパフォーマンスを効果的に監視し、問題へ迅速に対応できるようになる。
このようにフロントエンド開発者にとって心強い仕組みが整っているのがVercelなのです。気軽にWebアプリケーションを作成したいフロントエンド開発者を惹きつけてやまない理由はここにあります。
Vercelの料金体系
Vercelには主に3つの料金プランが存在します。一番左のHobbyプランは無料でVercelを使えるプランですが、使えるリソースに限りがあります。
Delivery Network
| Resource | Limit / Description |
|---|---|
| Edge Requests | 1 Million / month |
| Fast Data Transfer | 100 GB / month |
Web Application Firewall
| Resource | Limit / Description |
|---|---|
| Custom Firewall Rules | 3つまで |
| IP Blocking | 3つまで |
| Rate Limiting | 1 Million / month |
Storage
| Resource | Limit / Description |
|---|---|
| Blob Storage | 1 GB / month |
| Edge Config | 100,000 Reads & 100 writes / month |
| Neon | 0.5GB / project (Free plan) |
| Redis(KV) Storage | 30 MB, single DB (Free plan) |
Compute
| Resource | Limit / Description |
|---|---|
| Vercel Functions(呼び出し回数) | 1 Million / month |
Proにアップグレードするとこれらの上限は原則なくなり、 月額固定料金($20)+従量課金(一定量を超えた後) というハイブリッドな料金体系になっております。またこれらのリソース上限に加えて、商用利用が不可となっています。個人向けのPro、企業向けのEnterpriseプランであれば商用利用が認められています。
Vercelの機能
ここで具体的な機能をいきなり羅列していくのはあまりにも酷なので、今回は練習で実際にアプリを作り、そこで用いた機能を中心にご紹介していくことにします。作成したのは「アルバムアプリ」です。

こちらは私が毎月貢いでいるOpenAIよりCodex先生がお書きになり、弟子の私が若干修正したものです。今回は「Vercelを理解すること」に重きを置いていますので、その点ご容赦いただけますと幸いです(にしても、AIでコーディングされたUIってなんとなく分かってしまうのなぜなんでしょうね?)。
今回私が用いたVercelの機能は以下の5つです。
| No. | 機能 | 概要 | 今回の用途 |
|---|---|---|---|
| 1 | Neon for Vercel | PostgreSQL データベース | 各写真のメタデータを保存 |
| 2 | Vercel Blob | オブジェクトストレージ | 画像ファイル自体を保存 |
| 3 | Vercel Functions | サーバレス関数 | フロントからのHTTPリクエストを捌くAPI |
| 4 | GitHubからの自動デプロイ | プッシュすると自動でビルド&デプロイ | 特定のリポジトリに紐づけて、プッシュと同時に自動デプロイ |
| 5 | Logs | アプリのログを一元管理 | ビルド時やランタイムのログを監視 |
では実際の画面とともに各機能の特徴を見ていきましょう!
Neon for Vercel
こちらはVercel上のプロジェクトと容易に統合できるよう設計された、サーバレスのPostgreSQLサービスです。
Neonについて
NeonはPostgreSQLをクラウドサービスとして再構築し、よりスケーラブルで信頼性の高いアプリケーション構築に貢献することをミッションとしています。
Vercelをよくご存知でおられる同志の皆さまは「あれ、Vercel PostgreSQLってあったやん」と思われていることでしょう。実はこちら、2025年の第一四半期までにNeonへ完全に移行されました(参考:https://neon.com/docs/guides/vercel-postgres-transition-guide )。比較的最近の出来事なので、生成AIはこれを知らずに応答してくることがあります。
では実際に設定してみましょう。まずはDashboardの上部メニューバーから”Storage”を選択します。
そして”Create Database”をクリックします。
そうするとStorageの選択画面が出てきますので、今回は”Neon”をクリックします。
ストレージの名前を入力して、”Create”をクリックすれば作成完了です。
そうすると、接続先のURL等の .envに追記すべき内容が書かれています。こちらを自身の環境の .envにコピペします。
Vercel Blob
こちらはVercelに標準搭載されているオブジェクトストレージサービスです。感覚としてはAWSにおけるS3のようなものです。BlobとはBinary Large Objectの略称で、汎用的なバイナリデータの塊です。テキストや画像、動画など様々なデータ形式を扱うことができ、フロントでファイルを扱おうとすると頻繁に出てきます。
こちらも先ほどのNeonと同様にDashboard>"Storage"から設定することができます。実際にやってみましょう。最初の部分は省かせていただきまして、ストレージの名前を設定して”Create”で作成完了です。
こちらもNeonと同様に、ストレージへのアクセストークンが発行されるので自身の環境にある .envへコピペしましょう。
Vercel Functions
こちらはサーバレスなバックエンドの仕組みを提供するものであり、APIエンドポイントの実装やDBアクセスの実現を担います。例えばAPIエンドポイントであれば、プロジェクトのルートに api/ というディレクトリを作成した上でコードを配置すると、Vercelが自動的にエンドポイントをデプロイしてくれます。
APIのディレクトリ構成
今回はフレームワーク非依存で、プロジェクト直下にapi/ディレクトリを置くような構成にしております。しかしながら、Vercelでアプリを構築する際はNext.jsと合わせて利用されることが多く、App Routerを用いることになります。その際、APIは app/api直下へ置くことになります。例えばphotosというAPIを実装したければ、app/api/photos/というディレクトリを作成し、その直下のroute.tsにロジックを記述します。
今回のアプリでは、以下のようなコードを実装しています。
import type { VercelRequest, VercelResponse } from '@vercel/node'
import { neon } from '@neondatabase/serverless'
import { put } from '@vercel/blob'
import { randomUUID } from 'crypto'
export const config = {
api: {
bodyParser: {
sizeLimit: '10mb',
},
},
}
type PhotoRow = {
id: string
title: string
image_url: string
created_at: string
}
const databaseUrl = process.env.DATABASE_URL
const sqlClient = databaseUrl ? neon(databaseUrl) : null
let ensured = false
class ApiError extends Error {
status: number
constructor(message: string, status = 400) {
super(message)
this.status = status
}
}
export default async function handler(req: VercelRequest, res: VercelResponse) {
res.setHeader('Access-Control-Allow-Origin', '*') //動作確認を簡素化するために、どのOriginからでもAPI呼び出しができるように設定しています
res.setHeader('Access-Control-Allow-Methods', 'GET,POST,OPTIONS')
res.setHeader('Access-Control-Allow-Headers', 'Content-Type')
if (req.method === 'OPTIONS') {
return res.status(200).end()
}
try {
if (req.method === 'GET') {
const photos = await listPhotos()
return res.status(200).json({ photos })
}
if (req.method === 'POST') {
const photo = await createPhoto(req)
return res.status(201).json({ photo })
}
res.status(405).json({ error: 'Method not allowed' })
} catch (error) {
console.error(error)
if (error instanceof ApiError) {
res.status(error.status).json({ error: error.message })
return
}
const message = error instanceof Error ? error.message : 'Unexpected error'
res.status(500).json({ error: message })
}
}
async function ensureTable() {
if (ensured) return
const sql = requireSql()
await sql`
CREATE TABLE IF NOT EXISTS photos (
id TEXT PRIMARY KEY,
title TEXT NOT NULL,
image_url TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)
`
ensured = true
}
async function listPhotos() {
await ensureTable()
const sql = requireSql()
const rows = (await sql`
SELECT id, title, image_url, created_at
FROM photos
ORDER BY created_at DESC
`) as PhotoRow[]
return rows.map(mapRow)
}
async function createPhoto(req: VercelRequest) {
if (!process.env.BLOB_READ_WRITE_TOKEN) {
throw new ApiError('BLOB_READ_WRITE_TOKEN is not configured', 500)
}
const payload = (req.body ?? {}) as {
title?: string
fileData?: string
contentType?: string
}
if (!payload.fileData) {
throw new ApiError('fileData is required')
}
const { buffer, contentType } = decodeFile(payload.fileData, payload.contentType)
const id = randomUUID()
const blob = await put(`album/${id}`, buffer, {
access: 'public',
contentType,
})
await ensureTable()
const sql = requireSql()
const rows = (await sql`
INSERT INTO photos (id, title, image_url)
VALUES (${id}, ${payload.title?.trim() || 'Untitled'}, ${blob.url})
RETURNING id, title, image_url, created_at
`) as PhotoRow[]
return mapRow(rows[0] as PhotoRow)
}
function decodeFile(fileData: string, explicitType?: string) {
const match = fileData.match(/^data:(.*?);base64,(.*)$/)
const base64 = match ? match[2] : fileData
const contentType = explicitType || (match ? match[1] : 'application/octet-stream')
return {
buffer: Buffer.from(base64, 'base64'),
contentType: contentType || 'application/octet-stream',
}
}
function mapRow(row: PhotoRow) {
return {
id: row.id,
title: row.title,
imageUrl: row.image_url,
createdAt: row.created_at,
}
}
function requireSql() {
if (!sqlClient) throw new ApiError('DATABASE_URL is not configured', 500)
return sqlClient
}
handlerという関数を見ていただくとわかりやすいのですが、リクエストreqの内容に応じて各種レスポンスresを返すための処理が書かれています。しかもそれがTypeScriptで書かれていますので、そこまで重い処理でなければAPIの実装がフロントエンド言語のみで完結します。
GitHubからの自動デプロイ
ここからはいよいよVercelにアプリをデプロイしていきます。Vercelでは、ビルド→デプロイを自動的に行ってくれます。その方法としては、現在主に3種類が提供されています。
1. GitにPush
GitHubやGitLabと連携して、特定ブランチへのコミットをトリガーとして自動的にビルド・デプロイ
2. Vercel CLI
ローカルでvercelコマンドを用いることで、ローカルからVercelのクラウドインフラストラクチャへデプロイされる
3. Dashboardから
Vercelが用意しているコンソール画面からデプロイすることが可能。
今回は1番目のGitを用いた自動デプロイを行ってみます。Dashboardの”Overview”にある”Add new...”から”Project”を選びます。
こちらの画面では、連携されているGitHubアカウントに存在するRepositoriesが表示されます。ここから、今回新規作成するProjectへImportしたいRepositoryを選択します。
デフォルトではRepository名がそのままProject名になります。表示されている内容に問題なければ、そのまま”Deploy”をクリックします。
うまくいくと正常にデプロイされます。もしビルドに失敗すると真っ赤になりますので、エラーと格闘しながら解消していきましょう。
と、ここで忘れてはいけないのがStorageとProjectを接続することです。以下のボタンをクリックして、対応づけるProjectを指定しないと上手く疎通しませんのでご注意を!
ではアプリを確認してみましょう。こちらの”Visit”をクリックするとデプロイされたアプリの画面に遷移します。
うまく写真をアップロードできるでしょうか...
うまくいきました!!
超単純なアプリですが、きちんと動くと嬉しいですね。
CI/CDについて
今回のようにVercelのコンソール画面からGitHubとの連携を行うことで、少なくともCDは実現できていることになります。したがって、単純にデプロイまでの自動化を行いたい場合、GitHub ActionsなどのCI/CDツールは必ずしも必要とは限りません。もちろん、LintやFormatting、テストなどを行った上でデプロイさせたい場合にはGitHub Actionsを利用する必要があります。
Logs
無事アプリをデプロイできたら、Logsを確認してビルド・実行時のログを監視して上手く動いているかどうかを確認しましょう。Dashboardの”Logs”をクリックします。
お、きちんとログが流れてきていますね!Requestの列を見てみると/api/photosをエンドポイントとしてVercel Functionsが動いていることを示す$f$マークが見えますね。
下の真っ赤なログは、私が格闘した跡です。
最後に
いかがでしたでしょうか?今回の記事を執筆するにあたり色々と調査したのですが、想像していたよりもVercelで出来ることが多く、創作意欲が刺激されたように思います(ただ正直、無料枠だと利用できるストレージやコンピューティングリソースはかなり限られてきます)。今後色々と作っていきたいなと感じました。皆さんもぜひ、Vercelで遊んでみてください!
最後までお読みいただき、ありがとうございました!



















