Bước 1: Định nghĩa type Bookmark Step 1: Define the Bookmark Type ជំហាន 1: កំណត់ type Bookmark
- Chúng ta đang xây What we're building អ្វីដែលយើងកំពុងសង់
- A TypeScript interface that describes a bookmark.
- Vì sao quan trọng Why this matters ហេតុអ្វីសំខាន់
- Defining types up front makes the code self-documenting and catches mistakes at development time.
Open src/index.ts and replace the entire file with the following. Start by defining the Bookmark interface and an in-memory store:
Open src/index.ts and replace the entire file with the following. Start by defining the Bookmark interface and an in-memory store:
Open src/index.ts and replace the entire file with the following. Start by defining the Bookmark interface and an in-memory store:
interface Bookmark {
id: string;
url: string;
title: string;
createdAt: string;
}
// In-memory store (lost on restart, we will fix this in Step 3)
const bookmarks: Map<string, Bookmark> = new Map(); Bước 2: Thêm router Step 2: Add the Router ជំហាន 2: បន្ថែម router
- Chúng ta đang xây What we're building អ្វីដែលយើងកំពុងសង់
- A fetch handler that routes requests to the correct handler based on path and method.
- Vì sao quan trọng Why this matters ហេតុអ្វីសំខាន់
- Clean routing is the foundation of any API. Separating route matching from business logic keeps the code maintainable.
Add the fetch handler below the bookmark interface:
Add the fetch handler below the bookmark interface:
Add the fetch handler below the bookmark interface:
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const url = new URL(request.url);
const path = url.pathname;
const method = request.method;
// GET /bookmarks - list all
if (path === '/bookmarks' && method === 'GET') {
return listBookmarks();
}
// POST /bookmarks - create
if (path === '/bookmarks' && method === 'POST') {
return createBookmark(request);
}
// Match /bookmarks/:id pattern (used by both GET and DELETE below)
const match = path.match(/^\/bookmarks\/([a-zA-Z0-9_-]+)$/);
// GET /bookmarks/:id - get one
if (match && method === 'GET') {
return getBookmark(match[1]);
}
// DELETE /bookmarks/:id - delete one
if (match && method === 'DELETE') {
return deleteBookmark(match[1]);
}
// Fallback
if (path === '/') {
return Response.json({
name: 'Bookmark API',
endpoints: ['GET /bookmarks', 'POST /bookmarks', 'GET /bookmarks/:id', 'DELETE /bookmarks/:id']
});
}
return Response.json({ error: 'Not Found' }, { status: 404 });
},
}; Bước 3: Viết handler Step 3: Implement the Handlers ជំហាន 3: សរសេរ handler
- Chúng ta đang xây What we're building អ្វីដែលយើងកំពុងសង់
- Four handler functions that perform CRUD operations on the in-memory bookmark store.
- Vì sao quan trọng Why this matters ហេតុអ្វីសំខាន់
- Each handler is a small, focused function. This pattern will carry forward as you swap the storage layer in later steps.
Add these functions below the export default block:
Add these functions below the export default block:
Add these functions below the export default block:
function listBookmarks(): Response {
const all = Array.from(bookmarks.values());
return Response.json({ bookmarks: all, count: all.length });
}
async function createBookmark(request: Request): Promise<Response> {
let body: { url?: string; title?: string };
try {
body = await request.json() as { url?: string; title?: string };
} catch {
return Response.json(
{ error: 'Invalid JSON in request body' },
{ status: 400 }
);
}
if (!body.url || !body.title) {
return Response.json(
{ error: 'Missing required fields: url, title' },
{ status: 400 }
);
}
const id = crypto.randomUUID().slice(0, 8);
const bookmark: Bookmark = {
id,
url: body.url,
title: body.title,
createdAt: new Date().toISOString(),
};
bookmarks.set(id, bookmark);
return Response.json(bookmark, { status: 201 });
}
function getBookmark(id: string): Response {
const bookmark = bookmarks.get(id);
if (!bookmark) {
return Response.json({ error: 'Bookmark not found' }, { status: 404 });
}
return Response.json(bookmark);
}
function deleteBookmark(id: string): Response {
if (!bookmarks.has(id)) {
return Response.json({ error: 'Bookmark not found' }, { status: 404 });
}
bookmarks.delete(id);
return Response.json({ message: 'Bookmark deleted' });
} Bước 4: Test API Step 4: Test Your API ជំហាន 4: Test API
- Chúng ta đang xây What we're building អ្វីដែលយើងកំពុងសង់
- All four endpoints tested and working locally.
- Vì sao quan trọng Why this matters ហេតុអ្វីសំខាន់
- Testing each endpoint confirms the routing, validation, and response format are correct before adding persistence.
Make sure npm run dev is running, then test:
Make sure npm run dev is running, then test:
Make sure npm run dev is running, then test:
Tạo bookmark Create bookmarks បង្កើត bookmark
curl -X POST http://localhost:8787/bookmarks \
-H "Content-Type: application/json" \
-d '{"url":"https://developers.cloudflare.com/workers/","title":"Workers Docs"}' curl -X POST http://localhost:8787/bookmarks \
-H "Content-Type: application/json" \
-d '{"url":"https://developers.cloudflare.com/d1/","title":"D1 Docs"}' Liệt kê bookmark List all bookmarks រាយ bookmark
curl -s http://localhost:8787/bookmarks | jq Lấy một bookmark Get a single bookmark យក bookmark មួយ
Use an id from the create response:
Use an id from the create response:
Use an id from the create response:
curl -s http://localhost:8787/bookmarks/REPLACE_ID | jq Xóa bookmark Delete a bookmark លុប bookmark
curl -X DELETE http://localhost:8787/bookmarks/REPLACE_ID Test trường hợp lỗi Test error cases Test ករណី error
Missing fields — should return 400:
Missing fields — should return 400:
Missing fields — should return 400:
curl -X POST http://localhost:8787/bookmarks \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com"}' Non-existent bookmark — should return 404:
Non-existent bookmark — should return 404:
Non-existent bookmark — should return 404:
curl http://localhost:8787/bookmarks/does-not-exist File hoàn chỉnh The Complete File File ពេញ
For reference, here is the full src/index.ts after this step:
For reference, here is the full src/index.ts after this step:
For reference, here is the full src/index.ts after this step:
interface Bookmark {
id: string;
url: string;
title: string;
createdAt: string;
}
const bookmarks: Map<string, Bookmark> = new Map();
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const url = new URL(request.url);
const path = url.pathname;
const method = request.method;
if (path === '/bookmarks' && method === 'GET') {
return listBookmarks();
}
if (path === '/bookmarks' && method === 'POST') {
return createBookmark(request);
}
// Match /bookmarks/:id pattern (used by both GET and DELETE below)
const match = path.match(/^\/bookmarks\/([a-zA-Z0-9_-]+)$/);
if (match && method === 'GET') {
return getBookmark(match[1]);
}
if (match && method === 'DELETE') {
return deleteBookmark(match[1]);
}
if (path === '/') {
return Response.json({
name: 'Bookmark API',
endpoints: ['GET /bookmarks', 'POST /bookmarks', 'GET /bookmarks/:id', 'DELETE /bookmarks/:id']
});
}
return Response.json({ error: 'Not Found' }, { status: 404 });
},
};
function listBookmarks(): Response {
const all = Array.from(bookmarks.values());
return Response.json({ bookmarks: all, count: all.length });
}
async function createBookmark(request: Request): Promise<Response> {
let body: { url?: string; title?: string };
try {
body = await request.json() as { url?: string; title?: string };
} catch {
return Response.json(
{ error: 'Invalid JSON in request body' },
{ status: 400 }
);
}
if (!body.url || !body.title) {
return Response.json(
{ error: 'Missing required fields: url, title' },
{ status: 400 }
);
}
const id = crypto.randomUUID().slice(0, 8);
const bookmark: Bookmark = {
id,
url: body.url,
title: body.title,
createdAt: new Date().toISOString(),
};
bookmarks.set(id, bookmark);
return Response.json(bookmark, { status: 201 });
}
function getBookmark(id: string): Response {
const bookmark = bookmarks.get(id);
if (!bookmark) {
return Response.json({ error: 'Bookmark not found' }, { status: 404 });
}
return Response.json(bookmark);
}
function deleteBookmark(id: string): Response {
if (!bookmarks.has(id)) {
return Response.json({ error: 'Bookmark not found' }, { status: 404 });
}
bookmarks.delete(id);
return Response.json({ message: 'Bookmark deleted' });
}