[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://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
コメントを残す