Pada tutorial ini kita akan membahas tentang pengembangan aplikasi Ecommerce Cart menggunakan Next.js 13 dengan menggunakan Typescript. Dalam tutorial ini, kamu akan belajar bagaimana mengintegrasikan beberapa teknologi seperti Payment Gateway, Ant Design, Redux Toolkit, Next Auth, Postgre SQL, dan Prisma.
Step 1: Persiapkan Database
Database yang akan digunakan adalah PostgeSQL yang merupakan salah satu dabatase SQL terpopuler dan powerfull. Kamu dapat mendaftar pada website Supabase untuk mendapatkan cloud database service PostgeSQL secara gratis.
![Supabase Website](/_next/image?url=%2Fstatic%2Fimages%2Ftutorial%2Fpart-1-tutorial-nextjs-13-typescript-ecommerce-cart-dengan-payment-gateway-antd-supabase-redux-toolkit-next-auth-postgre-sql-prisma%2Fsupabase-landing.png&w=2048&q=100)
Setelah berhasil membuat akun, buatlah 1 project baru dengan nama ecommerce-tahu-coding, lalu generate password dan pilih region Singapore agar tidak lemot.
![Supabase Create New Project](/_next/image?url=%2Fstatic%2Fimages%2Ftutorial%2Fpart-1-tutorial-nextjs-13-typescript-ecommerce-cart-dengan-payment-gateway-antd-supabase-redux-toolkit-next-auth-postgre-sql-prisma%2Fsupabase-create-new-project.png&w=2048&q=100)
Jika proses create database sudah selesai, navigasi menuju setting - database lalu pada bagian connection string pilih tab URI, simpan url berikut dan jangan lupa tambahkan password yang sudah digenerate tadi pada url.
![Supabase Database Setting](/_next/image?url=%2Fstatic%2Fimages%2Ftutorial%2Fpart-1-tutorial-nextjs-13-typescript-ecommerce-cart-dengan-payment-gateway-antd-supabase-redux-toolkit-next-auth-postgre-sql-prisma%2Fsupabase-db-setting.png&w=2048&q=100)
Berikut contoh url string database:
postgresql://postgres:[YOUR-PASSWORD]@db.your-url.supabase.co:5432/postgres
postgresql://postgres:[YOUR-PASSWORD]@db.your-url.supabase.co:5432/postgres
Step 2: Install Next.js 13
Kita akan menggunakan salah satu framework fullstack based on React.js, yaitu Next.js. Jalankan command berikut pada terminal untuk memulai:
npx create-next-app@latest
npx create-next-app@latest
Jangan lupa setup seperti berikut:
![Next.js Setup](/_next/image?url=%2Fstatic%2Fimages%2Ftutorial%2Fpart-1-tutorial-nextjs-13-typescript-ecommerce-cart-dengan-payment-gateway-antd-supabase-redux-toolkit-next-auth-postgre-sql-prisma%2Fnextjs-setup.png&w=2048&q=100)
Setelah selesai setup Next.js jalankan project menggunakan command berikut:
npm run dev
npm run dev
Berikut tampilan aplikasi Next.js 13.
![Next.js Landing](/_next/image?url=%2Fstatic%2Fimages%2Ftutorial%2Fpart-1-tutorial-nextjs-13-typescript-ecommerce-cart-dengan-payment-gateway-antd-supabase-redux-toolkit-next-auth-postgre-sql-prisma%2Fnext-landing.png&w=2048&q=100)
Step 3: Setup Prisma ORM
Prisma merupakan database ORM (Object-Relational Mapping) yang yang memudahkan interaksi dengan basis data dan melakukan operasi CRUD (Create, Read, Update, Delete) dengan mudah dan efisien.
![Prisma Landing](/_next/image?url=%2Fstatic%2Fimages%2Ftutorial%2Fpart-1-tutorial-nextjs-13-typescript-ecommerce-cart-dengan-payment-gateway-antd-supabase-redux-toolkit-next-auth-postgre-sql-prisma%2Fprisma-landing.png&w=2048&q=100)
Lakukan instalasi prisma dengan menjalankan command berikut:
npm install prisma --save-dev
npm install prisma --save-dev
Init prisma pada project:
npx prisma init
npx prisma init
Setelah commandi diatas dijalankan maka prisma akan menggenerate folder prisma dan menambahkan key DATABASE_URL pada .env, kemudian buka file .env lalu tambahkan database url dari supabase.
# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema
# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings
DATABASE_URL="PASTE_YOUR_DATABASE_URL_HERE"
# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema
# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings
DATABASE_URL="PASTE_YOUR_DATABASE_URL_HERE"
Buka file schema.prisma kemudian tambahkan code berikut:
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(cuid())
name String
email String @unique
password String
emailVerified DateTime?
image String?
role String @default("user")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
Address Address[]
Transaction Transaction[]
@@map("users")
}
model Address {
id String @id @default(cuid())
userId String
street String
city String
state String
country String
postalCode String
isDefault Int
createdAt DateTime @default(now())
updatedAt DateTime?
user User @relation(fields: [userId], references: [id])
transactions Transaction[]
@@map("addresses")
}
model Transaction {
id String @id @default(cuid())
transactionNumber String
userId String
status String @default("PENDING")
totalItemsPrice Float
shippingPrice Float
shippingProvider String
shippingType String
addressId String
createdAt DateTime @default(now())
updatedAt DateTime?
user User @relation(fields: [userId], references: [id])
address Address @relation(fields: [addressId], references: [id])
items Item[]
@@map("transactions")
}
model Item {
id String @id @default(cuid())
productId String
quantity Int
transactionId String
price Float
createdAt DateTime @default(now())
updatedAt DateTime?
product Product @relation(fields: [productId], references: [id])
transaction Transaction @relation(fields: [transactionId], references: [id])
@@map("items")
}
model Category {
id String @id @default(cuid())
name String @unique
createdAt DateTime @default(now())
updatedAt DateTime?
products Product[]
@@map("categories")
}
model Product {
id String @id @default(cuid())
name String
description String
price Float
categoryId String
image String
createdAt DateTime @default(now())
updatedAt DateTime?
category Category @relation(fields: [categoryId], references: [id])
Item Item[]
@@map("products")
}
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(cuid())
name String
email String @unique
password String
emailVerified DateTime?
image String?
role String @default("user")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
Address Address[]
Transaction Transaction[]
@@map("users")
}
model Address {
id String @id @default(cuid())
userId String
street String
city String
state String
country String
postalCode String
isDefault Int
createdAt DateTime @default(now())
updatedAt DateTime?
user User @relation(fields: [userId], references: [id])
transactions Transaction[]
@@map("addresses")
}
model Transaction {
id String @id @default(cuid())
transactionNumber String
userId String
status String @default("PENDING")
totalItemsPrice Float
shippingPrice Float
shippingProvider String
shippingType String
addressId String
createdAt DateTime @default(now())
updatedAt DateTime?
user User @relation(fields: [userId], references: [id])
address Address @relation(fields: [addressId], references: [id])
items Item[]
@@map("transactions")
}
model Item {
id String @id @default(cuid())
productId String
quantity Int
transactionId String
price Float
createdAt DateTime @default(now())
updatedAt DateTime?
product Product @relation(fields: [productId], references: [id])
transaction Transaction @relation(fields: [transactionId], references: [id])
@@map("items")
}
model Category {
id String @id @default(cuid())
name String @unique
createdAt DateTime @default(now())
updatedAt DateTime?
products Product[]
@@map("categories")
}
model Product {
id String @id @default(cuid())
name String
description String
price Float
categoryId String
image String
createdAt DateTime @default(now())
updatedAt DateTime?
category Category @relation(fields: [categoryId], references: [id])
Item Item[]
@@map("products")
}
Schema diatas setara dengan diagram berikut.
![DB Diagram](/_next/image?url=%2Fstatic%2Fimages%2Ftutorial%2Fpart-1-tutorial-nextjs-13-typescript-ecommerce-cart-dengan-payment-gateway-antd-supabase-redux-toolkit-next-auth-postgre-sql-prisma%2Fdbdiagram.png&w=2048&q=100)
Lakukan migrasi agar schema kita up ke database:
npx prisma migrate dev
npx prisma migrate dev
Kamu dapat mengunjungi dashboard supabase kemudian pilih menu table untuk melihat table-table yang sudah dimigrasi.
![Supabase Tables](/_next/image?url=%2Fstatic%2Fimages%2Ftutorial%2Fpart-1-tutorial-nextjs-13-typescript-ecommerce-cart-dengan-payment-gateway-antd-supabase-redux-toolkit-next-auth-postgre-sql-prisma%2Fsupabase-table.png&w=2048&q=100)
Agar ada typescript typing berdasarkan schema ketika menggunakan ORM nanti, jalankan command berikut:
npx prisma generate
npx prisma generate
Kemudian install prisma client agar aplikasi dapat berinteraksi dengan database:
npm install @prisma/client
npm install @prisma/client
Buatlah sebuah prisma client pada pada src/lib/prisma.ts
import { PrismaClient } from "@prisma/client";
// PrismaClient is attached to the `global` object in development to prevent
// exhausting your database connection limit.
//
// Learn more:
// https://pris.ly/d/help/next-js-best-practices
const globalForPrisma = global as unknown as { prisma: PrismaClient };
export const prisma = globalForPrisma.prisma || new PrismaClient();
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;
export default prisma;
import { PrismaClient } from "@prisma/client";
// PrismaClient is attached to the `global` object in development to prevent
// exhausting your database connection limit.
//
// Learn more:
// https://pris.ly/d/help/next-js-best-practices
const globalForPrisma = global as unknown as { prisma: PrismaClient };
export const prisma = globalForPrisma.prisma || new PrismaClient();
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;
export default prisma;
Jika tidak ada error pada terminal server dev next.js maka proses setup ORM sudah berhasil.
Setup 4: Tambahkan Ant Design
Ant Design merupakan component UI library yang memudahkan pengembangan frontend dengan menyediakan component yang siap pakai dan responsif.
![Antd Landing](/_next/image?url=%2Fstatic%2Fimages%2Ftutorial%2Fpart-1-tutorial-nextjs-13-typescript-ecommerce-cart-dengan-payment-gateway-antd-supabase-redux-toolkit-next-auth-postgre-sql-prisma%2Fantd-landing.png&w=2048&q=100)
Install antd menggunakan command berikut:
npm i antd
npm i antd
Navigasi pada root layout pada folder app kemudian modifikasi menjadi berikut:
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import Provider from "@/provider";
import "./globals.css";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
title: "E-Commerce Tahu Coding",
description: "Modern E-Commerce with latest stack",
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body className={inter.className}>
<Provider>
<div className="text-black/80">{children}</div>
</Provider>
</body>
</html>
);
}
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import Provider from "@/provider";
import "./globals.css";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
title: "E-Commerce Tahu Coding",
description: "Modern E-Commerce with latest stack",
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body className={inter.className}>
<Provider>
<div className="text-black/80">{children}</div>
</Provider>
</body>
</html>
);
}
Buatlah sebuah component yaitu provider yang bertanggung jawab dalam menghandle provider client side pada aplikasi, pada component ini kita perlu menambahkan StyleProvider dari antd design dan set prop hashPriority menjadi "high" dan ssrInline menjadi true agar css tailwind tidak mereplace css antd dan compatible dengan next.js server side rendering.
"use client";
import { ReactNode } from "react";
import { StyleProvider } from "@ant-design/cssinjs";
type Props = {
children: ReactNode;
};
const Provider = ({ children }: Props) => {
return (
<StyleProvider hashPriority="high" ssrInline>
{children}
</StyleProvider>
);
};
export default Provider;
"use client";
import { ReactNode } from "react";
import { StyleProvider } from "@ant-design/cssinjs";
type Props = {
children: ReactNode;
};
const Provider = ({ children }: Props) => {
return (
<StyleProvider hashPriority="high" ssrInline>
{children}
</StyleProvider>
);
};
export default Provider;
Pastikan juga pada file global.css kita sudah menghapus css bawaan next.js dan meninggalkan directive tailwind saja.
@tailwind base;
@tailwind components;
@tailwind utilities;
@tailwind base;
@tailwind components;
@tailwind utilities;
Setup 5: Setup Redux Toolkit
Redux Toolkit merupkan library redux sudah disederhanakan sehingga kamu tidak perlu sibuk lagi menyiapkan boilerplate yang kompleks agar dapat memanfaatkan fitur-fitur redux.
![Redux Toolkit Landing](/_next/image?url=%2Fstatic%2Fimages%2Ftutorial%2Fpart-1-tutorial-nextjs-13-typescript-ecommerce-cart-dengan-payment-gateway-antd-supabase-redux-toolkit-next-auth-postgre-sql-prisma%2Fredux-toolkit-landing.png&w=2048&q=100)
Lakukan instalasi redux toolkit menggunakan command berikut:
npm install @reduxjs/toolkit react-redux
npm install @reduxjs/toolkit react-redux
Tambahkan store redux seperti berikut:
import { configureStore } from "@reduxjs/toolkit";
export const store = configureStore({
reducer: {},
});
// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>;
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch;
import { configureStore } from "@reduxjs/toolkit";
export const store = configureStore({
reducer: {},
});
// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>;
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch;
Modifikasi component Provider dengan menambahkan ReduxProvider:
"use client";
import { ReactNode } from "react";
import { StyleProvider } from "@ant-design/cssinjs";
import { store } from "@/store";
import { Provider as ReduxProvider } from "react-redux";
type Props = {
children: ReactNode;
};
const Provider = ({ children }: Props) => {
return (
<ReduxProvider store={store}>
<StyleProvider hashPriority="high" ssrInline>
{children}
</StyleProvider>
</ReduxProvider>
);
};
export default Provider;
"use client";
import { ReactNode } from "react";
import { StyleProvider } from "@ant-design/cssinjs";
import { store } from "@/store";
import { Provider as ReduxProvider } from "react-redux";
type Props = {
children: ReactNode;
};
const Provider = ({ children }: Props) => {
return (
<ReduxProvider store={store}>
<StyleProvider hashPriority="high" ssrInline>
{children}
</StyleProvider>
</ReduxProvider>
);
};
export default Provider;
Selamat! Proses setup aplikasi sudah selesai, kedepan kita akan belajar bagaimana caranya menambahkan NextAuth agar dapat Login dan Register pada aplikasi hingga menambahkan middleware agar dapat memproteksi halaman admin.
Code untuk tutorial kali ini link repo, jangan lupa star di repo ya!
Stay tune dan jangan lupa share & support Tahu Coding, Terima Kasih!