I have this getProducts in my backend:
export const getProducts = async (req: Request, res: Response) => {
try {
const search = (req.query.search as string || '').toLowerCase();
const categoryId = req.query.c as string;
const subcategoryId = req.query.sc as string;
const brandId = req.query.b as string;

    const conditions = [];
    const queryParams: any[] = [];

    if (search) {
        const searchWords = search.split(/\s+/).filter(Boolean);
        const searchConditions = searchWords.map((word) => {
            queryParams.push(`${word}%`, `%${word}%`);
            return `(LOWER(p.name) LIKE $${queryParams.length - 1} OR LOWER(p.name) LIKE $${queryParams.length})`;
        });
        conditions.push(`(${searchConditions.join(' OR ')})`);
    }

    if (categoryId) {
        queryParams.push(categoryId);
        conditions.push(`(c.id = $${queryParams.length} OR EXISTS (
                SELECT 1 FROM subcategories sub 
                WHERE sub.category_id = $${queryParams.length} 
            AND (sub.id = sc.id OR sub.id = p.brand_id)
        ))`);
    }

    if (subcategoryId) {
        queryParams.push(subcategoryId);
        conditions.push(`(sc.id = $${queryParams.length} OR p.brand_id = $${queryParams.length})`);
    }

    if (brandId) {
        queryParams.push(brandId);
        conditions.push(`p.brand_id = $${queryParams.length}`);
        }

        const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';

        // Add search to queryParams
        queryParams.push(search);

        const productQuery = `
            WITH filtered_products AS (
                SELECT p.*, 
                       sc.id AS subcategory_id, sc.name AS subcategory_name,
                       c.id AS main_category_id, c.name AS main_category_name
                FROM products p
                LEFT JOIN product_categories pc ON p.id = pc.product_id
                LEFT JOIN subcategories sc ON pc.category_id = sc.id
                LEFT JOIN categories c ON sc.category_id = c.id
                ${whereClause}
                ORDER BY 
                    CASE 
                        WHEN $${queryParams.length} != '' THEN 
                        CASE 
                            WHEN LOWER(p.name) LIKE LOWER($${queryParams.length} || '%') THEN 1
                                WHEN LOWER(p.name) LIKE '%' || LOWER($${queryParams.length}) || '%' THEN 2
                            ELSE 3
                        END
                    ELSE 0
                END,
                p.created_at DESC
        )
        SELECT 
            fp.*,
            (SELECT jsonb_agg(jsonb_build_object(
                'id', c.id, 'name', c.name, 'price', c.price, 'qty', c.qty, 'sold', c.sold,
                'images', (SELECT jsonb_agg(i.url) FROM images i WHERE i.color_id = c.id),
                'sizes', (SELECT jsonb_agg(jsonb_build_object(
                    'id', s.id, 'name', s.name, 'price', s.price, 'qty', s.qty, 'sold', s.sold,
                    'images', (SELECT jsonb_agg(i.url) FROM images i WHERE i.size_id = s.id)
                )) FROM sizes s WHERE s.color_id = c.id)
            )) FROM colors c WHERE c.product_id = fp.id) AS colors,
            (SELECT jsonb_agg(jsonb_build_object(
                'id', s.id, 'name', s.name, 'price', s.price, 'qty', s.qty, 'sold', s.sold, 'color_id', s.color_id,
                'images', (SELECT jsonb_agg(i.url) FROM images i WHERE i.size_id = s.id)
            )) FROM sizes s WHERE s.product_id = fp.id AND s.color_id IS NULL) AS sizes,
            (SELECT jsonb_agg(i.url) FROM images i WHERE i.product_id = fp.id AND i.color_id IS NULL AND i.size_id IS NULL) AS images
        FROM filtered_products fp
    `;

    const productResult = await query(productQuery, queryParams);

    const products = productResult.rows.map(product => ({
        id: product.id,
        name: product.name,
        description: product.description,
        brand_id: product.brand_id,
        has_color: product.has_color,
        has_size: product.has_size,
        product_type: product.product_type,
        price: product.price,
        qty: product.qty,
        sold: product.sold,
        created_at: product.created_at,
        updated_at: product.updated_at,
        categories: [
            {
                id: product.subcategory_id,
                name: product.subcategory_name,
                main_category_id: product.main_category_id,
                main_category_name: product.main_category_name
            }
        ],
        colors: product.colors || [],
        sizes: product.sizes || [],
        images: [
            ...(product.images || []),
            ...(product.colors || []).flatMap((c: { images: any; }) => c.images || []),
            ...(product.sizes || []).flatMap((s: { images: any; }) => s.images || [])
        ]
    }));

    const result = {
        products,
        total: products.length
    };

    res.json(result);
} catch (error) {
    console.error('Error fetching products:', error);
    res.status(500).json({ error: 'Internal server error' });
}

};

