Tutorials
8 min read
By Gavin Elliott

Building Your First AI App: A Complete Tutorial

Learn how to build and deploy your first AI application from scratch using Next.js and OpenAI's GPT API. Complete with code examples and deployment guide.

#Tutorial#Next.js#OpenAI#GPT API#React#Development#Beginner

Building Your First AI App: A Complete Tutorial

Ever wanted to build your own AI application but didn't know where to start? This tutorial will walk you through creating a simple AI writing assistant from scratch.

What we'll build: A clean, modern AI writing assistant that helps users improve their writing with suggestions, tone adjustments, and grammar corrections.

Tech stack: Next.js, React, OpenAI API, Tailwind CSS

Time required: 2-3 hours

Prerequisites: Basic knowledge of JavaScript and React

What You'll Learn

By the end of this tutorial, you'll have:

  • A fully functional AI writing assistant
  • Understanding of OpenAI API integration
  • Knowledge of AI app architecture patterns
  • A deployed application you can share

Let's dive in!

Step 1: Project Setup

First, let's create a new Next.js project:

npx create-next-app@latest ai-writing-assistant
cd ai-writing-assistant
npm install openai

For styling, we'll use Tailwind CSS (it should already be included in the Next.js setup).

Step 2: Environment Configuration

Create a .env.local file in your project root:

OPENAI_API_KEY=your_openai_api_key_here
NEXT_PUBLIC_APP_URL=http://localhost:3000

Important: Never commit your API key to version control. Get your API key from OpenAI's dashboard.

Step 3: Setting Up the OpenAI Client

Create lib/openai.js:

import OpenAI from 'openai';

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
});

export default openai;

Step 4: Creating the API Route

Create pages/api/improve-writing.js:

import openai from '../../lib/openai';

export default async function handler(req, res) {
  if (req.method !== 'POST') {
    return res.status(405).json({ message: 'Method not allowed' });
  }

  const { text, mode } = req.body;

  if (!text) {
    return res.status(400).json({ message: 'Text is required' });
  }

  try {
    let prompt;
    
    switch (mode) {
      case 'improve':
        prompt = `Improve the following text while maintaining its original meaning and tone. Make it clearer, more engaging, and better structured:\n\n${text}`;
        break;
      case 'formal':
        prompt = `Rewrite the following text in a more formal, professional tone:\n\n${text}`;
        break;
      case 'casual':
        prompt = `Rewrite the following text in a more casual, friendly tone:\n\n${text}`;
        break;
      case 'grammar':
        prompt = `Fix any grammar, spelling, and punctuation errors in the following text:\n\n${text}`;
        break;
      default:
        prompt = `Improve the following text:\n\n${text}`;
    }

    const completion = await openai.chat.completions.create({
      model: "gpt-3.5-turbo",
      messages: [
        {
          role: "system",
          content: "You are a helpful writing assistant. Provide clear, improved versions of text while maintaining the original intent."
        },
        {
          role: "user",
          content: prompt
        }
      ],
      max_tokens: 1000,
      temperature: 0.7,
    });

    const improvedText = completion.choices[0].message.content;

    res.status(200).json({ 
      improvedText,
      originalText: text,
      mode 
    });

  } catch (error) {
    console.error('OpenAI API error:', error);
    res.status(500).json({ 
      message: 'Error processing your request',
      error: error.message 
    });
  }
}

Step 5: Building the Frontend

Replace the content of pages/index.js:

import { useState } from 'react';
import Head from 'next/head';

