NextAuth.jsは何をしているのだ?
NextAuth.jsはとても便利らしいのだ。
なので試しに使ってみたのだ。
そしたら魔法みたいなコードだったのだ。すごかったのだ。
ただ、あまりにも魔法コードすぎて「中身はどうなってるのだ」と気になったので調べたのだ。
気になった部分
自分が気になったのは、おもに2つなのだ。
[...nextauth]/route.ts
←この書き方はなんなのだ?🤔
このよく分からない書き方は、Next.jsのdynamic routeという機能なのだ。
なので、NextAuth.jsとは無関係なのだ。
以下のスクショを見ると、イメージしやすいと思うのだ👇
つまり、この書き方をすることで「ここから先は全部NextAuthに任せるぅ!」ができるらしいのだ👇
import NextAuth from "next-auth/next";
import { config } from "auth";
const handler = NextAuth(config);
export { handler as GET, handler as POST };
useSession()
←なんでこれでセッションが取れるのだ?🤔
これがこの記事を書くキッカケなのだ。
なんでコンポーネントの中でuseSession()
って書くだけでセッションが取れるのだ?👇
export default function Header() {
const { data: session } = useSession()
....
謎すぎるのだ。
というのもぼくの認識では、セッションというのは以下のようなものなのだ👇
- クライアント側が「僕のIDは1なのだ」なcookieをサーバーに投げる
(もしくはhttpのauthorizationヘッダーにトークンをくっつけたりする) - サーバー側は、DBとかに保存してるセッションのリストを見て「こやつがIDが1のやつなのだ」と特定する
この一連の「こやつなのだ」と特定する流れをセッションを呼ぶこともあるし、「セッションのリスト」のことをセッションと呼ぶこともある・・・という認識なのだ。
つまり、useSession
で取得できる「セッション」というのは「こやつなのだ」と特定するため情報を指すと思うのだ。だけどそれは本来サーバー側にあるものだと思うのだ。なのでuseSession()
と書くだけでセッションの中身が取れるのは意味不明なのだ。
そんなわけでuseSession()
の中身を見てみたのだ。
そしたら以下のような感じのコードが書いてあったのだ👇
const value: SessionContextValue<R> = React.useContext(SessionContext)
if (!value && process.env.NODE_ENV !== "production") {
throw new Error(
"[next-auth]: `useSession` must be wrapped in a <SessionProvider />"
)
}
まず「SessionContext
の中身から、sessionを取ってくるのだ」な処理をしていたのだ。
なのでSessionContext
を提供しているSessionProvider
の中身を見てみたのだ👇
...
const [session, setSession] = React.useState(() => {
if (hasInitialSession) __NEXTAUTH._session = props.session
return props.session
})
...
__NEXTAUTH._getSession = async ({ event } = {}) => {
try {
const storageEvent = event === "storage"
if (storageEvent || __NEXTAUTH._session === undefined) {
__NEXTAUTH._lastSync = now()
__NEXTAUTH._session = await getSession({
broadcast: !storageEvent,
})
setSession(__NEXTAUTH._session)
return
}
...
ざっと見た感じ「session
というstateを用意して、getSession
という関数でsessionを取得する」みたいなことをしているのだ。
さらにgetSession
の中身を見てみると、以下のような感じになっていたのだ👇
export async function getSession(params?: GetSessionParams) {
const session = await fetchData<Session>(
"session",
__NEXTAUTH,
logger,
params
)
....
return session
}
fetchData
というのは、fetch関数をラップしただけの関数だったのだ。
そんなわけで中身を読み解くと、getSession
関数は
/sessionというパスにGETリクエストを投げるだけの関数
だったのだ。
ネットワークタブを開いてみると、たしかにページを開いただけで/session
にアクセスされているのだ👇
魔法のようなコードの裏側は、じつは泥臭かったのだ。
開発者の人、ありがとうなのだ。
useSession()
は効率が悪いのだ
useSession()
の仕組みを知ってから思ったのだが、useSession()
は効率が悪いのだ。
というのもNext13のApp Routerではすべてのコンポーネントがサーバーコンポーネントになったのだ。だからサーバーコンポーネントの場合は最初からサーバー側で「こやつなのだ」と特定してからコンポーネントを生成したほうが良いのだ。
useSession()
を使うと無駄にfetchが走ってしまうのだ。だれも得しないのだ。
というかuseSession()
はクライアント側の機能(Contextやstate)を使っているので、そもそもサーバーコンポーネントでは使えないのだ。
なので、サーバーコンポーネントの中でセッションを使いたい場合は、getServerSession
という関数を使うのだ。
以下みたいな感じなのだ👇
import { getServerSession } from "next-auth/next"
import { authOptions } from "pages/api/auth/[...nextauth]"
export default async function Page() {
const session = await getServerSession(authOptions)
return <pre>{JSON.stringify(session, null, 2)}</pre>
}
//引用:https://next-auth.js.org/configuration/nextjs
今後はこの書き方が主流になると思うのだ。
おまけ)NextAuth.js → Auth.js
名前が「Auth.js」に変わるらしいのだ。
Next.jsに限らず、いろいろなフレームワークや、素のJSにも対応するらしいのだ。
すごいのだ。これはとてもよいものなのだ。みんなも応援するのだ。
おわり
Discussion
語尾変換良きですね!
なのだっていうだけで、脳内でずんだもんボイスが聞こえてきますね!
コードが読み切れていないのですが、
<SessionProvider session={session}>
みたいにしてサーバー側でgetServerSessionの値をSessionProviderに渡したら、__NEXTAUTH._sessionがundefinedではなくなりuseSession()してもサーバー側にfetch走らない雰囲気ありそうですplaceofさん、補足ありがとうなのだ。
たしかにその部分を見ると、そんな感じがするのだ。
でもコードを最後まで追うと、「最後に絶対getSessionするマン」になってるのだ👇
コード引用元
僕はもうよく分からないのだ。
なのでApp Routerのドキュメントが整備されるまで待つことにするのだ😭
私は今のところこうしてます。
sessionを取るラップ
用途が違う中、サーバーサイドの処理と比較して"効率が悪い"とするのは不適切かなと。
文中にもありますが、クライアント向けに提供されている機能なので、httpOnlyなcookieの有効性をfetchで確認していること自体に違和感は感じませんでした。
"useSession()は効率が悪い"という見出しだと読み手に誤解を与えそうだなと思ったのですみません...🙇♂️