build-ai2026-03-1315 min
Ứng Dụng AI Đầu Tiên Của Bạn: Từ Ý Tưởng Đến Sản Phẩm Chạy Được Trong 30 Phút
Không cần kinh nghiệm lập trình sâu. Tutorial này hướng dẫn build một Subject Line Generator AI thực sự hoạt động, dùng Next.js + OpenAI API — với code giải thích từng bước.
Ứng Dụng AI Đầu Tiên Của Bạn: Từ Ý Tưởng Đến Sản Phẩm Chạy Được Trong 30 Phút
Hầu hết mọi người nghĩ "build app AI" cần học lập trình nhiều năm. Thực ra không — với Next.js và OpenAI API, bạn có thể build một tool AI thực sự hoạt động trong buổi chiều đầu tiên.
Tutorial này: xây Subject Line Generator — nhập mô tả email, AI generate 5 subject lines hấp dẫn.
📌 TL;DR
- Sản phẩm: Web app nhận input text, gọi OpenAI API, hiển thị kết quả
- Tech: Next.js 14 (App Router) + OpenAI SDK + Tailwind CSS
- Yêu cầu: Node.js đã cài, OpenAI account (có free trial)
- Thời gian: 30-45 phút
Bước 1: Tạo Project
npx create-next-app@latest subject-generator --typescript --tailwind --app
cd subject-generator
npm install openai
Khi được hỏi, chọn:
- TypeScript: Yes
- Tailwind CSS: Yes
- App Router: Yes
Bước 2: OpenAI API Key
- Đăng ký tại platform.openai.com
- Vào API Keys → Create new key
- Tạo file
.env.localtrong project root:
OPENAI_API_KEY=sk-proj-your-key-here
Quan trọng: Không bao giờ commit file
.env.locallên Git. File.gitignorecủa Next.js đã tự động ignore nó.
Bước 3: Tạo API Route
Tạo file app/api/generate/route.ts:
import { NextRequest, NextResponse } from 'next/server'
import OpenAI from 'openai'
// Khởi tạo OpenAI client — tự động đọc OPENAI_API_KEY từ env
const openai = new OpenAI()
export async function POST(request: NextRequest) {
try {
// Lấy nội dung từ request body
const { emailDescription } = await request.json()
if (!emailDescription || emailDescription.trim().length < 10) {
return NextResponse.json(
{ error: 'Mô tả email phải có ít nhất 10 ký tự' },
{ status: 400 }
)
}
// Gọi OpenAI API
const completion = await openai.chat.completions.create({
model: 'gpt-4o-mini', // Rẻ hơn gpt-4o, đủ tốt cho task này
messages: [
{
role: 'system',
content: 'Bạn là chuyên gia viết email marketing. Hãy tạo ra các subject line thu hút, ngắn gọn (dưới 60 ký tự), không dùng caps lock quá nhiều, không spam-like.'
},
{
role: 'user',
content: `Tạo 5 subject line cho email về: ${emailDescription}
Yêu cầu:
- Mỗi cái một kiểu tiếp cận khác nhau (tò mò, benefit, urgency, personal, direct)
- Dưới 60 ký tự mỗi cái
- Có thể dùng emoji nếu phù hợp
Trả về JSON format:
{"subjects": ["subject 1", "subject 2", "subject 3", "subject 4", "subject 5"]}`
}
],
response_format: { type: 'json_object' }
})
// Parse response
const result = JSON.parse(completion.choices[0].message.content || '{}')
return NextResponse.json({
subjects: result.subjects || [],
tokensUsed: completion.usage?.total_tokens
})
} catch (error: unknown) {
console.error('OpenAI API Error:', error)
if (error instanceof Error && error.message.includes('API key')) {
return NextResponse.json(
{ error: 'API key không hợp lệ' },
{ status: 401 }
)
}
return NextResponse.json(
{ error: 'Đã có lỗi xảy ra. Thử lại sau.' },
{ status: 500 }
)
}
}
Bước 4: Tạo Giao Diện
Thay thế nội dung app/page.tsx:
'use client'
import { useState } from 'react'
export default function Home() {
const [description, setDescription] = useState('')
const [subjects, setSubjects] = useState<string[]>([])
const [loading, setLoading] = useState(false)
const [error, setError] = useState('')
async function handleGenerate() {
if (!description.trim()) return
setLoading(true)
setError('')
setSubjects([])
try {
const response = await fetch('/api/generate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ emailDescription: description })
})
const data = await response.json()
if (!response.ok) {
setError(data.error || 'Có lỗi xảy ra')
return
}
setSubjects(data.subjects)
} catch {
setError('Không thể kết nối server. Kiểm tra internet và API key.')
} finally {
setLoading(false)
}
}
return (
<main className="min-h-screen bg-gray-950 text-white flex items-center justify-center p-4">
<div className="w-full max-w-2xl">
{/* Header */}
<div className="text-center mb-10">
<h1 className="text-4xl font-bold mb-3 bg-gradient-to-r from-blue-400 to-cyan-400 bg-clip-text text-transparent">
✉️ AI Subject Generator
</h1>
<p className="text-gray-400">
Mô tả email của bạn → AI generate 5 subject lines ấn tượng
</p>
</div>
{/* Input Card */}
<div className="bg-gray-900 border border-gray-800 rounded-2xl p-6 mb-6">
<label className="block text-sm font-medium text-gray-400 mb-2">
Email về chủ đề gì?
</label>
<textarea
value={description}
onChange={(e) => setDescription(e.target.value)}
placeholder="VD: Email giới thiệu khóa học AI miễn phí cho người mới bắt đầu, kèm bonus template prompts..."
className="w-full bg-gray-800 border border-gray-700 rounded-xl p-4 text-white placeholder-gray-500 resize-none focus:outline-none focus:border-blue-500 transition-colors"
rows={4}
/>
<div className="mt-2 text-xs text-gray-500 text-right">
{description.length} ký tự
</div>
</div>
{/* Generate Button */}
<button
onClick={handleGenerate}
disabled={loading || description.trim().length < 10}
className="w-full py-4 rounded-xl font-semibold text-lg bg-gradient-to-r from-blue-500 to-cyan-500 hover:from-blue-600 hover:to-cyan-600 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200"
>
{loading ? (
<span className="flex items-center justify-center gap-2">
<span className="animate-spin">⟳</span>
Đang generate...
</span>
) : (
'⚡ Generate Subject Lines'
)}
</button>
{/* Error */}
{error && (
<div className="mt-4 p-4 bg-red-900/30 border border-red-800 rounded-xl text-red-400">
{error}
</div>
)}
{/* Results */}
{subjects.length > 0 && (
<div className="mt-6">
<h2 className="text-lg font-semibold text-gray-300 mb-3">
✨ 5 Subject Lines:
</h2>
<div className="space-y-3">
{subjects.map((subject, index) => (
<div
key={index}
onClick={() => navigator.clipboard.writeText(subject)}
className="bg-gray-900 border border-gray-800 rounded-xl p-4 cursor-pointer hover:border-blue-500 transition-colors group"
title="Click để copy"
>
<span className="text-gray-400 text-sm mr-2">{index + 1}.</span>
<span className="text-white">{subject}</span>
<span className="text-gray-600 text-xs ml-2 opacity-0 group-hover:opacity-100 transition-opacity">
Click để copy
</span>
</div>
))}
</div>
</div>
)}
</div>
</main>
)
}
Bước 5: Chạy Local
npm run dev
Mở http://localhost:3000 — app của bạn đã chạy!
Bước 6: Deploy Lên Vercel (Free)
# Cài Vercel CLI
npm i -g vercel
# Deploy
vercel
# Thêm env variable trong Vercel dashboard:
# Settings → Environment Variables → OPENAI_API_KEY
Sau vài phút bạn sẽ có URL public để share với mọi người.
Hiểu Code Đã Viết
| Phần | Giải thích |
|---|---|
app/api/generate/route.ts | Server-side API, gọi OpenAI — secret key an toàn |
response_format: json_object | Bắt AI trả về JSON chuẩn, dễ parse |
gpt-4o-mini | Model rẻ hơn 10x nhưng đủ tốt cho task đơn giản |
'use client' | Component chạy ở browser, dùng được state và event |
.env.local | Env vars chỉ available server-side, không expose ra browser |
Bước Tiếp Theo
- Build chatbot đầy đủ với conversation history: AI Chatbot Guide
- Tìm hiểu API sâu hơn: AI API Guide
- Dùng Cursor AI để code nhanh hơn: Cursor AI Review
- Build với v0.dev (no-code alternative): v0.dev Guide
- Scale lên với RAG: RAG Guide