export default function Home() {
  const [text, setText] = useState('');
  const [improvedText, setImprovedText] = useState('');
  const [mode, setMode] = useState('improve');
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState('');

  const handleSubmit = async (e) => {
    e.preventDefault();
    
    if (!text.trim()) {
      setError('Please enter some text to improve');
      return;
    }

    setLoading(true);
    setError('');

    try {
      const response = await fetch('/api/improve-writing', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ text, mode }),
      });

      if (!response.ok) {
        throw new Error('Failed to improve text');
      }

      const data = await response.json();
      setImprovedText(data.improvedText);
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };

  const copyToClipboard = async () => {
    try {
      await navigator.clipboard.writeText(improvedText);
      alert('Text copied to clipboard!');
    } catch (err) {
      console.error('Failed to copy text:', err);
    }
  };

  return (
    <div className="min-h-screen bg-gray-50">
      <Head>
        <title>AI Writing Assistant</title>
        <meta name="description" content="Improve your writing with AI" />
      </Head>

      <div className="container mx-auto px-4 py-8">
        <header className="text-center mb-8">
          <h1 className="text-4xl font-bold text-gray-900 mb-2">
            AI Writing Assistant
          </h1>
          <p className="text-gray-600">
            Improve your writing with the power of AI
          </p>
        </header>

        <div className="max-w-4xl mx-auto">
          <form onSubmit={handleSubmit} className="space-y-6">
            {/* Mode Selection */}
            <div>
              <label className="block text-sm font-medium text-gray-700 mb-2">
                Choose improvement mode:
              </label>
              <select
                value={mode}
                onChange={(e) => setMode(e.target.value)}
                className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
              >
                <option value="improve">General Improvement</option>
                <option value="formal">Make More Formal</option>
                <option value="casual">Make More Casual</option>
                <option value="grammar">Fix Grammar</option>
              </select>
            </div>

            {/* Text Input */}
            <div>
              <label className="block text-sm font-medium text-gray-700 mb-2">
                Enter your text:
              </label>
              <textarea
                value={text}
                onChange={(e) => setText(e.target.value)}
                placeholder="Paste your text here..."
                rows={8}
                className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
              />
            </div>

            {/* Submit Button */}
            <button
              type="submit"
              disabled={loading}
              className="w-full bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors"
            >
              {loading ? 'Improving...' : 'Improve Text'}
            </button>
          </form>

          {/* Error Message */}
          {error && (
            <div className="mt-4 p-4 bg-red-100 border border-red-400 text-red-700 rounded-md">
              {error}
            </div>
          )}

          {/* Results */}
          {improvedText && (
            <div className="mt-8 space-y-4">
              <div className="bg-white p-6 rounded-lg shadow-md">
                <div className="flex justify-between items-center mb-4">
                  <h3 className="text-lg font-semibold text-gray-900">
                    Improved Text
                  </h3>
                  <button
                    onClick={copyToClipboard}
                    className="bg-green-600 text-white px-4 py-2 rounded-md hover:bg-green-700 transition-colors"
                  >
                    Copy to Clipboard
                  </button>
                </div>
                <div className="prose max-w-none">
                  <p className="text-gray-800 leading-relaxed">
                    {improvedText}
                  </p>
                </div>
              </div>

              {/* Original Text for Comparison */}
              <div className="bg-gray-100 p-6 rounded-lg">
                <h3 className="text-lg font-semibold text-gray-900 mb-4">
                  Original Text
                </h3>
                <p className="text-gray-700 leading-relaxed">
                  {text}
                </p>
              </div>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

Step 6: Testing Your Application

Run your development server:

npm run dev

Open http://localhost:3000 and test your AI writing assistant!

Step 7: Adding Error Handling and Rate Limiting

For production applications, you'll want to add proper error handling and rate limiting:

// lib/rateLimit.js
import { LRUCache } from 'lru-cache';

type Options = {
  uniqueTokenPerInterval?: number;
  interval?: number;
};

export default function rateLimit(options: Options) {
  const tokenCache = new LRUCache({
    max: options.uniqueTokenPerInterval || 500,
    maxAge: options.interval || 60000,
  });

  return {
    check: (key: string, limit: number) =>
      new Promise<void>((resolve, reject) => {
        const tokenCount = (tokenCache.get(key) as number[]) || [0];
        if (tokenCount[0] === 0) {
          tokenCache.set(key, tokenCount);
        }
        tokenCount[0] += 1;

        const currentUsage = tokenCount[0];
        const isRateLimited = currentUsage >= limit;

        if (isRateLimited) {
          reject(new Error('Rate limit exceeded'));
        } else {
          resolve();
        }
      }),
  };
}

Step 8: Deployment

Deploy to Vercel (Recommended)

  1. Push your code to GitHub
  2. Connect your GitHub repo to Vercel
  3. Add your environment variables in Vercel dashboard
  4. Deploy!

Deploy to Netlify

  1. Build your app: npm run build
  2. Deploy the out directory to Netlify
  3. Add environment variables in Netlify dashboard

Step 9: Enhancements and Next Steps

Now that you have a basic AI writing assistant, here are some enhancements you can add:

Advanced Features

  • Character count and word count display
  • Writing style analysis
  • Tone detection and adjustment
  • Multiple language support

User Experience Improvements

  • Dark mode toggle
  • Writing history
  • Export to different formats
  • Real-time suggestions

Business Features

  • User authentication
  • Usage tracking and limits
  • Subscription plans
  • API analytics

Code Examples for Common Enhancements

Adding Word Count

const WordCount = ({ text }) => {
  const words = text.trim().split(/\s+/).length;
  const characters = text.length;
  
  return (
    <div className="text-sm text-gray-500">
      {words} words, {characters} characters
    </div>
  );
};

Adding Dark Mode

const [darkMode, setDarkMode] = useState(false);

const toggleDarkMode = () => {
  setDarkMode(!darkMode);
  document.documentElement.classList.toggle('dark');
};

Adding User Authentication

// Using NextAuth.js
import { useSession, signIn, signOut } from 'next-auth/react';

const AuthButton = () => {
  const { data: session } = useSession();
  
  if (session) {
    return (
      <button onClick={() => signOut()}>
        Sign out {session.user.email}
      </button>
    );
  }
  
  return (
    <button onClick={() => signIn()}>
      Sign in
    </button>
  );
};

Performance Optimization Tips

API Optimization

  • Cache common requests
  • Implement request debouncing
  • Use streaming for long responses
  • Optimize prompt engineering

Frontend Optimization

  • Use React.memo for expensive components
  • Implement lazy loading
  • Optimize images and assets
  • Use service workers for offline functionality

Common Pitfalls and Solutions

API Rate Limits

  • Implement client-side rate limiting
  • Show clear error messages
  • Provide usage statistics to users

High API Costs

  • Set usage limits per user
  • Implement caching strategies
  • Use cheaper models when possible

Poor User Experience

  • Add loading indicators
  • Implement error boundaries
  • Provide clear feedback

Security Considerations

API Key Security

  • Never expose API keys in client-side code
  • Use environment variables
  • Implement server-side validation

Input Validation

  • Sanitize user inputs
  • Implement rate limiting
  • Add content filtering

Conclusion

Congratulations! You've built your first AI application. This tutorial covered:

  • Setting up a Next.js project with OpenAI integration
  • Creating API routes for AI functionality
  • Building a responsive frontend
  • Implementing error handling
  • Deployment strategies

Your next steps:

  1. Add more features from the enhancement list
  2. Deploy to production and get user feedback
  3. Monitor usage and optimize performance
  4. Consider monetization strategies

Remember: The best AI apps solve real problems for real people. Focus on user value, not just cool AI features.

What will you build next?


Want to see more AI app tutorials? Check out our complete development guides for different types of AI applications.

Browse More Tutorials → Get App Ideas →

Found this helpful?

Share it with others who might benefit.

Stay Updated

Get more insights like this delivered to your inbox. No spam, just valuable content.