AI Full-Stack App Builder – Complete Build and Integration Guide
Part 1: Building the Standalone AI Full-Stack App Builder
What You're Building
The AI Full-Stack App Builder is a web application that transforms natural language descriptions into complete, runnable web applications. Users describe what they want to build (e.g., "A project management app with users, projects, and tasks"), and the system generates database schemas, REST APIs, and frontend pages. The app includes a visual schema editor, code preview, and export functionality for generated projects.
Core Features
- Natural Language Processing: Convert plain English descriptions into technical specifications
- Database Schema Generation: Auto-create MongoDB schemas with proper relationships
- API Generation: Generate complete REST APIs with CRUD operations
- Frontend Scaffolding: Create Next.js pages and components
- Visual Schema Editor: Interactive database schema editing and validation
- Code Preview: Live preview of generated code before export
- Project Export: Download complete projects as ZIP files or push to Git
Tech Stack
Frontend:
- Next.js 14 with App Router
- TypeScript for type safety
- Tailwind CSS for styling
- React Flow for schema visualization
- Monaco Editor for code preview
- React Hook Form for form management
Backend:
- Next.js API routes
- OpenAI GPT-4 for specification parsing
- MongoDB for project storage
- JSZip for project packaging
- Git integration for repository creation
Development Tools:
- ESLint and Prettier for code quality
- Jest for testing
- Docker for containerization
Project Structure
app-builder/
├── app/
│ ├── (auth)/
│ │ └── login/
│ ├── (dashboard)/
│ │ ├── builder/
│ │ │ ├── page.tsx
│ │ │ ├── new/
│ │ │ │ └── page.tsx
│ │ │ └── [id]/
│ │ │ └── page.tsx
│ │ └── layout.tsx
│ ├── api/
│ │ ├── projects/
│ │ │ ├── route.ts
│ │ │ ├── [id]/
│ │ │ │ └── route.ts
│ │ │ └── generate/
│ │ │ └── route.ts
│ │ ├── export/
│ │ │ └── route.ts
│ │ └── templates/
│ │ └── route.ts
│ ├── globals.css
│ └── layout.tsx
├── components/
│ ├── builder/
│ │ ├── SpecInput.tsx
│ │ ├── SchemaEditor.tsx
│ │ ├── APIPreview.tsx
│ │ ├── CodePreview.tsx
│ │ ├── ExportOptions.tsx
│ │ └── ProjectWizard.tsx
│ ├── ui/
│ │ ├── Button.tsx
│ │ ├── Input.tsx
│ │ ├── Textarea.tsx
│ │ ├── Modal.tsx
│ │ └── CodeEditor.tsx
│ └── layout/
│ ├── Header.tsx
│ └── Sidebar.tsx
├── lib/
│ ├── ai/
│ │ └── specParser.ts
│ ├── generator/
│ │ ├── schemaGenerator.ts
│ │ ├── apiGenerator.ts
│ │ ├── frontendGenerator.ts
│ │ └── projectBuilder.ts
│ ├── export/
│ │ ├── zipExporter.ts
│ │ └── gitExporter.ts
│ ├── database/
│ │ └── mongodb.ts
│ └── utils.ts
├── templates/
│ ├── backend/
│ │ ├── models/
│ │ ├── controllers/
│ │ ├── routes/
│ │ └── server.js
│ ├── frontend/
│ │ ├── pages/
│ │ ├── components/
│ │ └── styles/
│ └── config/
│ ├── package.json
│ └── README.md
├── types/
│ └── builder.ts
├── public/
│ └── icons/
├── package.json
├── next.config.js
├── tailwind.config.js
├── tsconfig.json
└── .env.local
Data Models
// types/builder.ts
export interface Entity {
name: string;
fields: Field[];
relationships: Relationship[];
indexes?: Index[];
}
export interface Field {
name: string;
type: 'string' | 'number' | 'boolean' | 'date' | 'array' | 'object';
required: boolean;
unique?: boolean;
default?: any;
validation?: ValidationRule[];
}
export interface Relationship {
type: 'one-to-one' | 'one-to-many' | 'many-to-many';
target: string;
field?: string;
foreignField?: string;
}
export interface Index {
fields: string[];
unique?: boolean;
sparse?: boolean;
}
export interface ProjectSpec {
id: string;
name: string;
description: string;
entities: Entity[];
techStack: {
backend: 'node' | 'python' | 'java';
frontend: 'nextjs' | 'react' | 'vue';
database: 'mongodb' | 'postgresql' | 'mysql';
};
features: string[];
createdAt: Date;
updatedAt: Date;
userId: string;
}
export interface GenerationRequest {
description: string;
techStack?: {
backend?: string;
frontend?: string;
database?: string;
};
features?: string[];
}
API Endpoints
// app/api/projects/route.ts
POST /api/projects // Create new project spec
GET /api/projects // List user's projects
// app/api/projects/[id]/route.ts
GET /api/projects/[id] // Get specific project
PUT /api/projects/[id] // Update project
DELETE /api/projects/[id] // Delete project
// app/api/projects/generate/route.ts
POST /api/projects/generate // Generate project from spec
// app/api/export/route.ts
POST /api/export // Export project as ZIP or Git
// app/api/templates/route.ts
GET /api/templates // Get available templates
Core Components
1. Specification Input Component
// components/builder/SpecInput.tsx
'use client';
import { useState } from 'react';
import { Button } from '@/components/ui/Button';
import { Textarea } from '@/components/ui/Textarea';
interface SpecInputProps {
onSubmit: (spec: GenerationRequest) => void;
loading?: boolean;
}
export default function SpecInput({ onSubmit, loading }: SpecInputProps) {
const [description, setDescription] = useState('');
const [techStack, setTechStack] = useState({
backend: 'node',
frontend: 'nextjs',
database: 'mongodb'
});
const [features, setFeatures] = useState<string[]>([]);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (!description.trim()) return;
onSubmit({
description,
techStack,
features
});
};
const addFeature = (feature: string) => {
if (feature.trim() && !features.includes(feature)) {
setFeatures([...features, feature]);
}
};
const removeFeature = (index: number) => {
setFeatures(features.filter((_, i) => i !== index));
};
return (
<form onSubmit={handleSubmit} className="space-y-6">
<div>
<label className="block text-sm font-medium mb-2">
Describe your application
</label>
<Textarea
value={description}
onChange={(e) => setDescription(e.target.value)}
placeholder="Describe what you want to build... (e.g., 'A project management app with users, projects, tasks, and comments. Users can create projects, assign tasks to team members, and track progress.')"
rows={6}
className="w-full"
/>
</div>
<div className="grid grid-cols-3 gap-4">
<div>
<label className="block text-sm font-medium mb-2">Backend</label>
<select
value={techStack.backend}
onChange={(e) => setTechStack({...techStack, backend: e.target.value})}
className="w-full p-2 border rounded-md"
>
<option value="node">Node.js</option>
<option value="python">Python</option>
<option value="java">Java</option>
</select>
</div>
<div>
<label className="block text-sm font-medium mb-2">Frontend</label>
<select
value={techStack.frontend}
onChange={(e) => setTechStack({...techStack, frontend: e.target.value})}
className="w-full p-2 border rounded-md"
>
<option value="nextjs">Next.js</option>
<option value="react">React</option>
<option value="vue">Vue.js</option>
</select>
</div>
<div>
<label className="block text-sm font-medium mb-2">Database</label>
<select
value={techStack.database}
onChange={(e) => setTechStack({...techStack, database: e.target.value})}
className="w-full p-2 border rounded-md"
>
<option value="mongodb">MongoDB</option>
<option value="postgresql">PostgreSQL</option>
<option value="mysql">MySQL</option>
</select>
</div>
</div>
<div>
<label className="block text-sm font-medium mb-2">Additional Features</label>
<div className="flex flex-wrap gap-2 mb-2">
{features.map((feature, index) => (
<span
key={index}
className="bg-blue-100 text-blue-800 px-2 py-1 rounded text-sm flex items-center gap-1"
>
{feature}
<button
type="button"
onClick={() => removeFeature(index)}
className="text-blue-600 hover:text-blue-800"
>
×
</button>
</span>
))}
</div>
<input
type="text"
placeholder="Add feature (e.g., 'authentication', 'file upload')"
className="w-full p-2 border rounded-md"
onKeyPress={(e) => {
if (e.key === 'Enter') {
e.preventDefault();
addFeature(e.currentTarget.value);
e.currentTarget.value = '';
}
}}
/>
</div>
<Button type="submit" disabled={loading || !description.trim()}>
{loading ? 'Generating...' : 'Generate App'}
</Button>
</form>
);
}
2. Schema Editor Component
// components/builder/SchemaEditor.tsx
'use client';
import { useState } from 'react';
import { Entity } from '@/types/builder';
import { Button } from '@/components/ui/Button';
interface SchemaEditorProps {
entities: Entity[];
onUpdateEntity: (index: number, entity: Entity) => void;
onAddEntity: () => void;
onRemoveEntity: (index: number) => void;
}
export default function SchemaEditor({
entities,
onUpdateEntity,
onAddEntity,
onRemoveEntity
}: SchemaEditorProps) {
const [selectedEntity, setSelectedEntity] = useState<number>(0);
const updateEntityField = (entityIndex: number, fieldIndex: number, updates: Partial<Field>) => {
const entity = entities[entityIndex];
const newFields = [...entity.fields];
newFields[fieldIndex] = { ...newFields[fieldIndex], ...updates };
onUpdateEntity(entityIndex, { ...entity, fields: newFields });
};
const addField = (entityIndex: number) => {
const entity = entities[entityIndex];
const newField: Field = {
name: 'newField',
type: 'string',
required: false,
unique: false
};
onUpdateEntity(entityIndex, { ...entity, fields: [...entity.fields, newField] });
};
const removeField = (entityIndex: number, fieldIndex: number) => {
const entity = entities[entityIndex];
const newFields = entity.fields.filter((_, i) => i !== fieldIndex);
onUpdateEntity(entityIndex, { ...entity, fields: newFields });
};
return (
<div className="flex h-full">
{/* Entity List */}
<div className="w-64 bg-gray-50 border-r p-4">
<div className="flex items-center justify-between mb-4">
<h3 className="font-semibold">Entities</h3>
<Button onClick={onAddEntity} size="sm">
+ Add
</Button>
</div>
<div className="space-y-2">
{entities.map((entity, index) => (
<div
key={index}
className={`p-3 border rounded cursor-pointer ${
selectedEntity === index ? 'bg-blue-50 border-blue-300' : 'bg-white'
}`}
onClick={() => setSelectedEntity(index)}
>
<div className="flex items-center justify-between">
<span className="font-medium">{entity.name}</span>
<button
onClick={(e) => {
e.stopPropagation();
onRemoveEntity(index);
}}
className="text-red-500 hover:text-red-700"
>
×
</button>
</div>
<div className="text-sm text-gray-500">
{entity.fields.length} fields
</div>
</div>
))}
</div>
</div>
{/* Entity Editor */}
<div className="flex-1 p-4">
{entities[selectedEntity] && (
<div>
<div className="mb-4">
<input
type="text"
value={entities[selectedEntity].name}
onChange={(e) => onUpdateEntity(selectedEntity, {
...entities[selectedEntity],
name: e.target.value
})}
className="text-xl font-bold border-none outline-none"
/>
</div>
<div className="space-y-3">
<div className="flex items-center justify-between">
<h4 className="font-medium">Fields</h4>
<Button onClick={() => addField(selectedEntity)} size="sm">
+ Add Field
</Button>
</div>
{entities[selectedEntity].fields.map((field, fieldIndex) => (
<div key={fieldIndex} className="flex items-center space-x-2 p-3 border rounded">
<input
type="text"
value={field.name}
onChange={(e) => updateEntityField(selectedEntity, fieldIndex, {
name: e.target.value
})}
className="flex-1 p-2 border rounded"
placeholder="Field name"
/>
<select
value={field.type}
onChange={(e) => updateEntityField(selectedEntity, fieldIndex, {
type: e.target.value as any
})}
className="p-2 border rounded"
>
<option value="string">String</option>
<option value="number">Number</option>
<option value="boolean">Boolean</option>
<option value="date">Date</option>
<option value="array">Array</option>
<option value="object">Object</option>
</select>
<label className="flex items-center space-x-1">
<input
type="checkbox"
checked={field.required}
onChange={(e) => updateEntityField(selectedEntity, fieldIndex, {
required: e.target.checked
})}
/>
<span className="text-sm">Required</span>
</label>
<label className="flex items-center space-x-1">
<input
type="checkbox"
checked={field.unique || false}
onChange={(e) => updateEntityField(selectedEntity, fieldIndex, {
unique: e.target.checked
})}
/>
<span className="text-sm">Unique</span>
</label>
<button
onClick={() => removeField(selectedEntity, fieldIndex)}
className="text-red-500 hover:text-red-700"
>
×
</button>
</div>
))}
</div>
</div>
)}
</div>
</div>
);
}
3. AI Specification Parser
// lib/ai/specParser.ts
import OpenAI from 'openai';
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
export async function parseSpecification(request: GenerationRequest): Promise<Entity[]> {
const systemPrompt = `You are an expert software architect. Parse the user's application description and generate a database schema.
Return a JSON array of entities with this exact structure:
[
{
"name": "EntityName",
"fields": [
{
"name": "fieldName",
"type": "string|number|boolean|date|array|object",
"required": true|false,
"unique": true|false
}
],
"relationships": [
{
"type": "one-to-one|one-to-many|many-to-many",
"target": "TargetEntityName",
"field": "foreignKeyField"
}
]
}
]
Guidelines:
- Use ${request.techStack?.database || 'mongodb'} database conventions
- Include common fields like id, createdAt, updatedAt
- Create proper relationships between entities
- Use appropriate field types and constraints
- Consider the features: ${request.features?.join(', ') || 'basic CRUD'}`;
const response = await openai.chat.completions.create({
model: 'gpt-4',
messages: [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: request.description }
],
temperature: 0.3,
});
const content = response.choices[0].message.content;
return JSON.parse(content || '[]');
}
4. Code Generators
// lib/generator/schemaGenerator.ts
export function generateMongoSchema(entity: Entity): string {
const fields = entity.fields.map(field => {
let fieldDef = ` ${field.name}: { type: ${getMongooseType(field.type)}`;
if (field.required) fieldDef += ', required: true';
if (field.unique) fieldDef += ', unique: true';
if (field.default !== undefined) fieldDef += `, default: ${JSON.stringify(field.default)}`;
fieldDef += ' }';
return fieldDef;
}).join(',\n');
return `const mongoose = require('mongoose');
const ${entity.name}Schema = new mongoose.Schema({
${fields}
}, {
timestamps: true
});
module.exports = mongoose.model('${entity.name}', ${entity.name}Schema);`;
}
function getMongooseType(type: string): string {
const typeMap = {
'string': 'String',
'number': 'Number',
'boolean': 'Boolean',
'date': 'Date',
'array': '[String]',
'object': 'Object'
};
return typeMap[type] || 'String';
}
// lib/generator/apiGenerator.ts
export function generateController(entity: Entity): string {
const name = entity.name.toLowerCase();
const Name = entity.name;
return `const ${Name} = require('../models/${Name}');
exports.getAll = async (req, res) => {
try {
const ${name}s = await ${Name}.find();
res.json(${name}s);
} catch (error) {
res.status(500).json({ error: error.message });
}
};
exports.getById = async (req, res) => {
try {
const ${name} = await ${Name}.findById(req.params.id);
if (!${name}) return res.status(404).json({ error: 'Not found' });
res.json(${name});
} catch (error) {
res.status(500).json({ error: error.message });
}
};
exports.create = async (req, res) => {
try {
const ${name} = new ${Name}(req.body);
await ${name}.save();
res.status(201).json(${name});
} catch (error) {
res.status(400).json({ error: error.message });
}
};
exports.update = async (req, res) => {
try {
const ${name} = await ${Name}.findByIdAndUpdate(
req.params.id,
req.body,
{ new: true }
);
if (!${name}) return res.status(404).json({ error: 'Not found' });
res.json(${name});
} catch (error) {
res.status(400).json({ error: error.message });
}
};
exports.delete = async (req, res) => {
try {
const ${name} = await ${Name}.findByIdAndDelete(req.params.id);
if (!${name}) return res.status(404).json({ error: 'Not found' });
res.json({ message: 'Deleted successfully' });
} catch (error) {
res.status(500).json({ error: error.message });
}
};`;
}
// lib/generator/frontendGenerator.ts
export function generateNextJSPage(entity: Entity): string {
const name = entity.name.toLowerCase();
const Name = entity.name;
return `import { useState, useEffect } from 'react';
import { ${Name} } from '@/types/${name}';
export default function ${Name}Page() {
const [${name}s, set${Name}s] = useState<${Name}[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch${Name}s();
}, []);
const fetch${Name}s = async () => {
try {
const response = await fetch('/api/${name}s');
const data = await response.json();
set${Name}s(data);
} catch (error) {
console.error('Error fetching ${name}s:', error);
} finally {
setLoading(false);
}
};
if (loading) return <div>Loading...</div>;
return (
<div className="container mx-auto p-6">
<h1 className="text-2xl font-bold mb-6">${Name}s</h1>
<div className="grid gap-4">
{${name}s.map((${name}) => (
<div key={${name}.id} className="border rounded p-4">
<h3 className="font-semibold">${name}.name</h3>
{/* Add more fields as needed */}
</div>
))}
</div>
</div>
);
}`;
}
5. Project Builder and Exporter
// lib/generator/projectBuilder.ts
import JSZip from 'jszip';
import { ProjectSpec } from '@/types/builder';
import { generateMongoSchema } from './schemaGenerator';
import { generateController } from './apiGenerator';
import { generateNextJSPage } from './frontendGenerator';
export async function buildProject(spec: ProjectSpec): Promise<Buffer> {
const zip = new JSZip();
// Add package.json
zip.file('package.json', generatePackageJson(spec));
// Add backend files
const backendFolder = zip.folder('backend');
backendFolder.file('server.js', generateServerFile(spec));
const modelsFolder = backendFolder.folder('models');
spec.entities.forEach(entity => {
modelsFolder.file(`${entity.name}.js`, generateMongoSchema(entity));
});
const controllersFolder = backendFolder.folder('controllers');
spec.entities.forEach(entity => {
controllersFolder.file(`${entity.name}Controller.js`, generateController(entity));
});
// Add frontend files
const frontendFolder = zip.folder('frontend');
frontendFolder.file('package.json', generateFrontendPackageJson(spec));
const pagesFolder = frontendFolder.folder('pages');
spec.entities.forEach(entity => {
pagesFolder.file(`${entity.name.toLowerCase()}.tsx`, generateNextJSPage(entity));
});
// Add README
zip.file('README.md', generateReadme(spec));
return zip.generateAsync({ type: 'nodebuffer' });
}
function generatePackageJson(spec: ProjectSpec): string {
return JSON.stringify({
name: spec.name.toLowerCase().replace(/\s+/g, '-'),
version: '1.0.0',
description: spec.description,
main: 'backend/server.js',
scripts: {
start: 'node backend/server.js',
dev: 'nodemon backend/server.js',
'dev:frontend': 'cd frontend && npm run dev'
},
dependencies: {
express: '^4.18.0',
mongoose: '^7.0.0',
cors: '^2.8.5',
dotenv: '^16.0.0'
},
devDependencies: {
nodemon: '^2.0.0'
}
}, null, 2);
}
function generateServerFile(spec: ProjectSpec): string {
const imports = spec.entities.map(entity =>
`const ${entity.name}Controller = require('./controllers/${entity.name}Controller');`
).join('\n');
const routes = spec.entities.map(entity => {
const name = entity.name.toLowerCase();
return `
// ${entity.name} routes
app.get('/api/${name}s', ${entity.name}Controller.getAll);
app.get('/api/${name}s/:id', ${entity.name}Controller.getById);
app.post('/api/${name}s', ${entity.name}Controller.create);
app.put('/api/${name}s/:id', ${entity.name}Controller.update);
app.delete('/api/${name}s/:id', ${entity.name}Controller.delete);`;
}).join('\n');
return `const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
require('dotenv').config();
${imports}
const app = express();
const PORT = process.env.PORT || 3000;
// Middleware
app.use(cors());
app.use(express.json());
// Connect to MongoDB
mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/${spec.name.toLowerCase()}');
// Routes
${routes}
app.listen(PORT, () => {
console.log(\`Server running on port \${PORT}\`);
});`;
}
Main Application Page
// app/(dashboard)/builder/page.tsx
'use client';
import { useState, useEffect } from 'react';
import { ProjectSpec, Entity } from '@/types/builder';
import SpecInput from '@/components/builder/SpecInput';
import SchemaEditor from '@/components/builder/SchemaEditor';
import APIPreview from '@/components/builder/APIPreview';
import CodePreview from '@/components/builder/CodePreview';
import ExportOptions from '@/components/builder/ExportOptions';
export default function BuilderPage() {
const [projects, setProjects] = useState<ProjectSpec[]>([]);
const [currentProject, setCurrentProject] = useState<ProjectSpec | null>(null);
const [loading, setLoading] = useState(false);
const [step, setStep] = useState<'input' | 'schema' | 'preview' | 'export'>('input');
const handleGenerateSpec = async (request: GenerationRequest) => {
setLoading(true);
try {
const response = await fetch('/api/projects/generate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(request),
});
const entities = await response.json();
const newProject: ProjectSpec = {
id: crypto.randomUUID(),
name: 'New Project',
description: request.description,
entities,
techStack: request.techStack || {
backend: 'node',
frontend: 'nextjs',
database: 'mongodb'
},
features: request.features || [],
createdAt: new Date(),
updatedAt: new Date(),
userId: 'current-user-id'
};
setCurrentProject(newProject);
setStep('schema');
} catch (error) {
console.error('Failed to generate spec:', error);
} finally {
setLoading(false);
}
};
const handleUpdateEntity = (index: number, entity: Entity) => {
if (!currentProject) return;
const updatedEntities = [...currentProject.entities];
updatedEntities[index] = entity;
setCurrentProject({
...currentProject,
entities: updatedEntities,
updatedAt: new Date()
});
};
const handleAddEntity = () => {
if (!currentProject) return;
const newEntity: Entity = {
name: 'NewEntity',
fields: [
{ name: 'id', type: 'string', required: true, unique: true },
{ name: 'name', type: 'string', required: true }
],
relationships: []
};
setCurrentProject({
...currentProject,
entities: [...currentProject.entities, newEntity],
updatedAt: new Date()
});
};
const handleRemoveEntity = (index: number) => {
if (!currentProject) return;
const updatedEntities = currentProject.entities.filter((_, i) => i !== index);
setCurrentProject({
...currentProject,
entities: updatedEntities,
updatedAt: new Date()
});
};
const handleExport = async (format: 'zip' | 'git') => {
if (!currentProject) return;
try {
const response = await fetch('/api/export', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ projectId: currentProject.id, format }),
});
if (format === 'zip') {
const blob = await response.blob();
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `${currentProject.name}.zip`;
a.click();
URL.revokeObjectURL(url);
}
} catch (error) {
console.error('Failed to export project:', error);
}
};
return (
<div className="min-h-screen bg-gray-50">
<div className="max-w-7xl mx-auto p-6">
<div className="mb-8">
<h1 className="text-3xl font-bold mb-2">AI Full-Stack App Builder</h1>
<p className="text-gray-600">Transform your ideas into complete web applications</p>
</div>
{/* Progress Steps */}
<div className="flex items-center space-x-4 mb-8">
{['input', 'schema', 'preview', 'export'].map((stepName, index) => (
<div key={stepName} className="flex items-center">
<div className={`w-8 h-8 rounded-full flex items-center justify-center ${
step === stepName ? 'bg-blue-600 text-white' : 'bg-gray-200 text-gray-600'
}`}>
{index + 1}
</div>
<span className={`ml-2 capitalize ${
step === stepName ? 'text-blue-600 font-medium' : 'text-gray-600'
}`}>
{stepName}
</span>
{index < 3 && <div className="w-8 h-0.5 bg-gray-200 mx-2" />}
</div>
))}
</div>
{/* Step Content */}
{step === 'input' && (
<div className="max-w-2xl mx-auto">
<SpecInput onSubmit={handleGenerateSpec} loading={loading} />
</div>
)}
{step === 'schema' && currentProject && (
<div className="bg-white rounded-lg shadow-sm h-96">
<SchemaEditor
entities={currentProject.entities}
onUpdateEntity={handleUpdateEntity}
onAddEntity={handleAddEntity}
onRemoveEntity={handleRemoveEntity}
/>
<div className="p-4 border-t">
<button
onClick={() => setStep('preview')}
className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
>
Continue to Preview
</button>
</div>
</div>
)}
{step === 'preview' && currentProject && (
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<div className="bg-white rounded-lg shadow-sm p-6">
<h3 className="text-lg font-semibold mb-4">API Endpoints</h3>
<APIPreview entities={currentProject.entities} />
</div>
<div className="bg-white rounded-lg shadow-sm p-6">
<h3 className="text-lg font-semibold mb-4">Generated Code</h3>
<CodePreview project={currentProject} />
</div>
</div>
)}
{step === 'export' && currentProject && (
<div className="max-w-2xl mx-auto">
<ExportOptions
project={currentProject}
onExport={handleExport}
/>
</div>
)}
</div>
</div>
);
}
Environment Configuration
# .env.local
MONGODB_URI=mongodb://localhost:27017/app_builder
OPENAI_API_KEY=your_openai_api_key_here
GITHUB_TOKEN=your_github_token_here
NEXTAUTH_SECRET=your_nextauth_secret_here
NEXTAUTH_URL=http://localhost:3000
Package Dependencies
{
"dependencies": {
"next": "14.0.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"typescript": "5.0.0",
"tailwindcss": "3.3.0",
"openai": "4.0.0",
"jszip": "3.10.0",
"mongodb": "6.0.0",
"react-flow-renderer": "10.3.0",
"@monaco-editor/react": "4.6.0",
"react-hook-form": "7.45.0",
"@hookform/resolvers": "3.3.0",
"zod": "3.22.0"
},
"devDependencies": {
"@types/node": "20.0.0",
"@types/react": "18.2.0",
"@types/react-dom": "18.2.0",
"eslint": "8.50.0",
"eslint-config-next": "14.0.0",
"prettier": "3.0.0",
"jest": "29.7.0",
"@testing-library/react": "13.4.0"
}
}
Part 2: Now that your app is working fine, let's integrate it with Weam
Integration Overview
Once your AI Full-Stack App Builder is fully functional as a standalone application, you can integrate it into the Weam ecosystem. This integration involves:
- Authentication: Using Weam's session management
- Database: Migrating to Weam's MongoDB with proper naming conventions
- UI Theming: Matching Weam's design system
- Routing: Setting up base path configuration
- Deployment: Configuring NGINX and Docker
Weam Integration Steps
Step 1: Authentication Integration
Replace your standalone authentication with Weam's iron-session:
// lib/session.ts
import { getIronSession } from 'iron-session';
import { cookies } from 'next/headers';
export interface SessionData {
user?: {
id: string;
email: string;
name: string;
};
}
export const sessionOptions = {
cookieName: 'weam_session',
password: process.env.IRON_SESSION_PASSWORD!,
cookieOptions: {
secure: process.env.NODE_ENV === 'production',
httpOnly: true,
sameSite: 'lax' as const,
path: '/',
domain: process.env.COOKIE_DOMAIN,
maxAge: 60 * 60 * 24 * 7, // 7 days
},
};
export async function getSession() {
const cookieStore = await cookies();
return getIronSession<SessionData>(cookieStore, sessionOptions);
}
Step 2: Database Migration
Update your database collections to follow Weam's naming convention:
// lib/database/mongodb.ts
import { MongoClient } from 'mongodb';
const client = new MongoClient(process.env.MONGODB_URI!);
export const db = client.db('weam');
// Weam naming convention: solution_{solutionName}_{tableName}
export const projects = db.collection('solution_appbuilder_projects');
export const exports = db.collection('solution_appbuilder_exports');
export const templates = db.collection('solution_appbuilder_templates');
Step 3: Base Path Configuration
Update your Next.js configuration for Weam routing:
// next.config.js
const nextConfig = {
basePath: '/app-builder',
assetPrefix: '/app-builder',
async rewrites() {
return [
{
source: '/app-builder/api/:path*',
destination: '/api/:path*',
},
];
},
};
module.exports = nextConfig;
Step 4: Environment Variables
Update your environment configuration:
# .env.local
NEXT_PUBLIC_API_BASE_PATH=/app-builder
MONGODB_URI=mongodb://localhost:27017/weam
IRON_SESSION_PASSWORD=your-secure-session-password
COOKIE_DOMAIN=.weam.ai
OPENAI_API_KEY=your-openai-key
GITHUB_TOKEN=your-github-token
Step 5: Sidebar Integration
Add your solution to Weam's sidebar:
// Update src/seeders/superSolution.json
{
"name": "AI Full-Stack App Builder",
"path": "/app-builder",
"icon": "CodeBracket",
"description": "Generate full-stack applications from natural language specs"
}
Step 6: NGINX Configuration
Add routing configuration to Weam's NGINX:
# Add to /etc/nginx/sites-available/weam
location /app-builder/ {
proxy_pass http://localhost:3011/app-builder/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
Step 7: Docker Configuration
Create Docker setup for deployment:
# Dockerfile
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
FROM node:18-alpine AS production
WORKDIR /app
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/public ./public
COPY --from=builder /app/package*.json ./
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3011
CMD ["npm", "start"]
# docker-compose.yml
version: '3.8'
services:
app-builder:
build: .
ports:
- "3011:3011"
environment:
- NODE_ENV=production
- NEXT_PUBLIC_API_BASE_PATH=/app-builder
- MONGODB_URI=${MONGODB_URI}
- IRON_SESSION_PASSWORD=${IRON_SESSION_PASSWORD}
- COOKIE_DOMAIN=.weam.ai
- OPENAI_API_KEY=${OPENAI_API_KEY}
- GITHUB_TOKEN=${GITHUB_TOKEN}
restart: unless-stopped
Final Integration Checklist
✅ Standalone app fully functional
✅ Weam authentication integrated
✅ Database collections follow naming convention
✅ Base path configuration set
✅ Sidebar entry added
✅ NGINX routing configured
✅ Docker deployment ready
✅ Environment variables updated
Testing Integration
- Authentication Test: Verify users can access the app through Weam login
- Database Test: Confirm data is stored with proper user/company scoping
- Routing Test: Check that all routes work under
/app-builder base path
- Generation Test: Verify AI spec parsing works in production
- Export Test: Confirm ZIP and Git export functionality works
- UI Test: Ensure theming matches Weam's design system
Your AI Full-Stack App Builder is now fully integrated into the Weam ecosystem while maintaining all its standalone functionality!
AI Full-Stack App Builder – Complete Build and Integration Guide
Part 1: Building the Standalone AI Full-Stack App Builder
What You're Building
The AI Full-Stack App Builder is a web application that transforms natural language descriptions into complete, runnable web applications. Users describe what they want to build (e.g., "A project management app with users, projects, and tasks"), and the system generates database schemas, REST APIs, and frontend pages. The app includes a visual schema editor, code preview, and export functionality for generated projects.
Core Features
Tech Stack
Frontend:
Backend:
Development Tools:
Project Structure
Data Models
API Endpoints
Core Components
1. Specification Input Component
2. Schema Editor Component
3. AI Specification Parser
4. Code Generators
5. Project Builder and Exporter
Main Application Page
Environment Configuration
# .env.local MONGODB_URI=mongodb://localhost:27017/app_builder OPENAI_API_KEY=your_openai_api_key_here GITHUB_TOKEN=your_github_token_here NEXTAUTH_SECRET=your_nextauth_secret_here NEXTAUTH_URL=http://localhost:3000Package Dependencies
{ "dependencies": { "next": "14.0.0", "react": "18.2.0", "react-dom": "18.2.0", "typescript": "5.0.0", "tailwindcss": "3.3.0", "openai": "4.0.0", "jszip": "3.10.0", "mongodb": "6.0.0", "react-flow-renderer": "10.3.0", "@monaco-editor/react": "4.6.0", "react-hook-form": "7.45.0", "@hookform/resolvers": "3.3.0", "zod": "3.22.0" }, "devDependencies": { "@types/node": "20.0.0", "@types/react": "18.2.0", "@types/react-dom": "18.2.0", "eslint": "8.50.0", "eslint-config-next": "14.0.0", "prettier": "3.0.0", "jest": "29.7.0", "@testing-library/react": "13.4.0" } }Part 2: Now that your app is working fine, let's integrate it with Weam
Integration Overview
Once your AI Full-Stack App Builder is fully functional as a standalone application, you can integrate it into the Weam ecosystem. This integration involves:
Weam Integration Steps
Step 1: Authentication Integration
Replace your standalone authentication with Weam's iron-session:
Step 2: Database Migration
Update your database collections to follow Weam's naming convention:
Step 3: Base Path Configuration
Update your Next.js configuration for Weam routing:
Step 4: Environment Variables
Update your environment configuration:
# .env.local NEXT_PUBLIC_API_BASE_PATH=/app-builder MONGODB_URI=mongodb://localhost:27017/weam IRON_SESSION_PASSWORD=your-secure-session-password COOKIE_DOMAIN=.weam.ai OPENAI_API_KEY=your-openai-key GITHUB_TOKEN=your-github-tokenStep 5: Sidebar Integration
Add your solution to Weam's sidebar:
Step 6: NGINX Configuration
Add routing configuration to Weam's NGINX:
Step 7: Docker Configuration
Create Docker setup for deployment:
Final Integration Checklist
✅ Standalone app fully functional
✅ Weam authentication integrated
✅ Database collections follow naming convention
✅ Base path configuration set
✅ Sidebar entry added
✅ NGINX routing configured
✅ Docker deployment ready
✅ Environment variables updated
Testing Integration
/app-builderbase pathYour AI Full-Stack App Builder is now fully integrated into the Weam ecosystem while maintaining all its standalone functionality!