Closed4

Next.jsの理解を深める!Chapter 6-7

nakamotonakamoto

Chapter 6

Before you can continue working on your dashboard, you'll need some data. In this chapter, you'll be setting up a PostgreSQL database using @vercel/postgres. If you're already familiar with PostgreSQL and would prefer to use your own provider, you can skip this chapter and set it up on your own. Otherwise, let's continue!

In this chapter...
Here are the topics we’ll cover

Push your project to GitHub.

Set up a Vercel account and link your GitHub repo for instant previews and deployments.

Create and link your project to a Postgres database.

Seed the database with initial data.

  • Next.jsをVercelにデプロイすると@vercel/postgresを使用できる。
  • DBのリージョンはとても重要。ラウンドトリップを考慮し選択すべし。
  • アプリケーションのホスティングされてるリージョンとDBのリージョンは揃える。
  • ただ@vercel/postgresはいまのところ日本リージョンなし。
  • Vercel FunctionsとDBのラウンドトリップとても多い。だからリージョンは揃える。
    いまのところどっちもシンガポールとするともっとも早い。
    もし他のDB使うならどっちも日本とできるとベスト。

Seed your database
Now that your database has been created, let's seed it with some initial data. This will allow you to have some data to work with as you build the dashboard.

In the /scripts folder of your project, there's a file called seed.js. This script contains the instructions for creating and seeding the invoices, customers, user, revenue tables.

Don't worry if you don't understand everything the code is doing, but to give you an overview, the script uses SQL to create the tables, and the data from placeholder-data.js file to populate them after they've been created.

Next, in your package.json file, add the following line to your scripts:

/package.json
"scripts": {
  "build": "next build",
  "dev": "next dev",
  "start": "next start",
  "seed": "node -r dotenv/config ./scripts/seed.js"
},

This is the command that will execute seed.js.

Now, run npm run seed. You should see some console.log messages in your terminal to let you know the script is running.

nakamotonakamoto

Chapter 7(first)

Choosing how to fetch data
API layer
APIs are an intermediary layer between your application code and database. There are a few cases where you might use an API:

If you're using 3rd party services that provide an API.
If you're fetching data from the client, you want to have an API layer that runs on the server to avoid exposing your database secrets to the client.
In Next.js, you can create API endpoints using Route Handlers.

Database queries
When you're creating a full-stack application, you'll also need to write logic to interact with your database. For relational databases like Postgres, you can do this with SQL, or an ORM like Prisma.

There are a few cases where you have to write database queries:

When creating your API endpoints, you need to write logic to interact with your database.
If you are using React Server Components (fetching data on the server), you can skip the API layer, and query your database directly without risking exposing your database secrets to the client.

  • API エンドポイントを作成するときは、DBと対話するロジックを作成する。React Server Components (サーバー上のデータを取得) を使用している場合は、API レイヤーをスキップして、DBの秘密をクライアントに公開する危険を冒さずにデータベースに直接クエリを実行できる。

Using Server Components to fetch data
By default, Next.js applications use React Server Components. Fetching data with Server Components is a relatively new approach and there are a few benefits of using them:

Server Components support promises, providing a simpler solution for asynchronous tasks like data fetching. You can use async/await syntax without reaching out for useEffect, useState or data fetching libraries.
Server Components execute on the server, so you can keep expensive data fetches and logic on the server and only send the result to the client.
As mentioned before, since Server Components execute on the server, you can query the database directly without an additional API layer.

  • RSCはPromiseをサポートし、データフェッチなどの非同期タスクに対してよりシンプルな解決策を提供する。useEffectやuseState、データ取得ライブラリを使用することなく、async/await構文を利用できる。
  • RSCはサーバー上で実行されるため、重いデータフェッチや処理のロジックをサーバー側で素早く実行できる。その結果、処理されたデータのみがクライアントに送信される。RSCはサーバー上で動作することから、追加のAPI層を介さずに直接データベースへのクエリを実行できる。

Using SQL
For your dashboard project, you'll write database queries using the Vercel Postgres SDK and SQL. There are a few reasons why we'll be using SQL:

SQL is the industry standard for querying relational databases (e.g. ORMs generate SQL under the hood).
Having a basic understanding of SQL can help you understand the fundamentals of relational databases, allowing you to apply your knowledge to other tools.
SQL is versatile, allowing you to fetch and manipulate specific data.
The Vercel Postgres SDK provides protection against SQL injections.
Don't worry if you haven't used SQL before - we have provided the queries for you.

Go to /app/lib/data.ts, here you'll see that we're importing the sql function from @vercel/postgres. This function allows you to query your database:

  • @vercel/postgressqlを使用すると動的なクエリを実行できたりする。
/app/lib/data.ts
import { sql } from '@vercel/postgres';
 
// ...
  • 従来ReactによってDBを取得するとなったらTanStack Query SWRなどを使って行ってた。
    それらは内部的にuseEffect useStateなどを使っておりRSCには不要となる。
/app/dashboard/page.tsx
import { Card } from '@/app/ui/dashboard/cards';
import RevenueChart from '@/app/ui/dashboard/revenue-chart';
import LatestInvoices from '@/app/ui/dashboard/latest-invoices';
import { lusitana } from '@/app/ui/fonts';
import { fetchRevenue } from '@/app/lib/data';
 
export default async function Page() {
  const revenue = await fetchRevenue();
  // ...
}
nakamotonakamoto

Chapter 7(second)

What are request waterfalls?
A "waterfall" refers to a sequence of network requests that depend on the completion of previous requests. In the case of data fetching, each request can only begin once the previous request has returned data.

For example, we need to wait for fetchRevenue() to execute before fetchLatestInvoices() can start running, and so on.

This pattern is not necessarily bad. There may be cases where you want waterfalls because you want a condition to be satisfied before you make the next request. For example, you might want to fetch a user's ID and profile information first. Once you have the ID, you might then proceed to fetch their list of friends. In this case, each request is contingent on the data returned from the previous request.

However, this behavior can also be unintentional and impact performance.

/app/dashboard/page.tsx
const revenue = await fetchRevenue();
const latestInvoices = await fetchLatestInvoices(); // wait for fetchRevenue() to finish
const {
  numberOfInvoices,
  numberOfCustomers,
  totalPaidInvoices,
  totalPendingInvoices,
} = await fetchCardData(); // wait for fetchLatestInvoices() to finish
  • 「リクエストウォーターフォール」は、前のリクエストの完了に依存する。
    各リクエストは、前のリクエストがデータを返した後にのみ開始できる。

Parallel data fetching
A common way to avoid waterfalls is to initiate all data requests at the same time - in parallel.

In JavaScript, you can use the Promise.all() or Promise.allSettled() functions to initiate all promises at the same time. For example, in data.ts, we're using Promise.all() in the fetchCardData() function:

/app/lib/data.js
export async function fetchCardData() {
  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,
    ]);
    // ...
  }
}

By using this pattern, you can:

Start executing all data fetches at the same time, which can lead to performance gains.
Use a native JavaScript pattern that can be applied to any library or framework.

  • 「リクエストウォーターフォール」を避ける一般的な方法は、すべてのデータリクエストを同時に並行して開始すること。Promise.all()Promise.allSettled()を使用して、すべてのプロミスを同時に開始できる。
  • Promise.all()は全てのリクエストを一通り実行する。
    Promise.allSettled()は一つ転けたら中断する。
このスクラップは2024/01/20にクローズされました