ecommerce-template/apps/remix-template/app/routes/api.products.tsx

124 lines
3 KiB
TypeScript

import type { ActionFunctionArgs, LoaderFunctionArgs } from "@remix-run/node";
import { getProducts, createProduct } from "@repo/db/index";
export async function loader({ request }: LoaderFunctionArgs) {
try {
const url = new URL(request.url);
const page = parseInt(url.searchParams.get("page") || "1");
const limit = parseInt(url.searchParams.get("limit") || "10");
if (page < 1 || limit < 1 || limit > 100) {
return new Response(
JSON.stringify({
error: "Invalid pagination parameters. Page must be >= 1, limit must be 1-100",
}),
{
status: 400,
headers: { "Content-Type": "application/json" },
}
);
}
const result = await getProducts(page, limit);
return new Response(JSON.stringify(result), {
headers: { "Content-Type": "application/json" },
});
} catch (error: any) {
console.error("API Error:", error);
return new Response(
JSON.stringify({
error: "Failed to fetch products",
message: error.message,
}),
{
status: 500,
headers: { "Content-Type": "application/json" },
}
);
}
}
export async function action({ request }: ActionFunctionArgs) {
if (request.method !== "POST") {
return new Response(JSON.stringify({ error: "Method not allowed" }), {
status: 405,
headers: { "Content-Type": "application/json" },
});
}
try {
const body = await request.json();
const { name, description, price, imageUrl, isPublished } = body;
// Validation
if (!name || typeof name !== "string" || !name.trim()) {
return new Response(
JSON.stringify({
error: "Product name is required and must be a non-empty string",
}),
{
status: 400,
headers: { "Content-Type": "application/json" },
}
);
}
if (price === undefined || price === null || isNaN(Number(price))) {
return new Response(
JSON.stringify({
error: "Price is required and must be a valid number",
}),
{
status: 400,
headers: { "Content-Type": "application/json" },
}
);
}
const priceValue = Number(price);
if (priceValue < 0) {
return new Response(
JSON.stringify({ error: "Price must be non-negative" }),
{
status: 400,
headers: { "Content-Type": "application/json" },
}
);
}
const productData = {
name: name.trim(),
description: description?.trim() || null,
price: priceValue.toString(),
imageUrl: imageUrl?.trim() || null,
isPublished: Boolean(isPublished),
};
const newProduct = await createProduct(productData);
return new Response(
JSON.stringify({
success: true,
message: "Product created successfully",
data: newProduct,
}),
{
status: 201,
headers: { "Content-Type": "application/json" },
}
);
} catch (error: any) {
console.error("API Error:", error);
return new Response(
JSON.stringify({
success: false,
error: "Failed to create product",
message: error.message || "An unexpected error occurred",
}),
{
status: 500,
headers: { "Content-Type": "application/json" },
}
);
}
}