Next.jsの詳しい説明(React.js入門&実践)

Next.jsとは?
- ReactベースのフルスタックWebアプリケーションフレームワークです。
- ReactはクライアントサイドでサポートしますがNext.jsはサーバサイド機能もサポートします。
- Vue.jsベースの Nuxt.js とよく対比されます。
インストール
以下のオフィシャルサイトを参考にインストールします。
以下が、インストールを行う際に必要な条件です。
- Node.js 18.17 以上
- Windows (WSL) 、マックの場合はmacOS
以下は手動インストールの手順です。
$ mkdir myapp
$ cd myapp
$ npm install next@latest react@latest react-dom@latest typescript@latest @types/react @types/node
$ mkdir app
./package.json に下記を追加します。
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
:
./app/layout.tsx を作成します。
./app/layout.tsx
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="ja">
<body>{children}</body>
</html>
)
}
./app/page.tsx を作成します。
./app/page.tsx
export default function Page() {
return <h1>Top Page</h1>
}
開発用にサーバを起動します。
$ npm run dev
ブラウザから http://{サーバアドレス:3000/ にアクセスして「Top Page」が表示されれば成功です。
本番サーバを起動するには build してから start します。
$ npm run build
$ npm run start
フォルダ・ファイル構成
Next.js の一般的なフォルダ構成は以下の通りです。
myapp/ : プロジェクト名。好きな名前を命名。
app/ : App Routerを使用する場合のメインフォルダ
pages/ : Pages Routerを使用する場合のメインフォルダ
public/ : スタティックファイルを格納するフォルダ
src/ : オプショナルなアプリケーションソースを格納するフォルダ
myapp の下には下記などのファイルを置きます。
next.config.js Configuration file for Next.js
package.json Project dependencies and scripts
instrumentation.ts OpenTelemetry and Instrumentation file
middleware.ts Next.js request middleware
.env Environment variables
.env.local Local environment variables
.env.production Production environment variables
.env.development Development environment variables
.eslintrc.json Configuration file for ESLint
.gitignore Git files and folders to ignore
next-env.d.ts TypeScript declaration file for Next.js
tsconfig.json Configuration file for TypeScript
jsconfig.json Configuration file for JavaScript
./myapp/app の下には以下のようなファイルを置きます。
拡張子は JavaScript(.js), JavaScript XML(.jsx), TypeScript(.tsx) を意味します。ここでは主に .jsx を使用します。
layout .js .jsx .tsx Layout
template .js .jsx .tsx Re-rendered layout
page .js .jsx .tsx Page
loading .js .jsx .tsx Loading UI
not-found .js .jsx .tsx Not found UI
error .js .jsx .tsx Error UI
global-error .js .jsx .tsx Global error UI
route .js .ts API endpoint
default .js .jsx .tsx Parallel route fallback page
App Router と Pages Router
Next.js では App Router と Pages Router の2種類の方式をサポートしており、どちらを選ぶかによって開発方式が大きく異なります。
./app を使用するのが App Router で、
./pages を使用するのが Pages Router です。
App Router は Next.js v13.4 から導入された新しい開発方式で、
Pages Router の進化版とも言えることから、今後は App Router 方式が主流になると思われます。
ここでは App Router についてのみ説明していきます。
基本ファイル
ページ(page.tsx)
./app/{パス名}/page.tsx を作成すると、ブラウザから http://{サーバ名}/{パス名} でアクセスできます。
./app/{パス名1}/{パス名2}/page.tsx を作成すると、ブラウザから http://{サーバ名}/{パス名1}/{パス名2} でアクセスできます。
試しに ./app/page-test フォルダを作成して、配下に ./app/page-test/page.tsx を作成してみます。
./app/page-test/page.tsx
export default function Layout({
children,
}: {
children: React.ReactNode
}) {
return (
<>
<hr />
<div>{children}</div>
<hr />
</>
)
}
./app/layout-test/page.tsx
export default function Page() {
return <h1>Layout Test</h1>
}
http://~/layout-test を開いて Layout Test の上下に線が引かれていれば成功です。これは、http://~/layout-test/page1/page2 のように階層が深くなっても反映されます。
テンプレート(template.tsx)
テンプレート(Template)はレイアウト(Layout)とほぼ同じ機能を持ちます。レイアウトは画面が遷移してもDOM要素やステートが維持されるのに対し、テンプレートは遷移の度に新しいDOM要素が作成され、ステートがリセットされます。画面遷移時にレイアウトに埋め込んだフォームを初期化したい場合やアニメーションを表示したい場合など、レイアウトの代わりにテンプレートが使用されます。
./app/template-test/template.tsx
export default function Template({
children
}: {
children: React.ReactNode
}) {
return <div>{children}</div>
}
./app/template-test/page.tsx
export default function Page() {
return <h1>Template Test</h1>
}
http://~/template-test にアクセスして Template Test が表示されれば成功です。
ローディング(loading.tsx)
コンポーネントをローディング中に何か表示したい場合は loading.tsx を使用します。
./app/loading-test/loading.tsx
export default function Template({
children
}: {
children: React.ReactNode
}) {
return <div>{children}</div>
}
./app/template-test/page.tsx
export default function Page() {
return <h1>Template Test</h1>
}
http://~/template-test にアクセスして Template Test が表示されれば成功です。
ローディング(loading.tsx)
コンポーネントをローディング中に何か表示したい場合は loading.tsx を使用します。
./app/loading-test/loading.tsx
export default function Loading() {
return <div>Loading...</div>
}
時間のかかるコンポーネント WaitComponent を作成します。
./app/loading-test/wait.tsx
function aWait() {
return new Promise(callback => { setTimeout(callback, 3000) })
}
export default async function WaitComponent() {
await aWait()
return <div>Finished!</div>
}
時間のかかるコンポーネントを <Suspense>~</Suspense> で囲み、fallback に Loading を指定します。<Suspense> を使用することにより、時間のかかる処理が終わるまでページ全体のレンダリングが待たされることなく、個々の <Suspense> を個別に非同期に呼び出すことができます。また、読み込み中は fallback で指定したローディング部品を表示します。
./app/loading-test/page.tsx
import { Suspense } from 'react'
import Loading from './loading'
import WaitComponent from './wait'
export default function Page() {
return (
<div>
<h1>Loading Test</h1>
<Suspense fallback={<Loading />}>
<WaitComponent />
</Suspense>
</div>
)
}
http://~/loading-test にアクセスすると数秒間 Loading… が表示されて Finished! に変われば成功です。
Not Foundページ(not-found.tsx)
./app 直下に not-found.tsx を作成すると、404 Not Found エラーが発生した際のページを作成することができます。
./app/not-found.tsx
export default function NotFound() {
return <h1>Not Found</h1>
}
存在しないページにアクセスして Not Found が表示されれば成功です。
エラーページ(error.tsx)
下記のようなエラーページを ./app/error-test/error.tsx として用意します。
./app/error-test/error.tsx
'use client'
import { useEffect } from 'react'
export default function Error({error, reset,}: {
error: Error & { digest?: string }
reset: () => void
}) {
useEffect(() => {console.error(error)}, [error])
return (
<>
<h1>Error</h1>
<button onClick={() => reset()}>Try again</button>
</>
)
}
./app/error-test/page.tsx の中でエラーを発生させてみます。
./app/error-test/page.tsx
export default function Page({params, searchParams}) {
const error = searchParams?.error
if (error) {
throw new Error('ERROR!!!!!')
}
return <h1>Error Test</h1>
}
http://~/error-test?error=1 にアクセスしてエラーページが表示されれば成功です。
グローバルエラー(global-error.tsx)
error.tsx では page.tsx で発生したエラーを捕捉することはできますが、layout.tsx や template.tsx で発生したエラーを捕捉することができません。これらを捕捉するには global-error.tsx を作成します。
./app/global-error.tsx
'use client'
export default function GlobalError({
error,
reset,
}: {
error: Error & { digest?: string }
reset: () => void
}) {
return (
<html>
<body>
<h2>Global Error</h2>
<div>{error.message}</div>
<button onClick={() => reset()}>Try again</button>
</body>
</html>
)
}
ルーティング定義(route.ts)
REST-API を作成する際などに作成します。詳細は API を参照してください。
スタイリング
CSSモジュール
グローバルのCSSを設定するには下記の様にします。
./app/global.css
body {
background-color: #ddf;
}
./app/layout.tsx
import './global.css'
:
クラス名を指定したCSSを適用するには下記の様にします。
./app/css-test/test1.module.css
.test1 { color: red; }
./app/css-test/test2.module.css
.test2 { color: blue; }
./app/css-test/page.tsx
import style1 from './test1.module.css'
import style2 from './test2.module.css'
export default function Page() {
return (
<>
<h1>CSS Test</h1>
<div className={style1.test1}>Test1 message.</div>
<div className={style2.test2}>Test2 message.</div>
</>
)
}
http://~/css-test を開いて背景が薄青く、赤いメッセージと青いメッセージが表示されていれば成功です。
Tailwind CSS
Tailwind CSS を使用することもできます。
Tailwind CSS を使用する場合は、すてのデフォルトスタイルがキャンセルされるため、すべての部品のスタイルを再構築する必要があります。
$ npm install -D tailwindcss postcss autoprefixer
$ npx tailwindcss init -p
./tailwind.config.js に下記を追加します。
./tailwind.config.js
module.exports = {
content: [ './app/**/*.{js,ts,jsx,tsx,mdx}' ],
theme: {
extend: {},
},
plugins: [],
}
./app/tailwind-test/style.css を作成します。
./app/tailwind-test/style.css
@tailwind base;
@tailwind components;
@tailwind utilities;
これを ./app/tailwind-test/page.tsx から読み込みます。
./app/tailwind-test/page.tsx
import './style.css'
export default function Page() {
return <h1 className="text-6xl font-bold text-blue-600">Tailwind Test</h1>
}
http://~/tailwind-test を開いて Tailwind Test の文字が青くなっていれば成功です。反映されない場合は簡易サーバ(npm run dev)を再起動してください。
Sass
スタイルシートとして Sass を使用することもできます。
$ npm install --save-dev sass
./app/sass-test/style.scss
$body-background-color: #dfd;
body {
background-color: $body-background-color;
}
./app/sass-test/page.tsx
import './style.scss'
export default function Page() {
return <h1>Sass Test</h1>
}
http://~/sass-test にアクセスして背景が緑になったら成功です。
フォルダ名
プライベートフォルダ(_xxx)
./app 配下にディレクトリを作成しても page.tsx や route.ts を配置しない限りはアクセスされることはありませんが、アンダーバー(_)で始まるフォルダはプライベートフォルダと認識され、たとえ page.tsx や route.ts が配置されてもアクセスされることはありません。
./app/
xxx/page.tsx
yyy/route.ts
_lib/page.tsx # アクセスされない
ルートグループ((xxx))
./app/(…) の名前を持つフォルダはルートグループを形成します。
例えば下記のフォルダ構成では、member1~member3 は一つの group1 グループに含まれますが、http://~/member1, http://~/member2, http://~/member3 としてアクセスすることができます。グループはレイアウトやテンプレートを共有することができます。
./app/
(gorup1)/
layout.tsz
member1/page.tsx
member2/page.tsx
member3/page.tsx
./app/(group1)/layout.tsx
export default function Layout({
children,
}: {
children: React.ReactNode
}) {
return (
<>
<div>(group1)</div>
<div>{children}</div>
</>
)
}
./app/(group1)/member1/page.tsx
export default function Page() {
return <h1>Member 3</h1>
}
http://~/member1 にアクセスして (group1) と Member 1 が表示されれば成功です。
ダイナミックルーティング([xxx])
http://~/blog/{slug} の様に URL でブログIDなどのパラメータを受け取りたい場合があります。
./app/blog/[slug] という名前のフォルダを作成します。
$ mkdir -p './app/blog/[slug]'
./app/blog/[slug]/page.tsx ファイルを作成します。
./app/blog/[slug]/page.tsx
export default function Page({ params }: { params: { slug: string } }) {
return <h1>Blog: {params.slug}</h1>
}
http://~/blog/123 にアクセスして Blog: 123 と表示されれば成功です。
ダイナミックルーティング:階層化([…xxx])
http://~/blog2/{slug1}/{slug2}/{slug3} のように階層的なパラメータを受け取るには、./app/blog2/[…slugs]/page.tsx ファイルを作成します。
$ mkdir -p './app/blog2/[...slugs]'
./app/blog2/[…slugs]/page.tsx
export default function Page({ params }: { params: { slugs: string[] } }) {
return <h1>Blog: {params.slugs.join(', ')}</h1>
}
http://~/blog2/123/456 にアクセスして Blog: 123, 456 と表示されれば成功です。
ダイナミックルーティング:パラメータ無し対応([[…xxx]])
上記の例では /blog2/123 や /blog2/456 にはアクセスできても /blog2 にアクセスすると 404 Not Found となってしまいます。/blog2 にもアクセスできるようにするには、[[…slugs]] の様に括弧を二重にします。下記の例では /blog3 にアクセスすると (None) を表示します。
$ mkdir -p './app/blog3/[[...slugs]]'
./app/blog3/[[…slugs]]/page.tsx
export default function Page({ params }: { params: { slugs: string[] } }) {
if (params.slugs) {
return <h1>Blog: {params.slugs.join(', ')}</h1>
} else {
return <h1>Blog: (None)</h1>
}
}
http://~/blog3 にアクセスして Blog: (Node) が、http://~/blog3/123/456 にアクセスして Blog: 123, 456 と表示されれば成功です。
ダイナミックルーティング:複数パラメータ([xxx]/[yyy])
http://~/blog4/{カテゴリ}/{slug} を受け取るには ./app/blog4/[category]/[slug]/page.tsx ファイルを作成します。
$ mkdir -p './app/blog4/[category]/[slug]'
./app/blog4/[category]/[slug]/page.tsx
export default function Page({ params }: { params: { category: string, slug: string } }) {
return <h1>Blog: {params.category} / {params.slug}</h1>
}
http://~/blog4/movie/123 にアクセスして Blog: movie / 123 と表示されれば成功です。
平行ルーティング(@xxx)
Next.js v13 からサポートされた機能で、ひとつのレイアウトの中で複数の子ノードを並列に描画することができます。時間のかかる子ノードを複数同時に描画したい場合、子ノードを @ で始まるフォルダ名で作成します。
./app/
parallel-test/
layout.tsx
page.tsx
@node1/page.tsx
@node2/page.tsx
./app/parallel-test/layout.tsx
export default function Layout(props) {
return (
<div>
<div>{props.children}</div>
<div>{props.node1}</div>
<div>{props.node2}</div>
</div>
)
}
./app/parallel-test/page.tsx
export default function Page() {
return <h1>Parallel Test</h1>
}
./app/parallel-test/@node1/page.tsx
export default function Page() {
return <h2>Node1</h2>
}
./app/parallel-test/@node2/page.tsx
export default function Page() {
return <h2>Node2</h2>
}
http://~/parallel-test にアクセスして Parallel Test, Node1, Node2 が表示されていれば成功です。
横取りルーティング((.)xxx, (..)xxx, (…)xxx)
Next.js 13.3 からサポートされた機能で、現在のページの上に、別のページをモーダルで表示することができます。写真をクリックするとその写真がモーダルで拡大表示されるケースなどで利用されます。少々コード量が多いので本家が提供しているサンプルを紹介します。
ソースサンプルは下記のファイルから構成されます。
./app/layout.tsx
./app/page.tsx
./app/default.tsx
./app/global.css
./app/photos/[id]/page.tsx
./app/@modal/default.tsx
./app/@modal/(.)photos/[id]/modal.tsx
./app/@modal/(.)photos/[id]/page.tsx
(.) などは下記の意味を持ちます。
(.) 同一階層のセグメントにマッチ
(..) ひとつ上のディレクトリのセグメントにマッチ
(..)(..) ふたつ上のディレクトリのセグメントにマッチ
(...) ルート(./app)ディレクトリにマッチ
コンポーネント(Components)
オリジナルコンポーネント(Original Component)
React なので部品をコンポーネント化することができます。テストメッセージを表示する部品を作成してみます。ここらへんの詳細は React を参照してください。
./app/component-test/testMessage.tsx
export default function TestMessage() {
return <div>Test message.</div>
}
./app/component-test/page.tsx
import TestMessage from './testMessage'
export default function Page() {
return (
<>
<h1>Component Test</h1>
<TestMessage />
</>
)
}
http://~/component-test を開いて Test message. と表示されていれば成功です。
リンク(Link)
Page Test へのリンクを張ってみます。<a> で張る方法と <Link> で張る方法があります。
./app/link-test/page.tsx
import Link from 'next/link'
export default function Page() {
return (
<>
<h1>Link Test</h1>
<ul>
<li><a href="page-test">Page Test</a></li>
<li><Link href="page-test">Page Test</Link></li>
</ul>
</>
)
}
http://~/link-test を表示して Page Test へのリンクが2つ表示されていれば成功です。<a> で張った場合は遷移時に画面の再読込が行われるのに対して、<Link> で張った場合は再読み込み無しに描画されます。
リンク(useRouter())
useRouter() を用いることで、ユーザクリック時ではなく、プログラム中から任意のタイミングでリンク先にジャンプすることができます。
./app/use-router-test/page.tsx
'use client'
import { useRouter } from 'next/navigation'
export default function Page() {
const router = useRouter()
return (
<div>
<h1>useRouter Test</h1>
<button onClick={() => router.push('/')}>Top</button>
</div>
)
}
http://~/use-route-test にアクセスしてボタンをクリックすると Top ページに遷移すれば成功です。
フォント(Font)
Googleフォントを利用することができます。Google の Roboto フォントを使用する例を下記に示します。指定可能なパラメータは Font を参照してください。
./app/font-test/page.tsx
import { Roboto } from 'next/font/google'
const Roboto900 = Roboto({ weight:'900', preload:false })
export default function Page() {
return (
<>
<h1>Google Roboto Font Test</h1>
<h1 className={Roboto900.className}>Google Roboto Font Test</h1>
</>
)
}
http://~/font-test にアクセスしてフォントの異なるタイトルが表示されていれば成功です。
イメージ(Image)
イメージコンポーネント <Image> を使用することができます。指定可能なパラメータは <Image> を参照してください。<img> とあまり変わらないように見えますが、配置されるサイズに応じて適切なサイズにトリミングしてくれます。
./app/image-test/page.tsx
import Image from 'next/image'
export default function Page() {
return (
<>
<h1>Image Test</h1>
<Image src="/image/sample.png" width={58} height={40} alt="Sample Image" />
</>
)
}
./public/image/sample.png に任意の PNGファイルを配置してください。http://~/image-test にアクセスすると画像が表示されれば成功です。
パス参照
絶対パスインポート(Absolute imports)
./components/button.tsx 部品を作成し、これを ./app/*/page.tsx から import する場合、’../../components/button’ と記述する必要がありますが、tsconfig.json または jsconfig.json に baseUrl を指定すると、’components/button’ として参照できるようになります。npm run dev を再起動する必要があります。
./tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
:
./components/button.tsx
export default function Button() {
return <button>Click me</button>
}
./app/absolute-import-test/page.tsx
import Button from 'components/button'
export default function Page() {
return (
<>
<h1>Absolute Import Test</h1>
<Button />
</>
)
}
コンフィグを書き換えたので npm run dev を再起動した後、http://~/absolute-import-test にアクセスし、Click me ボタンが表示されれば成功です。
モジュールエイリアス(Module aliases)
./tsconfig.json または ./jsconfig.json に paths を指定するとパス名のエイリアスを使用することができるようになります。
./tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": { "@alias1/*": ["components/*"] },
:
./app/module-alias-test/page.tsx
import Button from '@alias1/button'
export default function Page() {
return (
<>
<h1>Module Alias Test</h1>
<Button />
</>
)
}
コンフィグを書き換えたので npm run dev を再起動した後、http://~/module-alias-test にアクセスし、Click me ボタンが表示されれば成功です。
データ操作
フェッチ(Fetch)
fetch()を用いて他のWebにアクセスすることができます。fetch() に関する詳細は fetch() を参照してください。
./app/fetch-test/page.tsx
export default async function Page() {
const response = await fetch('https://www.tohoho-web.com/cgi/client-ip-address.cgi')
const body = await response.text()
return (
<>
<h1>Fetch Test</h1>
<div>{body}</div>
</>
)
}
http://~/fetch-test にアクセスし、IPアドレスが表示されていれば成功です。
サーバサイドコンポーネント(Server Side Component)
Next.js では簡単にサーバサイドの実行とクライアントサイドの実行を切り替えることができます。デフォルトはサーバサイドです。
./app/server-side-test/page.tsx
export default function Page() {
console.log('Server Side Test')
return <h1>Server Side Test</h1>
}
http://~/server-side-test にアクセスするとサーバ側のコンソールに Server Side Test が表示されます。
クライアントサイドコンポーネント(Client Side Component)
‘use client’ を指定するとクライアントサイドになります。
./app/client-side-test/page.tsx
'use client'
export default function Page() {
console.log('Client Side Test')
return <h1>Client Side Test</h1>
}
http://~/client-side-test にアクセスするとクライアント側の開発ツールのコンソールに Client Side Test が表示されます。
サーバアクション(Server action)
また、下記の様に ‘use server’ を指定したサーバサイドアクションを、クライアント側のフォームから簡単に呼び出すことができます。下記の例ではクライアントのフォームに入力したメッセージがサーバに送られ、サーバのコンソールに出力されます。
./app/server-action-test/page.tsx
export default function Page() {
async function action_test(form) {
'use server'
const message = form.get('message')
console.log({message})
}
return (
<>
<h1>Server Action Test</h1>
<form action={action_test}>
<input type="text" name="message" />
<button type="submit">OK</button>
</form>
</>
)
}
http://~/server-action-test にアクセスしてフォームに入力して [OK] を押すと、サーバ側にメッセージが表示されれば成功です。
デフォルト値(defaultValue)
フォーム部品にデフォルト値を設定するには value=”…” ではなく defaultValue=”…” を指定します。
<input type="text" name="message" defaultValue="xxx" />
フォームとアクション(Form & Action)
サーバで処理した結果を返すには useFormState() を用います。クライアントでフォームを表示し、サブミット時にサーバアクションを呼び出します。今回は常に成功するメソッドとしていますが、結果を Form State を経由して画面に返却しています。
./app/login-test/login.tsx
'use server'
export async function loginAction(state: any, formData: FormData) {
const user_id = formData.get('user_id')
const password = formData.get('password')
console.log({user_id, password})
return {
message: 'OK',
}
}
./app/login-test/page.tsx
'use client'
import { useFormState } from 'react-dom'
import { loginAction } from './login'
const initialState = { message: '' }
export default function Page() {
const [state, formAction] = useFormState(loginAction, initialState)
return (
<>
<h1>Login Test</h1>
<form action={formAction}>
<div><input type="text" name="user_id" placeholder="User ID" /></div>
<div><input type="password" name="password" placeholder="Password" /></div>
<button type="submit">Login</button>
<p>{state?.message}</p>
</form>
</>
)
}
http://~/login-test にアクセスして、User ID と Password に何かを入力して Login ボタンを押し、OK と表示されれば成功です。
API
ルーティング(route.ts)
route.ts は REST-API を作成するのに便利な機能です。下記の内容で ./app/api/books/route.ts を作成します。
./app/api/books/route.ts
var books = [
{ title: '吾輩は猫である', author: '夏目漱石' },
]
export async function GET(request: Request) {
return Response.json({ books: books }, { status: 200 })
}
export async function POST(request: Request) {
var book = await request.json()
books.push({ title: book.title, author: book.author })
return Response.json({ result: 'OK' }, { status: 200 })
}
curl でこれらを呼び出してみます。
$ curl http://localhost:3000/api/books
{"books":[{"title":"吾輩は猫である","author":"夏目漱石"}]}
$ curl -X POST http://localhost:3000/api/books -d '{"title":"銀河鉄道の夜","author":"宮沢賢治"}'
{"result":"OK"}
$ curl http://localhost:3000/api/books
{"books":[{"title":"吾輩は猫である","author":"夏目漱石"},{"title":"銀河鉄道の夜","author":"宮沢賢治"}]}
上記の例では入出力に JavaScript 標準の Response/Response を使用していますが、Next.js で拡張した NextRequest/NextResponse を使用することもできます。
ヘッダー(Headers)
リクエストヘッダを読み込むには headers() を使用します。レスポンスヘッダを指定するには Response() の第2引数オブジェクトに headers を指定します。詳細は headers を参照してください。
./app/api/header-test/route.ts
import { headers } from 'next/headers'
export async function GET(request: Request) {
const user_agent = headers().get('User-Agent')
return new Response('OK', {status: 200, headers: {'X-Test-Header': user_agent}})
}
curl で http://~/api/header-test を呼び出して、X-Test-Header が返却されれば成功です。
$ curl -i http://localhost:3000/api/header-test
HTTP/1.1 200 OK
x-test-header: curl/7.76.1
:
クッキー(Cookies)
Cookie を参照するには cookies() から get() して value を参照します。設定するには Response() の第2引数オブジェクトの headers に Set-Cookie を指定します。詳細は cookies を参照してください。
./app/api/cookie-test/route.ts
import { cookies } from 'next/headers'
export async function GET(request: Request) {
var my_cookie = cookies().get('my_cookie')
var my_cookie_value = my_cookie ? my_cookie.value : 'DUMMY_DATA'
return new Response('OK', {status: 200, headers: {'Set-Cookie': `my_cookie=${my_cookie_value}`}})
}
http://~/api/cookie-test を呼び出して Set-Cookie が設定されていれば成功です。
$ curl -i -H "Cookie: my_cookie=AAA" http://localhost:3000/api/cookie-test
HTTP/1.1 200 OK
set-cookie: my_cookie=AAA
:
クエリーパラメータ(Query parameters)
https://~/~?query=hello のようなURL中のクエリパラメータを得るには NextRequest.nextUrl.searchParams から get() します。
./app/api/query-test/route.ts
import { type NextRequest } from 'next/server'
export function GET(request: NextRequest) {
const param1 = request.nextUrl.searchParams.get('param1')
console.log({param1})
return Response.json({ result: 'OK' })
}
http://~/api/query-test?param1=Hello にアクセスしてサーバ側のコンソールに { param1: ‘Hello’ } と表示されれば成功です。
フォームデータ(Form data)
<form method=”POST”> で送信されるフォームデータを参照するには formData() を await して get() します。
./app/api/form-test/route.ts
export async function POST(request: Request) {
const formData = await request.formData()
const name = formData.get('name')
const email = formData.get('email')
return Response.json({ name, email })
}
http://~/api/form-test を下記の様に呼び出して下記の様に表示されれば成功です。
$ curl -X POST http://localhost:3000/api/form-test -d 'name=Yamada&email=yamada@example.com'
{"name":"Yamada","email":"yamada@example.com"}
CORS
画面とAPIサーバが異なるオリジンの場合、CORS を設定する必要があります。 ヘッダで Access-Control-Allow-* を返却します。
./app/api/cors-test/route.ts
export async function GET(request: Request) {
return Response.json({ result: 'OK' }, {
status: 200,
headers: {
'Access-Control-Allow-Origin': 'https://www.example.com/',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
})
}
その他
クエリーパラメータ(searchParams)
https://~/~?param1=hello のようなURL中のクエリパラメータを得るには searchParams を参照します。
./app/query-test/page.tsx
import { useSearchParams } from 'next/navigation'
export default function Page({params, searchParams}) {
const param1 = searchParams?.param1
return (
<>
<h1>Query Test</h1>
<div>{param1}</div>
</>
)
}
http://~/query-test?param1=Hello にアクセスして Hello が表示されれば成功です。
リダイレクト(Redirect)
リダイレクトするには redirect() を使用します。
./app/redirect-test/page.tsx
import { redirect } from 'next/navigation'
export default function Page() {
redirect('https://www.google.com/')
}
http://~/redirect-test にアクセスして Google にリダイレクトされれば成功です。
メタデータ(Metadate)
メタデータとしてタイトルを設定してみます。
./app/layout.tsx
import './global.scss'
import { Metadata } from 'next'
export const metadata: Metadata = {
title: 'NEXT.JS',
}
export default function RootLayout({
:
http://~/ を開いてブラウザのタブに NEXT.JS と表示されていれば成功です。
ミドルウェア(Middleware)
./middleware.ts には、Next.js が page.tsx を呼び出す前に行う共通処理を記述できます。リクエストのロギングや認証チェックなどに利用できます。
./middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
console.log(request.url)
return NextResponse.redirect(new URL('/', request.url))
}
export const config = {
matcher: '/middleware-test/:path*',
}
http://~/middleware-test にアクセスして / にリダイレクトすれば成功です。
config.matcher にはミドルウェアを適用するパスを指定します。複数指定や正規表現も使用できます。:path* は任意のパス名を示します。
export const config = {
matcher: ['/mid-test1/:path*', '/(mid-test2|mid-test3)/:path*'],
}
redirect() は別のパスにリダイレクトします。rewrite() はURL表示はそのままで実行内容のみを転送します。
export function middleware(request: NextRequest) {
if (request.nextUrl.pathname.startsWith('/mid-test1')) {
return NextResponse.redirect(new URL('/dashboard', request.url))
}
if (request.nextUrl.pathname.startsWith('/mid-test2')) {
return NextResponse.rewrite(new URL('/dashboard', request.url))
}
}