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

スポンサーリンク
「Next.jsでつくるフルスタックアプリwith TypeScript 前編」の感想・備忘録2の続き

itemディレクトリのTypeScript化

pages/api/item/readall.jsのtypeScript化

  • mv pages/api/item/readall.js pages/api/item/readall.ts
  • reqはNextApiRequestとする。
  • resはutils/types.tsでResReadAllTypeを定義し、NextApiResponse<ResReadAllType>とする。
  • findの戻り値はutils/types.tsにSavedItemDataType型を定義し、SavedItemDataType[]とする。
import type {NextApiRequest, NextApiResponse} from "next"; // 追加
import connectDB from '../../../utils/database'
import { ItemModel } from "../../../utils/schemaModel";
import {SavedItemDataType, ResReadAllType} from '../../../utils/types' // 追加

 // : NextApiResponse<ResReadAllType>を追加
const getAllItems = async (req: NextApiRequest, res: NextApiResponse<ResReadAllType>) => {
  try {
    await connectDB();
    // : SavedItemDataType[]を追加
    const allItems: SavedItemDataType[] = await ItemModel.find();
    return res.status(200).json({ msg: '取得成功', allItems: allItems })
  } catch (err) {
    return res.status(400).json({ msg: '取得失敗' })
  }
}
export default getAllItems;

utils/types.tsの修正

  • SavedItemDataType型、ResReadAllType型を追加
import type {NextApiRequest} from "next";
import {Types} from "mongoose";

// schemaModel.ts
export interface ItemDataType {
  title: string,
  image: string,
  price: string,
  description: string,
  email: string
}
export interface UserDataType {
  name: string,
  email: string,
  password: string,
}

// auth.ts
export interface DecodedType  {
  email: string
}

export interface ExtendedNextApiRequestAuth extends NextApiRequest {
  headers: {
    authorization: string
  },
  body: {
    email: string
  }
}

// register.ts, login.ts
export interface ExtendedNextApiRequestUser extends NextApiRequest {
  body: UserDataType
}

// login.ts
export interface SavedUserDataType extends UserDataType {
  _id: Types.ObjectId
}

// 追加
// readall.ts, [id].ts, update[id].ts, delete/[id].ts
export interface SavedItemDataType extends ItemDataType {
  _id: Types.ObjectId
}

// 追加
// readall.ts
export interface ResReadAllType {
  msg: string,
  allItems?: SavedItemDataType[]
}

// common
export interface ResMsgType {
  msg: string,
  token?: string
}

pages/api/item/create.jsのtypeScript化

  • mv pages/api/item/create.js pages/api/item/create.ts
  • resをNextApiResponse<ResMsgType>とする。
  • reqはtypes.tsに新たな型定義ExtendedNextApiRequestItemを追加する。
import type { NextApiResponse } from "next"; // 追加
import connectDB from '../../../utils/database';
import { ItemModel } from "../../../utils/schemaModel";
import auth from "../../../utils/auth";
import { ExtendedNextApiRequestItem, ResMsgType } from '../../../utils/types' // 追加

// : ExtendedNextApiRequestItemと: NextApiResponse<ResMsgType>追加
const createItem = async (req: ExtendedNextApiRequestItem, res: NextApiResponse<ResMsgType>) => {
  try {
    await connectDB();
    await ItemModel.create(req.body)
    return res.status(200).json({ msg: '作成しました。' })
  } catch (err) {
    return res.status(400).json({ msg: '失敗しました。' })
  }
}
export default auth(createItem);

utils/types.tsの修正

  • ExtendedNextApiRequestItem型を追加
・utils/types.ts
import type {NextApiRequest} from "next";
import {Types} from "mongoose";

// schemaModel.ts
export interface ItemDataType {
  title: string,
  image: string,
  price: string,
  description: string,
  email: string
}
export interface UserDataType {
  name: string,
  email: string,
  password: string,
}

// auth.ts
export interface DecodedType  {
  email: string
}

export interface ExtendedNextApiRequestAuth extends NextApiRequest {
  headers: {
    authorization: string
  },
  body: {
    email: string
  }
}

// register.ts, login.ts
export interface ExtendedNextApiRequestUser extends NextApiRequest {
  body: UserDataType
}

// login.ts
export interface SavedUserDataType extends UserDataType {
  _id: Types.ObjectId
}

// readall.ts, [id].ts, update[id].ts, delete/[id].ts
export interface SavedItemDataType extends ItemDataType {
  _id: Types.ObjectId
}

// readall.ts
export interface ResReadAllType {
  msg: string,
  allItems?: SavedItemDataType[]
}

// create.ts
export interface ExtendedNextApiRequestItem extends NextApiRequest {
  body: ItemDataType
}

// common
export interface ResMsgType {
  msg: string,
  token?: string
}

