Advanced Guides
Using Next.js Server Actions in Imaginary Programming
Learn how to install imaginary-dev
into a project that uses Server Actions in Next.js.
Using Next.js Server Actions in Imaginary Programming
The talk of the React world for the last few weeks has been React Server Components and the release in early May of Next.js Server Actions. We were excited to try them with Imaginary Programming, and what we found blew our socks off.
What’s the problem with using Imaginary Programming (or the GPT API) in a website?
Let’s start with reviewing a pretty basic problem with using OpenAI’s GPT APIs from website user interfaces. You might naively think that we can embed the openai
library into our web code and use it directly in the browser. After all, that’s how we use a bunch of other web APIs. But when you read the OpenAI API documentation, you come across the following:
“Remember that your API key is a secret! Do not share it with others or
expose it in any client-side code (browsers, apps). Production requests
must be routed through your own backend server where your API key can be
securely loaded from an environment variable or key management service.”
In short: if you use OpenAI’s APIs directly from the browser, your API key is liable to be stolen, and you are likely to be on the hook for a heap of extra API usage charges. (Note that there is one small exception to this rule, which is that if you provide a “bring your own key” service where you ask users to enter their own OpenAI API key, you can make direct calls to OpenAI, because no one is worried about stealing their own key.)
For Imaginary Programming, which is designed to make using OpenAI as easy as calling a function, this was a bit of a bummer. What should be a simple function call in web code became a backend implementation plus an HTTP network call. We did our best to hide this complexity by creating some wrapping functions in our @imaginary-dev/nextjs-util
package that make it look like you are directly calling the imaginary function in client code. While this works and is quite cool, it still involved more boilerplate than we were comfortable with.
Server Actions to the Rescue
Enter Server Actions, which are an alpha feature in Next.js 13.4 and a simple way for front-end React apps (or, as we are now apparently calling them, “client components”) to call server-side functions. Most of the tutorials and documentation focus on the use of Server Actions for mutating data and revalidating the browser, but Server Actions can also be used to create standard REST data queries. To create a Next.js Server Action that performs a query, all you need to do is (a) add the “use server” directive at either the function level or file level, (b) return some JSON from the function, and (c) call the function from a client component. All of the network call plumbing is dealt with automatically. Luckily, this is a great fit for Imaginary Programming.
So, to add Imaginary Programming as a Server Action in Next.js 13.4+, you’ll need to declare your imaginary function in a separate file with the “use server” directive at the top. Next, import and call the function as you would with any other async function from your client component. Next.js will handle provisioning the API endpoint, the encoding and decoding of the return value over the wire, and the client network fetch. From the user of Imaginary Programming’s perspective, it just looks like any old function call, which was our intention all along with Imaginary Programming.
// /src/classify-email.ts
“use server”
/**
* This function takes in email text and classifies it as “angry” or “happy”.
* @params emailText the email text to classify
* @returns the string “angry” or “happy”
* @imaginary
*/
export declare function classifyEmail(emailText:string): Promise<”angry” | “happy”>
// /src/pages/email/page.ts
import { classifyEmail } from “../../classify-email”
export default function pageComponent() {
return <button onClick={async () => alert(`the email is ${await classifyEmail(“I hate you!”)}`)}/>
}
To see this in practice, check out our Next.js imaginary programming template; it’s about as simple as it can possibly be. All you do is add “use server”
to the top of a file, declare what a function should do in a comment and TypeScript function declaration (don’t forget the @imaginary
tag!), and then call it from your client code the way you would call any other async function. Imaginary magic, directly built in to Next.js, at your fingertips!