No code, no terminal. A director plans it, a build agent writes it, verification runs — then a live URL appears. You watch.
Simpl will ask a couple of quick questions first
import { NextRequest, NextResponse } from 'next/server'
import { createClient } from '@/lib/supabase/server'
import { z } from 'zod'
const GuestSchema = z.object({
name: z.string().min(1),
email: z.string().email(),
message: z.string().min(10).max(500),
})
export async function POST(req: NextRequest) {
const body = await req.json()
const parsed = GuestSchema.safeParse(body)
if (!parsed.success) {
return NextResponse.json(
{ error: parsed.error.flatten() },
{ status: 400 }
)
}
const supabase = createClient()
const { error } = await supabase
.from('guestbook')
.insert(parsed.data)
if (error) {
return NextResponse.json(
{ error: error.message },
{ status: 500 }
)
}
return NextResponse.json({ ok: true })
}
export async function GET() {
const supabase = createClient()
const { data, error } = await supabase
.from('guestbook')
.select('id, name, message, created_at')
.order('created_at', { ascending: false })
.limit(50)
if (error) {
return NextResponse.json(
{ error: error.message },
{ status: 500 }
)
}
return NextResponse.json({ data })
}
'use client'
import { useState } from 'react'
type Entry = {
id: string
name: string
message: string
created_at: string
}
export default function Guestbook({
entries,
}: {
entries: Entry[]
}) {
const [name, setName] = useState('')
const [message, setMessage] = useState('')
const [status, setStatus] =
useState<'idle' | 'sending' | 'done'>('idle')
async function handleSubmit(
e: React.FormEvent
) {
e.preventDefault()
setStatus('sending')
const res = await fetch('/api/guestbook', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ name, message }),
})
setStatus(res.ok ? 'done' : 'idle')
}
return (
<main className="max-w-2xl mx-auto p-6">
<h1 className="text-2xl font-bold mb-8">
Guestbook
</h1>
<form
onSubmit={handleSubmit}
className="space-y-4 mb-10"
>
<input
value={name}
onChange={e => setName(e.target.value)}
placeholder="Your name"
required
className="w-full border rounded px-3 py-2"
/>
<textarea
value={message}
onChange={e => setMessage(e.target.value)}
placeholder="Leave a message..."
rows={4}
className="w-full border rounded px-3 py-2"
/>
<button
type="submit"
disabled={status === 'sending'}
>
{status === 'sending'
? 'Signing...'
: 'Sign guestbook'}
</button>
{status === 'done' && (
<p className="text-green-600">
Thanks for signing!
</p>
)}
</form>
<ul className="space-y-4">
{entries.map(entry => (
<li
key={entry.id}
className="border-b pb-3"
>
<p className="font-medium">
{entry.name}
</p>
<p className="text-gray-500 text-sm">
{entry.message}
</p>
</li>
))}
</ul>
</main>
)
}
import { NextRequest, NextResponse } from 'next/server'
import { createClient } from '@/lib/supabase/server'
import { z } from 'zod'
const GuestSchema = z.object({
name: z.string().min(1),
email: z.string().email(),
message: z.string().min(10).max(500),
})
export async function POST(req: NextRequest) {
const body = await req.json()
const parsed = GuestSchema.safeParse(body)
if (!parsed.success) {
return NextResponse.json(
{ error: parsed.error.flatten() },
{ status: 400 }
)
}
const supabase = createClient()
const { error } = await supabase
.from('guestbook')
.insert(parsed.data)
if (error) {
return NextResponse.json(
{ error: error.message },
{ status: 500 }
)
}
return NextResponse.json({ ok: true })
}
export async function GET() {
const supabase = createClient()
const { data, error } = await supabase
.from('guestbook')
.select('id, name, message, created_at')
.order('created_at', { ascending: false })
.limit(50)
if (error) {
return NextResponse.json(
{ error: error.message },
{ status: 500 }
)
}
return NextResponse.json({ data })
}
'use client'
import { useState } from 'react'
type Entry = {
id: string
name: string
message: string
created_at: string
}
export default function Guestbook({
entries,
}: {
entries: Entry[]
}) {
const [name, setName] = useState('')
const [message, setMessage] = useState('')
const [status, setStatus] =
useState<'idle' | 'sending' | 'done'>('idle')
async function handleSubmit(
e: React.FormEvent
) {
e.preventDefault()
setStatus('sending')
const res = await fetch('/api/guestbook', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ name, message }),
})
setStatus(res.ok ? 'done' : 'idle')
}
return (
<main className="max-w-2xl mx-auto p-6">
<h1 className="text-2xl font-bold mb-8">
Guestbook
</h1>
<form
onSubmit={handleSubmit}
className="space-y-4 mb-10"
>
<input
value={name}
onChange={e => setName(e.target.value)}
placeholder="Your name"
required
className="w-full border rounded px-3 py-2"
/>
<textarea
value={message}
onChange={e => setMessage(e.target.value)}
placeholder="Leave a message..."
rows={4}
className="w-full border rounded px-3 py-2"
/>
<button
type="submit"
disabled={status === 'sending'}
>
{status === 'sending'
? 'Signing...'
: 'Sign guestbook'}
</button>
{status === 'done' && (
<p className="text-green-600">
Thanks for signing!
</p>
)}
</form>
<ul className="space-y-4">
{entries.map(entry => (
<li
key={entry.id}
className="border-b pb-3"
>
<p className="font-medium">
{entry.name}
</p>
<p className="text-gray-500 text-sm">
{entry.message}
</p>
</li>
))}
</ul>
</main>
)
}
Write a sentence or two about what you want to build. No technical language required.
A director plans the work, a build agent writes the code, and verification runs automatically.
Your app deploys to a real URL. Real code, real hosting — nothing fake or sandboxed.