@vercel/postgres
는 자체 캐싱 의미론을 설정하지 않는데, 이는 프레임워크가 자체 정적 및 동적 동작을 설정할 수 있도록 한다.unstable_noStore
를 사용하여, 서버 컴포넌트나 데이터 Fetch 함수에서 정적 렌더링을 사용하지 않도록 선택할 수 있다. /app/lib/data.ts
파일 내에서 next/cache
라이브러리의 unstable_noStore()
함수를 import하여, 데이터 Fetch 함수의 맨 위에서 호출하면 된다.unstable_noStore
함수를 사용함으로써, 응답이 캐시되지 않도록 하여, 항상 최신 데이터를 가져올 수 있게 된다.import { sql } from "@vercel/postgres";
import {
CustomerField,
CustomersTable,
InvoiceForm,
InvoicesTable,
LatestInvoiceRaw,
User,
Revenue,
} from "./definitions";
import { formatCurrency } from "./utils";
import { unstable_noStore as noStore } from "next/cache";
export async function fetchRevenue() {
// Add noStore() here prevent the response from being cached.
// This is equivalent to in fetch(..., {cache: 'no-store'}).
noStore();
try {
const data = await sql<Revenue>`SELECT * FROM revenue`;
return data.rows;
} catch (error) {
console.error("Database Error:", error);
throw new Error("Failed to fetch revenue data.");
}
}
export async function fetchLatestInvoices() {
noStore();
try {
const data = await sql<LatestInvoiceRaw>`
SELECT invoices.amount, customers.name, customers.image_url, customers.email, invoices.id
FROM invoices
JOIN customers ON invoices.customer_id = customers.id
ORDER BY invoices.date DESC
LIMIT 5`;
const latestInvoices = data.rows.map((invoice) => ({
...invoice,
amount: formatCurrency(invoice.amount),
}));
return latestInvoices;
} catch (error) {
console.error("Database Error:", error);
throw new Error("Failed to fetch the latest invoices.");
}
}
export async function fetchCardData() {
noStore();
try {
const invoiceCountPromise = sql`SELECT COUNT(*) FROM invoices`;
const customerCountPromise = sql`SELECT COUNT(*) FROM customers`;
const invoiceStatusPromise = sql`SELECT
SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END) AS "paid",
SUM(CASE WHEN status = 'pending' THEN amount ELSE 0 END) AS "pending"
FROM invoices`;
const data = await Promise.all([
invoiceCountPromise,
customerCountPromise,
invoiceStatusPromise,
]);
const numberOfInvoices = Number(data[0].rows[0].count ?? "0");
const numberOfCustomers = Number(data[1].rows[0].count ?? "0");
const totalPaidInvoices = formatCurrency(data[2].rows[0].paid ?? "0");
const totalPendingInvoices = formatCurrency(data[2].rows[0].pending ?? "0");
return {
numberOfCustomers,
numberOfInvoices,
totalPaidInvoices,
totalPendingInvoices,
};
} catch (error) {
console.error("Database Error:", error);
throw new Error("Failed to card data.");
}
}
const ITEMS_PER_PAGE = 6;
export async function fetchFilteredInvoices(
query: string,
currentPage: number
) {
noStore();
const offset = (currentPage - 1) * ITEMS_PER_PAGE;
try {
const invoices = await sql<InvoicesTable>`
SELECT
invoices.id,
invoices.amount,
invoices.date,
invoices.status,
customers.name,
customers.email,
customers.image_url
FROM invoices
JOIN customers ON invoices.customer_id = customers.id
WHERE
customers.name ILIKE ${`%${query}%`} OR
customers.email ILIKE ${`%${query}%`} OR
invoices.amount::text ILIKE ${`%${query}%`} OR
invoices.date::text ILIKE ${`%${query}%`} OR
invoices.status ILIKE ${`%${query}%`}
ORDER BY invoices.date DESC
LIMIT ${ITEMS_PER_PAGE} OFFSET ${offset}
`;
return invoices.rows;
} catch (error) {
console.error("Database Error:", error);
throw new Error("Failed to fetch invoices.");
}
}
export async function fetchInvoicesPages(query: string) {
noStore();
try {
const count = await sql`SELECT COUNT(*)
FROM invoices
JOIN customers ON invoices.customer_id = customers.id
WHERE
customers.name ILIKE ${`%${query}%`} OR
customers.email ILIKE ${`%${query}%`} OR
invoices.amount::text ILIKE ${`%${query}%`} OR
invoices.date::text ILIKE ${`%${query}%`} OR
invoices.status ILIKE ${`%${query}%`}
`;
const totalPages = Math.ceil(Number(count.rows[0].count) / ITEMS_PER_PAGE);
return totalPages;
} catch (error) {
console.error("Database Error:", error);
throw new Error("Failed to fetch total number of invoices.");
}
}
export async function fetchInvoiceById(id: string) {
noStore();
try {
const data = await sql<InvoiceForm>`
SELECT
invoices.id,
invoices.customer_id,
invoices.amount,
invoices.status
FROM invoices
WHERE invoices.id = ${id};
`;
const invoice = data.rows.map((invoice) => ({
...invoice,
// Convert amount from cents to dollars
amount: invoice.amount / 100,
}));
return invoice[0];
} catch (error) {
console.error("Database Error:", error);
}
}
export async function fetchCustomers() {
noStore();
try {
const data = await sql<CustomerField>`
SELECT
id,
name
FROM customers
ORDER BY name ASC
`;
const customers = data.rows;
return customers;
} catch (err) {
console.error("Database Error:", err);
throw new Error("Failed to fetch all customers.");
}
}
export async function fetchFilteredCustomers(query: string) {
noStore();
try {
const data = await sql<CustomersTable>`
SELECT
customers.id,
customers.name,
customers.email,
customers.image_url,
COUNT(invoices.id) AS total_invoices,
SUM(CASE WHEN invoices.status = 'pending' THEN invoices.amount ELSE 0 END) AS total_pending,
SUM(CASE WHEN invoices.status = 'paid' THEN invoices.amount ELSE 0 END) AS total_paid
FROM customers
LEFT JOIN invoices ON customers.id = invoices.customer_id
WHERE
customers.name ILIKE ${`%${query}%`} OR
customers.email ILIKE ${`%${query}%`}
GROUP BY customers.id, customers.name, customers.email, customers.image_url
ORDER BY customers.name ASC
`;
const customers = data.rows.map((customer) => ({
...customer,
total_pending: formatCurrency(customer.total_pending),
total_paid: formatCurrency(customer.total_paid),
}));
return customers;
} catch (err) {
console.error("Database Error:", err);
throw new Error("Failed to fetch customer table.");
}
}
export async function getUser(email: string) {
noStore();
try {
const user = await sql`SELECT * from USERS where email=${email}`;
return user.rows[0] as User;
} catch (error) {
console.error("Failed to fetch user:", error);
throw new Error("Failed to fetch user.");
}
}
/app/lib/data.ts
파일에서 fetchRevenue()
안의 console.log
와 setTimeout
주석을 해제해보자.export async function fetchRevenue() {
try {
// Artificially delay a reponse for demo purposes.
// Don't do this in real life :)
console.log("Fetching revenue data...");
await new Promise((resolve) => setTimeout(resolve, 3000));
const data = await sql<Revenue>`SELECT * FROM revenue`;
console.log("Data fetch complete after 3 seconds.");
return data.rows;
} catch (error) {
console.error("Database Error:", error);
throw new Error("Failed to fetch revenue data.");
}
}