and I have this in products page:
import React from 'react';
import ProductList from '@/components/ProductList';
import AsyncNavBar from '@/components/AsyncNavBar';
import { unstable_cache } from 'next/cache';
import Link from 'next/link';

const ITEMS_PER_PAGE = 15;
export const revalidate = 3600

// Wrap fetchSubBrands with unstable_cache
const fetchSubBrands = unstable_cache(
async () => {
const response = await fetch('https://api.naayiq.com/subcategories/brands', {
headers: { 'Content-Type': 'application/json' },
cache: "force-cache",
next: {revalidate: 3600}
});

    if (response.status === 200) {
        return response.json();
    }
    throw new Error(`Failed to fetch sub-brands: ${response.statusText}`);
},
{
    revalidate: 1440,
}

);

const fetchProducts = async () => {
const response = await fetch('https://api.naayiq.com/products', {
headers: {'Content-Type': 'application/json'},
next: {revalidate: 3600}
});

if (!response.ok) {
    throw new Error('Failed to fetch products');
}
return response.json();

}

const getFilteredProducts = async (
page = 1,
query = '',
c = '',
sc = '',
b = '',
minPriceA = 0,
maxPriceA = Infinity,
availability = 'all',
sortBy = ''
) => {
const [productsData, subBrands] = await Promise.all([
fetchProducts(),
b ? fetchSubBrands() : Promise.resolve([]), // Uses unstable_cache: fetched once every 4 hours if 'b' is truthy
]);

let products = productsData.products;

const subCategories = b
    ? subBrands
        .filter((item) => item.category_id.toString() === b)
        .map((item) => item.id.toString())
    : [];

// Calculate min and max prices
let minPrice = Infinity;
let maxPrice = -Infinity;
products.forEach((product) => {
    product.sizes.forEach((size) => {
        const price = parseFloat(size.price);
        if (price < minPrice) minPrice = price;
        if (price > maxPrice) maxPrice = price;
    });
});

const filterProduct = (product) => {
    if (
        c &&
        !product.categories.some((cat) => cat.main_category_id?.toString() === c)
    )
        return false;
    if (
        sc &&
        product?.brand_id.toString() !== sc &&
        !product.categories.some((cat) => cat.id?.toString() === sc)
    )
        return false;
    if (b && !subCategories.includes(product?.brand_id.toString())) return false;

    // Price range filter
    const productPrices = product.sizes.map((size) => parseFloat(size.price));
    const productMinPrice = Math.min(...productPrices);
    const productMaxPrice = Math.max(...productPrices);

    if (productMinPrice < minPriceA || productMaxPrice > maxPriceA) {
        return false;
    }

    // Availability filter
    if (availability !== 'all') {
        const inStock = product.sizes.some((size) => size.qty > 0);
        if (
            (availability === 'in_stock' && !inStock) ||
            (availability === 'out_of_stock' && inStock)
        ) {
            return false;
        }
    }

    // Query filter
    if (query) {
        const normalizedName = product.name.normalize('NFC').toLowerCase();
        const cleanName = normalizedName.replace(
            /[\u200E\u200F\u202A-\u202E\u2066-\u2069]/g,
            ''
        );
        const queryWords = query.toLowerCase().split(/\s+/);
        return queryWords.every((word) => cleanName.includes(word));
    }

    return true;
};

// Apply the filter
let filteredProducts = products.filter(filterProduct);

// Calculate total sold for each product
filteredProducts.forEach((product) => {
    product.totalSold = product.sizes.reduce((sum, size) => sum + size.sold, 0);
});

// Apply sorting
switch (sortBy) {
    case 'Price: Low to High':
        filteredProducts.sort(
            (a, b) =>
                Math.min(...a.sizes.map((s) => parseFloat(s.price))) -
                Math.min(...b.sizes.map((s) => parseFloat(s.price)))
        );
        break;
    case 'Price: High to Low':
        filteredProducts.sort(
            (a, b) =>
                Math.max(...b.sizes.map((s) => parseFloat(s.price))) -
                Math.max(...a.sizes.map((s) => parseFloat(s.price)))
        );
        break;
    case 'Name: A to Z':
        filteredProducts.sort((a, b) => a.name.localeCompare(b.name));
        break;
    case 'Name: Z to A':
        filteredProducts.sort((a, b) => b.name.localeCompare(a.name));
        break;
    case 'Best Selling':
        filteredProducts.sort((a, b) => b.totalSold - a.totalSold);
        break;
    case 'Newest Arrivals':
        filteredProducts.sort(
            (a, b) => new Date(b.created_at) - new Date(a.created_at)
        );
        break;
    default:
        // Default sorting (optional)
        break;
}

// Additional query-based sorting if applicable
if (query && !sortBy) {
    const lowerQuery = query.toLowerCase();
    filteredProducts.sort((a, b) => {
        const aIndex = a.name.toLowerCase().indexOf(lowerQuery);
        const bIndex = b.name.toLowerCase().indexOf(lowerQuery);
        return (aIndex === -1 ? Infinity : aIndex) - (bIndex === -1 ? Infinity : bIndex);
    });
}

const totalProducts = filteredProducts.length;
const end = page * ITEMS_PER_PAGE;

return {
    products: filteredProducts.slice(0, end),
    totalProducts,
    hasMore: end < totalProducts,
    minPrice,
    maxPrice,
};

};

