# Web app tutorial  Estimated time: 10 minutes In this tutorial you will build an app that creates and displays custom Gadgémon. Custom sprites for your Gadgémon will be generated using OpenAI's DALL-E API and Gadget's built-in OpenAI connection. ## What you will learn  After finishing this tutorial, you will know how to: * Add a custom * Construct an and interact with the CRUD API * Build a React ## Step 1: Create a new Gadget app  Every Gadget app includes a hosted Postgres database, a serverless Node backend, and a Vite + React frontend. 1. Start by **creating a new Gadget app** at [https://gadget.new](https://gadget.new) 2. Select the **Web** app type. Make sure to use the **Yes, enable auth** option 3. Click the **Continue** button 4. Enter an app name and create the app ## Step 2: Build your model  **Models** in Gadget work similarly to models in an ORM (object relational mapper) and map to tables in a database. They have one or more **fields** which are columns in your tables and are used to store your application data. Create a new model to store your Gadgémon: 1. Click the **+** button next to `api/models` to create a new model 2. Name the model `gadgemon` and add the following fields: * `name`: a string field * `similar`: a string field * `element`: an enum field with options `grass`, `fire`, and `water` * `sprite`: a file field 3. Add a **Required** validation to `name`, `similar`, and `element`. The action needs all three values to generate a sprite. To learn more about models, see the [models documentation](https://docs.gadget.dev/guides/models). ## Step 3: Add the OpenAI plugin  Gadget has built-in plugins you can use to connect your app with external services, such as OpenAI. You can add the OpenAI plugin to your app by following these steps: 1. Click on **Settings** in the sidebar 2. Click on **Plugins** 3. Select **OpenAI** from the list of plugins 4. Leave the default **Gadget development keys** selected and click **Add connection** You can now use the [OpenAI connection](https://docs.gadget.dev/guides/plugins/openai) in your app to generate sprites for your Gadgémon. To learn more about plugins check out the [plugins documentation](https://docs.gadget.dev/guides/plugins). ## Step 4: Write your backend action  As you build models in Gadget, a CRUD (create, read, update, delete) API is automatically generated. You can see these **actions** and the code that powers them in the `api/models/gadgemon/actions` folder. Now add code to your `gadgemon.create` action that generates a sprite for your Gadgémon using the OpenAI plugin: 1. Go to `api/model/gadgemon/actions/create.js` 2. Paste the following code (replace the entire file): ```typescript import { applyParams, save, ActionOptions } from "gadget-server"; import { preventCrossUserDataAccess } from "gadget-server/auth"; export const run: ActionRun = async ({ params, record, logger, api }) => { applyParams(params, record); await preventCrossUserDataAccess(params, record); await save(record); }; export const onSuccess: ActionOnSuccess = async ({ params, record, logger, api, connections, }) => { // "record" is the newly created Gadgemon, with name, similar and element fields that will be added by the user const { id, name, similar, element } = record; // prompt sent to OpenAI to generate the Gadgemon sprite const prompt = `A pixel art style pokemon sprite named ${name} that looks similar to a ${similar} that is a ${element} element. Do not include any text, including the name, in the image`; // call the OpenAI images generate (DALL-E) API: https://github.com/openai/openai-node/blob/v4/src/resources/images.ts const response = await connections.openai.images.generate({ prompt, n: 1, size: "256x256", response_format: "url", }); const imageUrl = response.data?.[0]?.url; // write to the Gadget Logs logger.info({ imageUrl }, `Generated image URL for Gadgemon id ${id}`); // save the image file to the newly created Gadgémon record await api.gadgemon.update(id, { gadgemon: { sprite: { copyURL: imageUrl, }, }, }); }; export const options: ActionOptions = { actionType: "create", timeoutMS: 60000, }; ``` This code will run every time your `gadgemon.create` API action is called. ### Test your action  Gadget apps include an API playground that can be used to test your actions. Use the playground to run your code in the `gadgemon.create` action: 1. While in `api/models/gadgemon/actions/create.js`, click the **Run Action** button. 2. Use the playground's API client to create a Gadgémon: ```typescript await api.gadgemon.create({ name: "Gadgetbot", similar: "robot", element: "grass", user: { _link: "1", }, }); ``` 3. Click the execute query button to run the action You should see a `success: true` response in the playground, which means you created a new `gadgemon` **record**. 4. Go to `api/models/gadgemon/data` to view your model records Actions define your application's API. To learn more about actions, see the [actions guide](https://docs.gadget.dev/guides/actions). ## Step 5: Build your frontend  You need an interface to create and display your Gadgémon. In Gadget, your Vite + React frontend is found inside the `web` folder, and an API client has been set up for you in `web/api.js`. You will use this client and Gadget's React hooks to call your `gadgemon.create` action and read `gadgemon` model records. 1. Go to `web/routes/_app.signed-in.jsx` and replace the contents with the following code: ```tsx import { Card, CardContent } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { api } from "../api"; import { useFindMany } from "@gadgetinc/react"; import { AutoForm } from "@/components/auto"; const elementColors: Record = { grass: "bg-green-100 text-green-800", fire: "bg-red-100 text-red-800", water: "bg-blue-100 text-blue-800", }; export default function () { const [{ data: gadgemon, error, fetching }] = useFindMany(api.gadgemon, { select: { id: true, name: true, sprite: { url: true }, element: true, similar: true, }, live: true, }); return (

Create a Gadgemon

Gadgedex

{error && ( Failed to load Gadgemon: {error.message} )} {fetching && !gadgemon &&

Loading...

} {!fetching && !error && gadgemon?.length === 0 &&

No Gadgemon yet. Create one above!

} {gadgemon && gadgemon.length > 0 && (
{gadgemon.map(({ id, name, sprite, element, similar }) => (
{sprite?.url ? {name : "🎭"}

{name || "Unnamed Gadgemon"}

{element}

{element} {similar} type

))}
)}
); } ``` Like your database and backend, your Gadget frontends are already hosted and live on the internet. For more information on frontends, read the [frontend docs](https://docs.gadget.dev/guides/frontend). ### Test your app  Now you can view your completed app (and your Gadgémon!) in the frontend: 1. Click **Preview** in the top right of the Gadget editor 2. **Sign up** and **sign in** to your app You should see your Gadgémon displayed on the page. Try creating new Gadgémon using the form! ## Next steps  Dig deeper into the concepts used in this tutorial: * [Models](https://docs.gadget.dev/guides/models) * [Actions](https://docs.gadget.dev/guides/actions) * [Frontend](https://docs.gadget.dev/guides/frontend) Learn about development practices and tooling: * [Environments](https://docs.gadget.dev/guides/environments) * [Source control](https://docs.gadget.dev/guides/source-control) * [Local development with the Gadget CLI](https://docs.gadget.dev/guides/development-tools/cli) * [Deploying your app](https://docs.gadget.dev/guides/environments/deployment) * [Building with AI agents](https://docs.gadget.dev/guides/development-tools/working-with-agents) ### Questions?  Reach out on Gadget's [Discord](https://ggt.link/discord) server to talk with Gadget employees and the Gadget developer community!