Course

Building A Server Action: Create a Product

Interacting with the Database

In this lesson, we will learn how to interact with the database using the Prisma Client. We will create a new server action that will allow us to create a new Product in the database.

Basic Principles

Just like with any interactive web application- we build our actions out following the basic principles of CRUD (Create, Read, Update, Delete).

: Add new data to the database

: Retrieve data from the database

: Modify existing data in the database

: Remove data from the database

If we start with these basic principles in mind, we can build out our server actions to interact with the database in a meaningful way.

Creating a New Server Action - Create a Product

Let's start by creating a new server action that will allow us to create a new Product in the database. We will create a new file called products.ts in the directory.

// import our generated Prisma Client
import { prisma } from "@/lib/prisma";

products.ts;
("use server");

export async function createProduct() {
  // TODO: create a new Product in the database
}

To flesh out the createProduct function, we will use the Prisma Client to create a new Product in the database. We will use the method provided by the Prisma Client to create a new Product. Before we can create a new Product, we need to determine what data we need to provide to create a new Product. In this case, we need to provide the following data:

  • name
  • description
  • price
  • category

How do we know what data we need to provide to create a new Product? We can refer to the Prisma schema to determine what fields are required to create a new Product. Let's take a close look at the Prisma schema to determine what fields are required to create a new Product.

schema.prisma
model Product {
  id          Int      @id @default(autoincrement())
  name        String
  price       Float
  category    String
  description String
  images      Image[]
  reviews     Review[]
}

Any fields that aren't either marked as optional or have a default value are required. In this case, the , , , and fields are required to create a new Product. The remaining fields ( and ) are optional, because they have a default value of an empty array.

Meaning the function needs to take in an argument with a type like this:

interface CreateProductInput {
  name: string;
  description: string;
  price: number;
  category: string;
}

export async function createProduct(input: CreateProductInput) {
  // create a new Product in the database
}

Now that we know what data we need to provide to create a new Product, we can use the Prisma Client to create a new Product in the database.

monkey
// import our generated Prisma Client
import { prisma } from "@/lib/prisma";

products.ts;
("use server");

interface CreateProductInput {
  name: string;
  description: string;
  price: number;
  category: string;
}

export async function createProduct(input: CreateProductInput) {
  // create a new Product in the database
  const newProduct = await prisma.product.create({
    data: {
      name: input.name,
      description: input.description,
      price: input.price,
    },
  });

  return newProduct;
}
Insight

We use to access the Product model in the Prisma schema. Because we defined a model called in the Prisma schema, the Prisma Client automatically generates a property called that we can use to interact with the Product model in the database.


We use the method to create a new Product in the database. The method takes an object as an argument with a property that contains the data we want to create a new Product with.

Integrating the Server Action

Now that we have created a new server action to create a new Product in the database, we can integrate this server action into our application.

We can find the form used to create a new Product in the file. We can import the function from the file and use it to create a new Product in the database whenever the form is submitted.

Currently the submit handler for the form looks like this:

//
const handleSubmit = (e: any) => {
  e.preventDefault();
  console.log({ name, category, images, description, price });
};

Let's import the function and use it to create a new Product in the database whenever the form is submitted.

//
import { createProduct } from "@/lib/actions/products";

...

const handleSubmit = async (e: any) => {
  e.preventDefault();
  await createProduct({ name, category, description, price });
};

Here, we're calling the function with an object containing the , , and fields from the form. This will create a new Product in the database with the provided data.

Handling Image URLs

The logic here will be a little different. Our form will send us an array of image URLs, but we'll need to create a new Image record for each image URL and associate it with the newly created Product- we aren't just storing the URLs in the Product record directly.

Luckily- we can use the method provided by the Prisma Client to create a new Image record for each image URL and associate it with the newly created Product in one step. The syntax looks something like this:

//
const newProduct = await prisma.product.create({
  data: {
    name: product.name,
    description: product.description,
    price: product.price,
    category: product.category,
    images: {
      create: [
        // the array of image URLs
      ],
    },
  },
});

We can update our function to accept an array of image URLs and create a new Image record for each image URL and associate it with the newly created Product.

monkey
//
interface CreateProductInput {
  name: string;
  description: string;
  price: number;
  category: string;
  // update our type to accept an array of image URLs
  images?: string[];
}
export async function createProduct(product: CreateProductInput) {
  const newProduct = await prisma.product.create({
    data: {
      name: product.name,
      description: product.description,
      price: product.price,
      category: product.category,
      images: {
        // create a new Image record for each image URL
        create: product.images?.map((url) => ({ url })),
      },
    },
  });
  return newProduct;
}
Insight

This is a powerful feature of Prisma- we can create related records in one step. This is a great way to ensure data integrity and consistency in our database.

This pattern is called and is a common pattern in Prisma.

Error Handling

When working with databases, it's important to handle errors that may occur during database operations. We can use blocks to catch any errors that may occur during database operations and handle them appropriately.

//
export async function createProduct(product: CreateProductInput) {
  try {
    const newProduct = await prisma.product.create({
      data: {
        name: product.name,
        description: product.description,
        price: product.price,
        category: product.category,
        images: {
          create: product.images?.map((url) => ({ url })),
        },
      },
    });
    return newProduct;
  } catch (error) {
    console.error("Error creating product:", error);
    throw new Error("Error creating product");
  }
}

Then we can handle the error in the component where we call the function.

//
const handleSubmit = async (e: FormEvent) => {
  e.preventDefault();
  try {
    await createProduct({ name, category, description, price, images });
  } catch (error) {
    // show some toast or alert to the user
    console.error("Error creating product:", error);
  }
};

Conclusion

In this lesson, we learned how to interact with the database using the Prisma Client. We created a new server action that allows us to create a new Product in the database. We used the method provided by the Prisma Client to create a new Product in the database. We also learned how to handle errors that may occur during database operations.

0 Comments

"Please login to view comments"

glass-bbok

Join the Conversation!

Subscribing gives you access to the comments so you can share your ideas, ask questions, and connect with others.

Upgrade your account
tick-guideNext Lesson

Building A Server Action: Read a Product