// The main ProductsPage component
const ProductsPage = async ({ searchParams }) => {
// Destructure and parse search parameters
const {
page = '1',
query = '',
c = '',
sc = '',
b = '',
minPrice = '0',
maxPrice = '',
availability = 'all',
sortBy = '',
title = '',
} = searchParams;

const pageNum = parseInt(page, 10) || 1;
const minPriceNum = parseFloat(minPrice) || 0;
const maxPriceNum = maxPrice ? parseFloat(maxPrice) : Infinity;

try {
    // Get filtered products based on search parameters
    const { products, totalProducts, hasMore, minPrice, maxPrice } = await getFilteredProducts(
        pageNum,
        query,
        c,
        sc,
        b,
        minPriceNum,
        maxPriceNum,
        availability,
        sortBy
    );

    return (
        <>
            <AsyncNavBar />
            <ProductList
                initialProducts={products}
                totalProducts={totalProducts}
                hasMore={hasMore}
                initialPage={pageNum}
                initialQuery={query}
                initialC={c}
                initialSc={sc}
                initialB={b}
                minPrice={minPrice}
                maxPrice={maxPrice}
                title={sortBy || title || 'All Products'}
            />
        </>
    );
} catch (error) {
    // Enhanced error handling  user-friendly message
    console.error('Error fetching products:', error);
    return (
        <div className="font-serif flex flex-col justify-center items-center p-4">
            <p>Oops! Something went wrong while loading products. Please try again later.</p>
            <Link href="/" className="text-blue-500 underline mt-2">
                Go back home
            </Link>
        </div>
    );
}

};

export default ProductsPage;
However I decided to move to client side rendering instead of server side rendering so I want you to keep getProducts get all the products first... and then do the filter and search logic as server side rendering... so what I mean is move the logic from server side rendering to the backend entierly...
I also moved from serverless to ubuntu 1gb vps.
Do all the required changes.. also don't forgot to optimize for vps...