pages/api/item/[id].jsのtypeScript化

  • mv pages/api/item/[id].js pages/api/item/[id].ts
  • req, resはNextApiRequest型, NextApiResponse型を利用する。
  • レスポンスにはitemがあるのでtypes.tsに新たな型定義ResReadSingleTypeを追加する。
  • findByIdはnullを返すこともあるので、ユニオンを使ってSavedItemDataType | nullとし、レスポンスも分岐させる。
import type {NextApiRequest, NextApiResponse} from "next"; // 追加
import connectDB from '../../../utils/database'
import { ItemModel } from "../../../utils/schemaModel";
import { SavedItemDataType, ResReadSingleType } from '../../../utils/types' // 追加

// : NextApiRequestと: NextApiResponse<ResReadSingleType>追加
const getSingleItem = async (req: NextApiRequest, res: NextApiResponse<ResReadSingleType>) => {
  try {
    await connectDB();
    // : SavedItemDataType | null追加
    const item: SavedItemDataType | null = await ItemModel.findById(req.query.id);
    // 追加
    if (!item) {
      return res.status(400).json({msg: 'アイテムが存在しません'});
    }
    return res.status(200).json({msg: '取得成功', item: item});
  } catch (err) {
    return res.status(400).json({ msg: '取得失敗' })
  }
}
export default getSingleItem;

utils/types.tsの修正

  • ResReadSingleType型を追加
import type {NextApiRequest} from "next";
import {Types} from "mongoose";

// schemaModel.ts
export interface ItemDataType {
  title: string,
  image: string,
  price: string,
  description: string,
  email: string
}
export interface UserDataType {
  name: string,
  email: string,
  password: string,
}

// auth.ts
export interface DecodedType  {
  email: string
}

export interface ExtendedNextApiRequestAuth extends NextApiRequest {
  headers: {
    authorization: string
  },
  body: {
    email: string
  }
}

// register.ts, login.ts
export interface ExtendedNextApiRequestUser extends NextApiRequest {
  body: UserDataType
}

// login.ts
export interface SavedUserDataType extends UserDataType {
  _id: Types.ObjectId
}

// readall.ts, [id].ts, update[id].ts, delete/[id].ts
export interface SavedItemDataType extends ItemDataType {
  _id: Types.ObjectId
}

// readall.ts
export interface ResReadAllType {
  msg: string,
  allItems?: SavedItemDataType[]
}

// create.ts
export interface ExtendedNextApiRequestItem extends NextApiRequest {
  body: ItemDataType
}

// 追加
// [id].ts
export interface ResReadSingleType {
  msg: string,
  item?: SavedItemDataType
}

// common
export interface ResMsgType {
  msg: string,
  token?: string
}

pages/api/item/update/[id].jsのtypeScript化

  • mv pages/api/item/update/[id].js pages/api/item/update/[id].ts
  • レスポンスはResMsgTypeを使う。
  • それ以外の修正内容はpages/api/item/[id].tsと同じ。
import type {NextApiRequest, NextApiResponse} from "next"; // 追加
import connectDB from '../../../utils/database'
import { ItemModel } from "../../../utils/schemaModel";
import { SavedItemDataType, ResMsgType } from '../../../utils/types' // 追加

// : NextApiRequestと: NextApiResponse<ResMsgType>を追加
const getSingleItem = async (req: NextApiRequest, res: NextApiResponse<ResMsgType>) => {
  try {
    await connectDB();
    // : SavedItemDataType | nullを追加
    const item: SavedItemDataType | null = await ItemModel.findById(req.query.id);
    // 追加
    if (!item) {
      return res.status(400).json({msg: 'アイテムが存在しません'});
    }
    return res.status(200).json({msg: '取得成功', item: item});
  } catch (err) {
    return res.status(400).json({ msg: '取得失敗' })
  }
}
export default getSingleItem;

pages/api/item/delete/[id].jsのtypeScript化

  • mv pages/api/item/delete/[id].js pages/api/item/delete/[id].ts
  • 修正内容はpages/api/item/update/[id].tsと同じ。
import type {NextApiRequest, NextApiResponse} from "next"; // 追加
import connectDB from '../../../../utils/database'
import { ItemModel } from "../../../../utils/schemaModel";
import auth from "../../../../utils/auth";
import { SavedItemDataType, ResMsgType } from '../../../../utils/types' // 追加

// : NextApiRequestと: NextApiResponse<ResMsgType>を追加
const deleteItem = async (req: NextApiRequest, res: NextApiResponse<ResMsgType>) => {
  try {
    await connectDB();
    await ItemModel.deleteOne({_id: req.query.id});
    // : SavedItemDataType | nullを追加
    const singleItem: SavedItemDataType | null = await ItemModel.findById(req.query.id);
    // 追加
    if (!singleItem) {
      return res.status(400).json({ msg: 'アイテムが存在しません' });
    }
    if (singleItem.email === req.body.email) {
      return res.status(200).json({ msg: '削除成功' });
    } else {
      throw new Error();
    }
  } catch (err) {
    return res.status(400).json({ msg: '削除失敗' })
  }
}
export default auth(deleteItem);

コメント