「Next.jsでつくるフルスタックアプリwith TypeScript 前編」の感想・備忘録1

スポンサーリンク

点数

76点

感想

1〜5章は「Next.jsでつくるフルスタックアプリ前編」と全く同じ内容で、補章だけが追加されていた。

1〜5章はこちらと同じ内容

補章は「JavaScriptをTypeScriptに移行していく」という内容なので仕方がないことだが、少々分かりづらく退屈な内容だった。

環境構築

最初からTypeSciptを導入する場合

npx create-next-app --ts xxxxxxx

後からTypeScriptに移行する場合 ※書籍内ではこちらの方法

  1. touch tsconfig.jsonとして以下の内容を貼り付ける。
    https://github.com/mod728/nextjs-book-full-stack-app-typescript/blob/backendts/tsconfig.json
  2. tsconfig.json内でincludeされているnext-env.d.tsが存在しないので作成する。
    (includeはTypeScriptを適用するファイルを指定するもの)
    これによりReactがグローバルで使えるようになり、CSS Modulesが使えるようになる。
    touch next-env.d.ts
  3. 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);

コメント