Skip to main content
Back to Elite Events

Elite Events Documentation

Technical documentation, guides, and API references for the Elite Events platform.

Development Guides/API Integration

API Integration Guide

This guide explains how to use the new API-based data fetching in your Elite Events application.

Overview

The application now has two approaches for data management:

  1. Legacy (Static Data): Original Redux slices using static TypeScript data files
  2. API-Based (Database): New Redux slices and hooks that fetch from the MySQL database via REST API

You can gradually migrate components from static data to API-based data.


Quick Start

Start the Development Server

npm run dev

The API routes are automatically available at:

  • Products: http://localhost:3000/api/products
  • Categories: http://localhost:3000/api/categories
  • Blog: http://localhost:3000/api/blog
  • Cart: http://localhost:3000/api/cart
  • Wishlist: http://localhost:3000/api/wishlist

The easiest way to fetch data from the API is using custom hooks.

Fetch Products

import { useProducts } from "@/hooks/useProducts";

function ProductList() {
  const { products, loading, error, pagination } = useProducts({
    page: 1,
    limit: 10,
    categoryId: 2, // optional
    minPrice: 10,  // optional
    maxPrice: 100, // optional
  });

  if (loading) return <div>Loading products...</div>;
  if (error) return <div>Error: {error}</div>;

  return (
    <div>
      {products.map((product) => (
        <div key={product.id}>
          <h3>{product.title}</h3>
          <p>${product.discountedPrice}</p>
        </div>
      ))}
      <div>
        Page {pagination.page} of {pagination.totalPages}
      </div>
    </div>
  );
}

Fetch Categories

import { useCategories } from "@/hooks/useCategories";

function CategoryList() {
  const { categories, loading, error } = useCategories(true); // true = include product count

  if (loading) return <div>Loading categories...</div>;
  if (error) return <div>Error: {error}</div>;

  return (
    <div>
      {categories.map((category) => (
        <div key={category.id}>
          <h3>{category.title}</h3>
          {category.productCount && <p>{category.productCount} products</p>}
        </div>
      ))}
    </div>
  );
}

Fetch Blog Posts

import { useBlogPosts } from "@/hooks/useBlogPosts";

function BlogList() {
  const { posts, loading, error, pagination } = useBlogPosts({
    page: 1,
    limit: 10,
    search: "ecommerce", // optional
  });

  if (loading) return <div>Loading blog posts...</div>;
  if (error) return <div>Error: {error}</div>;

  return (
    <div>
      {posts.map((post) => (
        <div key={post.id}>
          <h2>{post.title}</h2>
          <p>{post.excerpt}</p>
          <small>{post.views} views</small>
        </div>
      ))}
    </div>
  );
}

Using Redux with API (Advanced)

Cart Management with API

"use client";

import { useDispatch, useSelector } from "react-redux";
import { AppDispatch } from "@/redux/store";
import {
  fetchCart,
  addToCartAsync,
  updateCartItemAsync,
  removeFromCartAsync,
  selectCartItems,
  selectCartLoading,
  selectCartError,
} from "@/redux/features/cartSliceApi";
import { useEffect } from "react";

function CartComponent() {
  const dispatch = useDispatch<AppDispatch>();
  const cartItems = useSelector(selectCartItems);
  const loading = useSelector(selectCartLoading);
  const error = useSelector(selectCartError);

  // Fetch cart on component mount
  useEffect(() => {
    dispatch(fetchCart());
  }, [dispatch]);

  const handleAddToCart = (productId: number, quantity: number) => {
    dispatch(addToCartAsync({ productId, quantity }));
  };

  const handleUpdateQuantity = (id: number, quantity: number) => {
    dispatch(updateCartItemAsync({ id, quantity }));
  };

  const handleRemove = (id: number) => {
    dispatch(removeFromCartAsync(id));
  };

  if (loading) return <div>Loading cart...</div>;
  if (error) return <div>Error: {error}</div>;

  return (
    <div>
      {cartItems.map((item) => (
        <div key={item.id}>
          <h3>{item.title}</h3>
          <p>Quantity: {item.quantity}</p>
          <button onClick={() => handleUpdateQuantity(item.id, item.quantity + 1)}>
            +
          </button>
          <button onClick={() => handleUpdateQuantity(item.id, item.quantity - 1)}>
            -
          </button>
          <button onClick={() => handleRemove(item.id)}>Remove</button>
        </div>
      ))}
    </div>
  );
}

Wishlist Management with API

"use client";

import { useDispatch, useSelector } from "react-redux";
import { AppDispatch } from "@/redux/store";
import {
  fetchWishlist,
  addToWishlistAsync,
  removeFromWishlistAsync,
  selectWishlistItems,
  selectWishlistLoading,
  selectWishlistError,
} from "@/redux/features/wishlistSliceApi";
import { useEffect } from "react";

