[A-00234] Typescript 1000本ノック (2)

前回記事はこちら

今回はBunを使ってRestAPIを作ります。

ディレクトリ構成は下記のとおり

.
├── README.md
├── biome.json
├── bun.lockb
├── command.sh
├── index.ts
├── node_modules
│   ├── @types
│   ├── bun-types
│   ├── typescript
│   └── undici-types
├── package-lock.json
├── package.json
├── tree.txt
└── tsconfig.json

6 directories, 9 files

下記コマンドでプロジェクトを作成します。

mkdir bun-blog-api
cd bun-blog-api
bun init

CRUD構成のREST APIを作成します。ソースは下記のとおり

import { serve } from "bun";

const PORT = 6989;

interface Post {
  id: string;
  title: string;
  content: string;
}

let blogPosts: Post[] = [];

// GET: Post by Id
function handleGetPostById(id: string) {
  const post = blogPosts.find((post) => post.id === id);

  if (!post) {
    return new Response("Post Not Found", { status: 404 });
  }

  return new Response(JSON.stringify(post), {
    headers: { "Content-Type": "application/json" },
  });
}

// GET: All Posts
function handleGetAllPosts() {
  return new Response(JSON.stringify(blogPosts), {
    headers: { "Content-Type": "application/json" },
  });
}

// POST: Add new Posts
function handleCreatePost(title: string, content: string) {
  const newPost: Post = {
    id: `${blogPosts.length}`,
    title: title,
    content: content,
  };

  blogPosts.push(newPost);

  return new Response(JSON.stringify(newPost), {
    headers: { "Content-Type": "application/json" },
    status: 201,
  });
}

// PATCH: Update a post
function handleUpdatePost(id: string, title: string, content: string) {
  const postIndex = blogPosts.findIndex((post) => post.id === id);

  if (postIndex === -1) {
    return new Response("Post Not Found", { status: 404 });
  }

  blogPosts[postIndex] = {
    ...blogPosts[postIndex],
    title,
    content,
  };

  return new Response("Post Updated", { status: 200 });
}

// DELETE: Delete a post
function handleDeletePost(id: string) {
  const postIndex = blogPosts.findIndex((post) => post.id === id);

  if (postIndex === -1) {
    return new Response("Post Not Found", { status: 404 });
  }

  blogPosts.splice(postIndex, 1);

  return new Response("Post Deleted", { status: 200 });
}

serve({
  port: PORT,
  async fetch(request: Request) {
    const { method } = request;
    const { pathname } = new URL(request.url);
    const pathRegexForID = /^\/api\/posts\/(\d+)$/;

    if (method === "GET" && pathname === "/api/posts") {
      return handleGetAllPosts();
    }

    if (method === "GET") {
      const match = pathname.match(pathRegexForID);
      const id = match && match[1];

      if (id) {
        return handleGetPostById(id);
      }
    }

    if (method === "POST" && pathname === "/api/posts") {
      const newPost = await request.json();
      return handleCreatePost(newPost.title, newPost.content);
    }

    if (method === "PATCH") {
      const match = pathname.match(pathRegexForID);
      const id = match && match[1];

      if (id) {
        const editedPost = await request.json();
        return handleUpdatePost(
          editedPost.id,
          editedPost.title,
          editedPost.content,
        );
      }
    }

    if (method === "DELETE" && pathname === "/api/posts") {
      const { id } = await request.json();
      return handleDeletePost(id);
    }

    return new Response("Not Found", { status: 404 });
  },
});

console.log(`Listening on http://localhost:${PORT}...`);

下記のコマンドで実行します。

user@usernoMacBook-Pro bun-blog-api % bun run index.ts 
Listening on http://localhost:6989...

データはインメモリでしか保存しない為、RestAPIを止めると全て消えてしまいますので注意。

下記の順にリクエストを実行してデータがちゃんと見れるかを確認します。

## POST
curl -X POST -d '{"title":"my first post", "content": "this is my first post on blog."}' http://localhost:6989/api/posts
## GET
curl -X GET http://localhost:6989/api/posts/0
## PATCH
curl -X PATCH -d '{"id":"0", "title":"my first batch.", "content":"Hello world!"}' http://localhost:6989/api/posts/0
## DELETE
curl -X DELETE -d '{"id":"0"}' http://localhost:6989/api/posts
user@usernoMacBook-Pro bun-blog-api % curl -X POST -d '{"title":"my first post", "content": "this is my first post on blog."}' http://localhost:6989/api/posts

{"id":"0","title":"my first post","content":"this is my first post on blog."}%                                                                                                   user@usernoMacBook-Pro bun-blog-api % curl -X GET http://localhost:6989/api/posts/0
{"id":"0","title":"my first post","content":"this is my first post on blog."}%                                                                                                   user@usernoMacBook-Pro bun-blog-api % curl -X PATCH -d '{"id":"0", "title":"my first batch.", "content":"Hello world!"}' curl -X GET http://localhost:6989/api/posts/0
curl: (6) Could not resolve host: curl
{"id":"0","title":"my first post","content":"this is my first post on blog."}%                                                                                                   user@usernoMacBook-Pro bun-blog-api % curl -X GET http://localhost:6989/api/posts/0                                                                                   
{"id":"0","title":"my first post","content":"this is my first post on blog."}%                                                                                                   user@usernoMacBook-Pro bun-blog-api % curl -X PATCH -d '{"id":"0", "title":"my first batch.", "content":"Hello world!"}' http://localhost:6989/api/posts/0
Post Updated%                                                                                                                                                                    user@usernoMacBook-Pro bun-blog-api % curl -X GET http://localhost:6989/api/posts/0                                                                       
{"id":"0","title":"my first batch.","content":"Hello world!"}%                                                                                                                   user@usernoMacBook-Pro bun-blog-api % curl -X DELETE http://localhost:6989/api/posts/0
Not Found%                                                                                                                                                                       user@usernoMacBook-Pro bun-blog-api % curl -X DELETE -d '{"id":"0"}' http://localhost:6989/api/posts
Post Deleted%           

・Appendix

参考文献はこちら

https://blog.thecodebrew.tech/build-your-first-rest-api-with-bun

https://github.com/cdbrw

https://typescriptbook.jp/reference/tsconfig

https://typescriptbook.jp/reference/values-types-variables/let-and-const

https://zenn.dev/azukiazusa/articles/804439f5afabe7

https://wp-kyoto.net/use-hono-on-the-nextjs-application

https://github.com/DavidHavl/hono-rest-api-starter

https://zenn.dev/minagishl/articles/c2f31c049348ea

https://zenn.dev/prog_daiki/books/8f186445cc080e/viewer/1cfdc7

https://github.com/Vitaee/HonoStarter/tree/main/src

https://zenn.dev/azukiazusa/articles/hono-cloudflare-workers-rest-api

https://biomejs.dev/ja/guides/getting-started

https://biomejs.dev/ja/guides/big-projects

https://zenn.dev/pe_be_o/articles/maeta-187-articles_c7602bb31f03d7

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

*