A real estate tracking application for house hunting in Italy, powered by Restate for durable execution and Vercel AI SDK for intelligent property analysis.
- π Property Scraping: Automatically extract property details from idealista.it and immobiliare.it
- π Property Management: Track properties with customizable status pipeline (to reach out β visit β offer β bought/rejected)
- π Notes: Add manual notes to each property for additional context
- π€ AI Analysis: Ask questions about your properties and get intelligent comparisons and insights
- π Filtering & Sorting: Easily filter by status and sort by price, size, or status
- πͺ Durable Execution: Powered by Restate - scraping and AI operations survive failures
- Backend: Restate Virtual Objects for stateful workflow management
- AI: Vercel AI SDK with OpenAI GPT-4o for property analysis
- Frontend: Next.js 15 (App Router)
- Scraping: Cheerio for HTML parsing, AI-powered data extraction
- Language: TypeScript
- Node.js 18+
- npm or yarn
- OpenAI API key (get one at platform.openai.com)
- Restate Server (installation guide)
npm installCreate a .env.local file or export the OpenAI API key:
export OPENAI_API_KEY=your_openai_api_keyIn a separate terminal, start the Restate server (the durable orchestrator):
npx @restatedev/restate-server@latestThe server will start on:
- API endpoint:
http://localhost:8080 - Admin UI:
http://localhost:9070
npm run devThe Next.js app will start on http://localhost:3000
Register your services so Restate can proxy and durably execute them:
npx @restatedev/restate deployments register -y --use-http1.1 http://localhost:3000/restateYou can also register services via the Restate Admin UI at http://localhost:9070
Open http://localhost:3000 in your browser and:
- Create a new research project with your search criteria
- Add property listings from idealista.it or immobiliare.it
- Track properties through your pipeline
- Ask AI questions about your properties
The application follows a 3-tier architecture:
βββββββββββββββββββββββββββββββββββββββββββ
β Next.js Frontend (React) β
β - Research creation page β
β - Property table with filtering/sortingβ
β - Q&A sidebar β
βββββββββββββββββββ¬ββββββββββββββββββββββββ
β
β HTTP
β
βββββββββββββββββββΌββββββββββββββββββββββββ
β Next.js API Routes (REST) β
β - /api/research β
β - /api/ads β
β - /api/questions β
βββββββββββββββββββ¬ββββββββββββββββββββββββ
β
β Restate Client
β
βββββββββββββββββββΌββββββββββββββββββββββββ
β Restate Server β
β (Proxies & durably executes services) β
βββββββββββββββββββ¬ββββββββββββββββββββββββ
β
β
βββββββββββββββββββΌββββββββββββββββββββββββ
β ResearchTracker Virtual Object β
β - Stores research criteria & ads β
β - Scrapes property listings (durable) β
β - AI-powered Q&A (durable LLM calls) β
βββββββββββββββββββββββββββββββββββββββββββ
- User submits a property URL (idealista.it or immobiliare.it)
- API route calls ResearchTracker Virtual Object via Restate
- Virtual Object determines which scraper to use based on domain
- Scraper fetches HTML with proper User-Agent headers
- AI (GPT-4o-mini) extracts structured data from HTML using Italian prompts
- Property data is stored in Virtual Object state with deterministic ID
- Result returned to frontend
Key durability features:
ctx.run()wraps scraping to journal results (runs only once)ctx.rand.uuidv4()generates deterministic IDs for propertiesdurableCalls()middleware journals LLM responses for Q&A
-
POST /api/research- Create new research project- Body:
{ name: string, criteria: string } - Returns:
{ name: string, criteria: string }
- Body:
-
GET /api/research/:name- Get research criteria- Returns:
{ name: string, criteria: string }
- Returns:
-
POST /api/ads/:researchName- Add property listing- Body:
{ url: string } - Returns:
PropertyAdobject
- Body:
-
GET /api/ads/:researchName- List all ads- Returns:
{ ads: PropertyAd[] }
- Returns:
-
PATCH /api/ads/:researchName/:adId/status- Update ad status- Body:
{ status: "to reach out" | "visit appointment taken" | "sent the offer" | "bought" | "rejected" }
- Body:
-
PATCH /api/ads/:researchName/:adId/notes- Update ad notes- Body:
{ notes: string }
- Body:
-
POST /api/questions/:researchName- Ask AI question- Body:
{ question: string } - Returns:
{ question: string, answer: string }
- Body:
-
GET /api/questions/:researchName- Get Q&A history- Returns:
{ questions: QuestionAnswer[] }
- Returns:
re-state/
βββ app/ # Next.js app directory
β βββ page.tsx # Home page (research creation)
β βββ research/[name]/page.tsx # Research management page
β βββ api/ # API routes
β βββ research/ # Research endpoints
β βββ ads/ # Property ad endpoints
β βββ questions/ # Q&A endpoints
βββ restate/ # Restate services
β βββ endpoint.ts # Service registration
β βββ services/
β βββ types.ts # Zod schemas & types
β βββ research-tracker.ts # Virtual Object
β βββ utils/
β βββ idealista-scraper.ts
β βββ immobiliare-scraper.ts
βββ package.json
To support additional property listing sites:
- Create a new scraper in
restate/services/utils/ - Follow the same pattern as existing scrapers (AI extraction)
- Add domain check in
research-tracker.tsaddAd handler - Update API route validation to allow the new domain
Check the Restate Admin UI at http://localhost:9070 to:
- View invocation journals
- See service state
- Monitor durability and retries
- Inspect completed operations
This application is designed to be deployed on Vercel with Restate Cloud or self-hosted Restate:
- Deploy Next.js app to Vercel
- Set
OPENAI_API_KEYin Vercel environment variables - Set
RESTATE_URLto your Restate Cloud or self-hosted endpoint - Register services with your production Restate instance
For production deployment details, see Restate deployment docs.