A full-stack template for building fast GPT-3 apps.
Note: This post is historical and describes Edge Functions as they existed at the time. For new projects, use Vercel Functions with the Node.js runtime and Fluid compute.
The field of artificial intelligence continues to take the world by storm. Huge strides have been made in text and image generation through tools like ChatGPT, GPT-3, DALL-E, and Stable Diffusion. It’s spawned a wave of exciting AI startups, many of which we’re seeing built with Vercel and Next.js.
One of the most exciting developments in the AI space is GPT-3, a cutting-edge natural language processing model developed by OpenAI. With its ability to understand and generate human-like text, GPT-3 has the potential to disrupt how we perform many of our tasks.
This historical walkthrough uses twitterbio.com to show how streaming changed the user experience for GPT-3 apps. For new projects, build this pattern with Vercel Functions, Fluid compute, and streaming responses.
Copy link to headingThe frontend
The Next.js frontend consists of a few elements:
A text box for users to copy their current bio or write a few sentences about themselves
A dropdown where they can select the tone of the bio they want to be generated
A submit button for generating their bio, which when clicked calls an API route that uses OpenAI’s GPT-3 model and returns two generated bios
Two containers to display the generated bios after we get them back from the API route
Here’s what the code for our index page looks like. We have a few pieces of state that correspond to the elements mentioned above. We’re also defining a prompt—like ChatGPT, we need to send a prompt to GPT-3 to instruct it to generate the new bios. Finally, we ask GPT-3 to generate two bios clearly labeled (so we can parse them correctly) using the user-provided bio and vibe as context.
const prompt = `Generate 2 ${vibe} twitter bios with no hashtags and clearly labeled "1." and "2.". Make sure each generated bio is at least 14 words and at max 20 words and base them on this context: ${bio}`;The rest of our index page is comprised of the UI elements themselves: our text box, dropdown, submit button, and two containers on the bottom that we display when we get the generated bios. There's also some loading logic for the button to show a loading indicator when clicked.
<textarea value={bio} onChange={(e) => setBio(e.target.value)} rows={4} className="..." placeholder={"e.g. Senior Engineer @vercel. Tweeting about web dev & AI."}/><div className="..."> <Image src="/2-black.png" width={30} height={30} alt="1 icon" /> <p className="...">Select your vibe.</p></div><div className="block"> <DropDown vibe={vibe} setVibe={(newVibe) => setVibe(newVibe)} /></div><button className="..." onClick={(e) => generateBio(e)}> Generate your bio →</button><hr className="..." /><div className="..."> {generatedBios && ( <> <div> <h2 className="...">Your generated bios</h2> </div> <div className="..."> {generatedBios .substring(generatedBios.indexOf("1") + 3) .split("2.") .map((generatedBio: any) => { return ( <div className="..." key={generatedBio}> <p>{generatedBio}</p> </div> ); })} </div> </> )}</div>In addition to the UI elements and the loading logic, we have a generateBio function that’s called when the user clicks the submit button. This sends a POST request to our /api/generate API route with the prompt in the body.
const generateBio = async (e: any) => { e.preventDefault(); setGeneratedBios(""); setLoading(true); const response = await fetch("/api/generate", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ prompt, }), }); if (!response.ok) { throw new Error(response.statusText); } let answer = await response.json(); setGeneratedBios(answer.choices[0].text); setLoading(false);};We get the generated bios back from the API route, save it to the generatedBios state, then display it to the user. Because we asked GPT-3 to return the text in a specific numbered format, we can split it based on the “2.” to show the user the two bios separated nicely into containers as seen below.
Copy link to headingThe backend
A great advantage of using Next.js is being able to handle both our frontend and backend in a single application. We can spin up an API route just by creating a file called generate.ts in our api folder. Let’s take a look at our /api/generate API Route.
We get the prompt from the request body that’s passed in on the frontend, then construct a payload to send to OpenAI. In this payload, we specify some important information like the exact model (GPT-3) and how many tokens we want OpenAI to respond with (a token is approximately 4 characters). In this case, we’re limiting the max tokens because Twitter bios have a character constraint.
After the payload is constructed, we send it in a POST request to OpenAI, await the result to get back the generated bios, then send them back to the client as JSON.
export default async function handler(req, res) { const { prompt } = req.body;
const payload = { model: "text-davinci-003", prompt, temperature: 0.7, top_p: 1, frequency_penalty: 0, presence_penalty: 0, max_tokens: 200, n: 1, };
const response = await fetch("https://api.openai.com/v1/completions", { headers: { "Content-Type": "application/json", Authorization: `Bearer ${process.env.OPENAI_API_KEY ?? ""}`, }, method: "POST", body: JSON.stringify(payload), });
const json = await response.json(); res.status(200).json(json);}There we have it! We built the first version of our application. Feel free to check out the code and demo for this approach.
Copy link to headingCurrent guidance: use Vercel Functions with streaming
The original version of this tutorial migrated the example to Edge Functions to get streaming and avoid timeouts. For new AI applications on Vercel, use Vercel Functions with the default Node.js runtime and Fluid compute. This gives you streaming support, longer durations, and full Node.js API compatibility.
If you are rebuilding this example today:
Keep the API route on the Node.js runtime.
Stream model output from the route to the client.
Use Fluid compute for long-running model calls and efficient concurrency.
Choose a function region close to the upstream AI provider when latency matters.
See Vercel Functions and Streaming Functions for current implementation guidance.
Copy link to headingResources
We hope this walkthrough helps you build incredible GPT-3 powered applications. We’ve already seen several sites built with this template such as Rephraser, GenzTranslator, and ChefGPT—some of which have thousands of users. Visit the Twitter Bio site to see everything we talked about in action, check out our other AI templates, or start optimizing prompts across various models with Vercel's AI Playground.