function WishlistComponent() {
  const dispatch = useDispatch<AppDispatch>();
  const wishlistItems = useSelector(selectWishlistItems);
  const loading = useSelector(selectWishlistLoading);
  const error = useSelector(selectWishlistError);

  // Fetch wishlist on component mount
  useEffect(() => {
    dispatch(fetchWishlist());
  }, [dispatch]);

  const handleAddToWishlist = (productId: number) => {
    dispatch(addToWishlistAsync(productId));
  };

  const handleRemove = (productId: number) => {
    dispatch(removeFromWishlistAsync(productId));
  };

  if (loading) return <div>Loading wishlist...</div>;
  if (error) return <div>Error: {error}</div>;

  return (
    <div>
      {wishlistItems.map((item) => (
        <div key={item.id}>
          <h3>{item.title}</h3>
          <p>${item.discountedPrice}</p>
          <button onClick={() => handleRemove(item.id)}>Remove</button>
        </div>
      ))}
    </div>
  );
}

Direct API Calls (Low-Level)

You can also use the API client functions directly without hooks or Redux:

import { getProducts, getProductById } from "@/lib/api/products";
import { getCategories } from "@/lib/api/categories";
import { addToCart, getCart } from "@/lib/api/cart";

async function example() {
  // Get all products
  const { products, pagination } = await getProducts({ page: 1, limit: 10 });

  // Get single product
  const product = await getProductById(1);

  // Get categories
  const categories = await getCategories();

  // Add to cart
  await addToCart(1, 2); // productId: 1, quantity: 2

  // Get cart
  const cart = await getCart();
}

Migration Strategy

Keep existing components working with static data while gradually migrating to API:

  1. Start with non-critical pages (e.g., blog)
  2. Update one component at a time
  3. Test thoroughly before moving to next component
  4. Keep both old and new Redux slices during transition

Option 2: Complete Migration

Replace all static data fetching at once:

  1. Update src/redux/store.ts to use new API slices
  2. Update all components to use new hooks
  3. Remove old static data files (keep for backup)

File Structure

src/
├── lib/
│   ├── prisma.ts                    # Prisma client singleton
│   └── api/                         # API client functions
│       ├── products.ts
│       ├── categories.ts
│       ├── cart.ts
│       ├── wishlist.ts
│       └── blog.ts
├── hooks/                           # Custom React hooks
│   ├── useProducts.ts
│   ├── useCategories.ts
│   └── useBlogPosts.ts
├── redux/
│   └── features/
│       ├── cart-slice.ts            # Legacy (static data)
│       ├── cart-slice-api.ts        # New (API-based)
│       ├── wishlist-slice.ts        # Legacy (static data)
│       └── wishlist-slice-api.ts    # New (API-based)
└── app/
    └── api/                         # Next.js API routes
        ├── products/
        ├── categories/
        ├── cart/
        ├── wishlist/
        └── blog/

Testing the API

Using Browser

Open these URLs in your browser:

Using curl

# Get all products
curl http://localhost:3000/api/products

# Get products with filters
curl "http://localhost:3000/api/products?page=1&limit=5&categoryId=1"

# Get single product
curl http://localhost:3000/api/products/1

# Get categories with product count
curl "http://localhost:3000/api/categories?includeProductCount=true"

# Get blog posts
curl http://localhost:3000/api/blog

Using Prisma Studio

View and edit database data directly:

npx prisma studio

Opens at http://localhost:5555


Error Handling

All API functions and hooks include error handling:

const { products, loading, error } = useProducts();

if (error) {
  // Show error message to user
  return <div className="error">Error: {error}</div>;
}

Redux slices also track errors:

const error = useSelector(selectCartError);

if (error) {
  toast.error(error);
  dispatch(clearError()); // Clear error after showing
}

Performance Considerations

Caching

The API routes don't currently implement caching. Consider adding:

  1. Client-side caching: Use React Query or SWR
  2. Server-side caching: Add Redis or Next.js cache headers
  3. Database caching: Prisma query result caching

Pagination

Always use pagination for large datasets:

const { products, pagination } = useProducts({
  page: currentPage,
  limit: 20, // Adjust based on your needs
});

Loading States

Show loading indicators for better UX:

if (loading) {
  return <Skeleton count={10} />; // Use skeleton loading
}

Authentication (Coming Soon)

Cart and Wishlist APIs require authentication. Currently using placeholder userId = 1.

To add authentication:

  1. Install NextAuth.js
  2. Add session management
  3. Update API routes to use authenticated user ID
  4. Add protected route middleware

See DATABASE_MIGRATION_PLAN.md Phase 7 for details.


Troubleshooting

API returns empty data

  1. Check if database is seeded: npx tsx test-db.ts
  2. Verify database connection in .env
  3. Check Prisma Studio: npx prisma studio

"Failed to fetch" errors

  1. Ensure dev server is running: npm run dev
  2. Check browser console for CORS issues
  3. Verify API route exists in src/app/api/

TypeScript errors

  1. Run Prisma generate: npx prisma generate
  2. Restart TypeScript server in VS Code
  3. Check import paths use @/ alias

Next Steps

  1. ✅ Database setup complete
  2. ✅ API routes implemented
  3. ✅ API client utilities created
  4. ✅ Custom hooks created
  5. ✅ Redux slices with API integration
  6. ⏳ Add authentication (NextAuth.js)
  7. ⏳ Migrate components to use API
  8. ⏳ Add loading skeletons
  9. ⏳ Implement error boundaries
  10. ⏳ Add caching strategy

Refer to DATABASE_MIGRATION_PLAN.md for the complete roadmap.

Documentation | Elite Events | Philip Rehberger