点数
76点
感想
1〜5章は「Next.jsでつくるフルスタックアプリ前編」と全く同じ内容で、補章だけが追加されていた。
補章は「JavaScriptをTypeScriptに移行していく」という内容なので仕方がないことだが、少々分かりづらく退屈な内容だった。
環境構築
最初からTypeSciptを導入する場合
npx create-next-app --ts xxxxxxx
後からTypeScriptに移行する場合 ※書籍内ではこちらの方法
- touch tsconfig.jsonとして以下の内容を貼り付ける。
https://github.com/mod728/nextjs-book-full-stack-app-typescript/blob/backendts/tsconfig.json - tsconfig.json内でincludeされているnext-env.d.tsが存在しないので作成する。
(includeはTypeScriptを適用するファイルを指定するもの)
これによりReactがグローバルで使えるようになり、CSS Modulesが使えるようになる。
touch next-env.d.ts - TypeScriptおよびタイプ情報のパッケージをインストール
Next.jsのTypeScript化には以下のパッケージが必要。
※ nextとmongooseはタイプ情報が含まれているので@typeは不要
npm install --save-dev typescript @types/react @types/react-dom @types/node
書籍内ではjsonwebtokenも使っているので以下のパッケージも必要。
npm install --save-dev @types/jsonwebtoken
/// <reference types="next" />
/// <reference types="next/image-types/global" />
utilsディレクトリのTypeScript化
utils/schemaModel.jsのTypeScript化
- mv utils/schemaModel.js utils/schemaModel.ts
mongooseの型定義部分にTypeScriptの型定義を追加する。
import mongoose from 'mongoose'
const Schema = mongoose.Schema;
interface ItemDataType {
title: string,
image: string,
price: string,
description: string,
email: string
}
// 追加
interface UserDataType {
name: string,
email: string,
password: string,
}
// <ItemDataType>を追加
const ItemSchema = new Schema<ItemDataType>({
title: String,
image: String,
price: String,
description: String,
email: String
});
// <UserDataType>を追加
const UserSchema = new Schema<UserDataType>({
name: {
type: String,
required: true,
unique: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
});
// <ItemDataType>を追加
export const ItemModel = mongoose.models.Item || mongoose.model<ItemDataType>("Item", ItemSchema);
// <UserDataType>を追加
export const UserModel = mongoose.models.User || mongoose.model<UserDataType>("User", UserSchema);
utils/database.jsのTypeScript化
- mv utils/database.js utils/database.ts/
※ 型定義が必要な箇所はないため、ファイル名の変更のみ
utils/auth.jsのTypeScript化
- mv utils/auth.js utils/auth.ts
- req, resはNext.jsが用意しているNextApiRequest型, NextApiResponse型を利用する。
- さらに、リクエストにはヘッダにauthorization、ボディにemailがあるのでNextApiRequestを継承した型を定義する。
- ミドルウェアはpagesディレクトリ内の関数コンポーネントを引数として受け取るので、引数はFunction型とする。
- jwt.verufyの戻り値の型はstring | jwt.JwtPayloadであるため、型アサーションを使って変数の型を上書きする。
- NextApiResponseはジェネリックを使ってレスポンスの型を指定する
import type {NextApiRequest, NextApiResponse} from 'next'
import jwt from 'jsonwebtoken'
interface DecodedType {
email: string
}
interface ExtendedNextApiRequestAuth extends NextApiRequest {
headers: {
authorization: string
},
body: {
email: string
}
}
interface ResMsgType {
msg: string
}
const middleware = (handler: Function) => {
return async (req: ExtendedNextApiRequestAuth, res: NextApiResponse<ResMsgType>) => {
if (req.method === 'GET') {
return handler(req, res);
}
// JWTではトークンを「Bearer xxxxxxxxxx」とするのが慣習なのでスペースで分割する
const token = req.headers.authorization.split(' ')[1];
if (!token) {
return res.status(401).json({ msg: 'トークンがありません。' })
}
try {
const decoded = jwt.verify(token, 'hogehogehoge');
req.body.email = (decoded as DecodedType).email;
return handler(req, res);
} catch (err) {
return res.status(401).json({ msg: 'トークンが正しくありません。' })
}
};
};
export default middleware;
共通型定義の切り出し
utils/types.tsの作成
- touch utils/types.ts
utils/schemaModel.tsのItemDataTypeとUserDataTypeをこちらに移動してexportを付ける。
// exportを追加
export interface ItemDataType {
title: string,
image: string,
price: string,
description: string,
email: string
}
// exportを追加
export interface UserDataType {
name: string,
email: string,
password: string,
}
utils/schemaModel.tsの修正
- utils/schemaModel.tsにimportを追加。
import mongoose from 'mongoose'
// 追加
import {ItemDataType, UserDataType} from "./types";
const Schema = mongoose.Schema;
const ItemSchema = new Schema<ItemDataType>({
title: String,
image: String,
price: String,
description: String,
email: String
});
const UserSchema = new Schema<UserDataType>({
name: {
type: String,
required: true,
unique: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
});
export const ItemModel = mongoose.models.Item || mongoose.model<ItemDataType>("Item", ItemSchema);
export const UserModel = mongoose.models.User || mongoose.model<UserDataType>("User", UserSchema);