🌐

祗園祭HPを支えるAPIを作った

2023/12/25に公開

この記事は木更津高専 Advent Calendar 2023 25日目の記事です。

これまでの祗園祭Webサイトシリーズ↓
学園祭の企画管理システム(一部)と認証システムを作った話 by @naotiki
祇園祭WebサイトのDBについて by @NXVZBGBFBEN
祇園祭Webサイトのフロントについて by @nairoki

はじめに

木更津高専プログラミング研究同好会(以下:プロ研)所属のこかすた〜です。
プロ研では、木更津高専の学園祭である祗園祭のホームページを作りました(※見る時期によってはすでに閉鎖されている可能性があります)。

この記事では、僕が担当したAPI部分について書きます。

作ったAPIについて

作ったAPIを全部あげてみます

  • 条件に合う企画一覧
  • 企画のステータスを変更
  • お知らせ一覧
  • お知らせ作成
  • 企画のラベル変更
  • 編集権限を持つ企画を返す
  • 隠し要素

とまぁ、こんな感じの部分を作りました。
高専ロボコンと時期がかぶり、朝7時に家を出て19時半に帰ってきてその後ロボコンの仕事もやる生活をしてたので、他のメンバーよりできたことは少ないです。他の3人に感謝です。。

APIの基本的な構造について

src/pages/apiにいろいろなAPIを作成していきました。各企画の状態を返したり変更したりするAPI[1]を例に解説していきます。
まずは、状態を返す場合はGETでAPIを叩き、状態を変更する場合はPOSTでAPIを叩き、それ以外の場合は405を出すようにしました。

export default async function handler(
    req: NextApiRequest,
    res: NextApiResponse
) {
    const method = req.method;
    if (method === 'POST') {
        //POSTだったときの処理 = 状態を変更する
    }
    else if (method === 'GET') {
        //GETだったときの処理 = 状態を返す
    }
    else{
        res.status(405).json({
            success: false,
            message: "405 Method Not Allowed",
        });
    }
}

そして、この中にAPIの処理を書いていきます。
なお、POSTするときのjsonの仕様は以下のとおりにしました。

{
  "projectId": "企画id",
  "name": "状態を表すステータス(OK or Limited or Suspended or その他)"
}

認証情報を確認してDBを変更する

まずは、naotiki君が作った認証システムを使って認証してない人を弾きます。ついでに、不正なjson(要素が不足している & projectIdが数字でない)も弾きます。

const session/*:Session | null*/ = await getServerSession(req, res, authOptions);
if (!session) {
    res.status(401).json({
        success: false,
        message: "401 Unauthorized",
    });
    return;
}
const {projectId, name} = req.body;
if(!projectId || !name || isNaN(projectId)){
    res.status(400).json({
        success: false,
        message: "400 Bad Request",
    });
    return;
}

次に、adminでなかった場合は所属を確認します。存在しない企画だった場合は400を、企画の管理者として追加されていない場合は401を返します。

if(session.member.role !== Role.Admin){ //認証
    const projectAPIRes = await appFetch('/api/projects/' + projectId);
    if(projectAPIRes.status === 404){
        res.status(400).json({
            success: false,
            message: "400 Bad Request",
        });
    }
    const project = await projectAPIRes.json();
    const affiliation = session.member.group.some(g=>g.id===project.group.id);
    if(!affiliation){
        res.status(401).json({
            success: false,
            message: "401 Unauthorized",
        });
        return;
    }
}

ここまで来ればIDがprojectIdの企画のステータスを変更できます。許容されている3つ(OK,Limited,Suspended)であればその状態にし、それ以外であれば状態をなし(null)に設定します。
Prismaのupdateを使って対象の企画のデータを更新し、更新後の企画状態を返します。

if (name === 'OK' || name === 'Limited' || name === 'Suspended') {
    const updateProjectStatus = await prisma.project.update({
        where: {
            id: Number(projectId),
        },
        data: {
            status: name
        }
    })
    res.status(200).json(updateProjectStatus);
}
else {
    const updateProjectStatus = await prisma.project.update({
        where: {
            id: Number(projectId),
        },
        data: {
            status: null
        }
    })
    res.status(200).json(updateProjectStatus);
}

DBからデータを取得して返す

次は、GETだったときの処理についてです。
まずは、クエリにidが指定されているかどうかを確認します。指定されていればnumber型に変換してprojectId変数に代入し、指定されてなければ400を返します。

let projectId: number;
if (req.query["id"]) {
    projectId = Number(req.query["id"])
}
else {
    res.status(400).json({
        success: false,
        message: "400 Bad Request",
    });
    return;
}

その後、PrismaのfincFirstを使い、projectIdが一致する要素を探します。
もし一致するものが見つかればそれを返し、なければ404を返します。

const Project = await prisma.project.findFirst({
    where: {
        id: {equals: projectId}
    }
});
if (Project) {
    res.status(200).json({"status":Project.status});
}
else {
    res.status(404).json({
        success: false,
        message: "404 Not Found",
    });
}

こんな感じでAPIは作られています。

隠し要素について

僕は2箇所に隠し要素を仕掛けました。気がついた人は果たしているのでしょうか

コンソール

ヘッダーの部分に

useEffect(()=>{
    console.log("ブラウザの開発者コンソールを思わず覗いちゃうような君は、ぜひ木更津高専 プログラミング研究同好会へ!");
},[])

を追加しました。これにより、開発者ツールのコンソールを見ると、プログラミング研究同好会の宣伝が表示されるようになっていました。

API

next.jsのデフォルトで用意されている/api/helloを改造し、プログラミング研究同好会の宣伝が返ってくるようにしました。

{
    "success": true,
    "message": "api/helloを叩いたそこの君、next.jsを知ってるね! ぜひ木更津高専 プログラミング研究同好会に来ないかい? 木更津高専生の君はすぐに近くの会員に、そうじゃない君は木更津高専で待ってるよ!"
}

こんな具合です。コードは別に難しいことはなく、

res.status(200).json({
    success: true,
    message: "api/helloを叩いたそこの君、next.jsを知ってるね! ぜひ木更津高専 プログラミング研究同好会に来ないかい? 木更津高専生の君はすぐに近くの会員に、そうじゃない君は木更津高専で待ってるよ!",
});

を追加しただけです。

トラブル

誤入力によって、ソースコードの中にアルファベット一文字が混入したままコミット・プルリクエスト発行してしまったときがありました。
本来レビューによって防げるのですが、プルリクエスト発行者も承認者も実行して確認する作業を怠っていたため、エラーで動作しない状態でマージされてしまう事案がありました。

プルリクではちゃんと動作チェックはしましょうという教訓です。

最後に

祗園祭という大きなイベントに開発者として関われたのはとてもいい経験になりました。来年もやるかもしれないです。

脚注
  1. ただし、このAPIのGET部分はnairoki君が作ってます。それ以外のAPIはほぼ全て自分が作りましたが、解説しやすいのがこのAPIなのでこれにしました ↩︎

GitHubで編集を提案
木更津高専プログラミング研究同好会

Discussion