

รู้จักกับ Deno Deploy และความสำคัญของ Code Review
ในยุคที่การพัฒนาเว็บแอปพลิเคชันและ API มีความรวดเร็วมากขึ้น การเลือกใช้แพลตฟอร์มที่เหมาะสมจึงเป็นสิ่งสำคัญ Deno Deploy กลายเป็นหนึ่งในตัวเลือกที่น่าสนใจสำหรับนักพัฒนาสมัยใหม่ ด้วยความสามารถในการรันโค้ด JavaScript และ TypeScript โดยตรงจาก edge ซึ่งให้ประสิทธิภาพสูงและ latency ต่ำ อย่างไรก็ตาม การปรับใช้โค้ดบน Deno Deploy โดยไม่มีการตรวจสอบคุณภาพอย่างเป็นระบบอาจนำไปสู่ปัญหาด้านความปลอดภัย ประสิทธิภาพ และการบำรุงรักษา
บทความนี้จะเจาะลึกถึงแนวปฏิบัติที่ดีที่สุด (Best Practices) สำหรับการตรวจสอบโค้ด (Code Review) บน Deno Deploy โดยเฉพาะ ซึ่งครอบคลุมตั้งแต่การตั้งค่าโปรเจกต์ การจัดการ dependencies การเขียนเทสต์ ไปจนถึงการปรับแต่งประสิทธิภาพ เราจะใช้ตัวอย่างจริงจากโปรเจกต์ที่ใช้งานบน Deno Deploy และเปรียบเทียบกับแนวทางที่ใช้ใน Node.js เพื่อให้เห็นความแตกต่างอย่างชัดเจน
การทำ Code Review ที่ดีไม่ใช่แค่การหาบั๊ก แต่เป็นการสร้างมาตรฐานร่วมกันในทีม ช่วยให้โค้ดมีคุณภาพสูงขึ้น ลด technical debt และเพิ่มความมั่นใจในการ deploy โค้ดขึ้น production สำหรับ Deno Deploy ซึ่งมีข้อจำกัดและจุดเด่นเฉพาะตัว การมีแนวทางที่ชัดเจนจะช่วยให้ทีมของคุณทำงานได้อย่างมีประสิทธิภาพมากขึ้น
1. การตั้งค่าโปรเจกต์และโครงสร้างพื้นฐานสำหรับ Deno Deploy
1.1 โครงสร้างโฟลเดอร์ที่แนะนำ
การมีโครงสร้างโปรเจกต์ที่ดีเป็นพื้นฐานสำคัญสำหรับการทำ Code Review ที่มีประสิทธิภาพ สำหรับ Deno Deploy เราแนะนำให้ใช้โครงสร้างดังนี้:
project/
├── src/
│ ├── routes/
│ │ ├── index.ts
│ │ ├── users.ts
│ │ └── api/
│ │ └── v1.ts
│ ├── middleware/
│ │ ├── auth.ts
│ │ └── logger.ts
│ ├── utils/
│ │ ├── helpers.ts
│ │ └── validators.ts
│ └── main.ts
├── tests/
│ ├── unit/
│ │ └── helpers_test.ts
│ └── integration/
│ └── api_test.ts
├── config/
│ ├── deno.json
│ └── env.ts
├── scripts/
│ └── deploy.ts
├── .github/
│ └── workflows/
│ └── ci.yml
├── deno.json
└── README.md
ข้อสังเกต: Deno Deploy รองรับการ import แบบ URL โดยตรง ทำให้เราไม่จำเป็นต้องมี node_modules เหมือนใน Node.js ซึ่งช่วยลดขนาดโปรเจกต์และทำให้ Code Review ง่ายขึ้น เพราะ dependencies ถูกระบุชัดเจนในไฟล์ import map
1.2 การตั้งค่า deno.json และ import map
ไฟล์ deno.json เป็นหัวใจของการจัดการโปรเจกต์ Deno ควรกำหนด tasks, import map, และ lint/format options ให้ชัดเจน:
{
"tasks": {
"dev": "deno run --watch --allow-net --allow-read src/main.ts",
"test": "deno test --allow-net --allow-read",
"lint": "deno lint",
"fmt": "deno fmt",
"check": "deno check src/main.ts",
"deploy": "deployctl deploy --project=my-project --prod"
},
"imports": {
"std/": "https://deno.land/[email protected]/",
"oak/": "https://deno.land/x/[email protected]/",
"dotenv/": "https://deno.land/x/[email protected]/"
},
"lint": {
"rules": {
"tags": ["recommended"],
"include": [
"no-unused-vars",
"no-explicit-any",
"no-nullish-coalescing"
]
}
},
"fmt": {
"options": {
"lineWidth": 80,
"indentWidth": 2,
"singleQuote": true
}
}
}
สิ่งที่ต้องตรวจสอบใน Code Review:
- URL ของ dependencies ควรระบุ version ให้แน่นอน (ไม่ใช้ latest) เพื่อป้องกัน breaking changes
- tasks ควรครอบคลุมการทำงานหลัก: dev, test, lint, fmt, check, deploy
- rules ใน lint ควรสอดคล้องกับมาตรฐานของทีม
- import map ควรจัดกลุ่ม dependencies อย่างเป็นระบบ
2. การจัดการ Dependencies และ Security ใน Deno Deploy
2.1 การใช้ Third-party Modules อย่างปลอดภัย
Deno Deploy แตกต่างจาก Node.js ตรงที่ไม่อนุญาตให้ใช้ npm modules โดยตรง แต่สามารถใช้ผ่าน npm.deno.dev หรือ import จาก deno.land/x ได้ อย่างไรก็ตาม การใช้ dependencies จากภายนอกมีความเสี่ยงด้านความปลอดภัยที่ต้องพิจารณา
แนวปฏิบัติที่ดี:
- ใช้เฉพาะ modules ที่ได้รับการยืนยันจากทีมงาน Deno หรือมี community trust สูง
- ตรวจสอบว่า module มีการอัปเดตสม่ำเสมอและไม่มีช่องโหว่ที่รู้จัก
- หลีกเลี่ยงการใช้ modules ที่มี dependencies ซับซ้อนเกินไป
- ใช้
deno vendorเพื่อสร้าง local copy ของ dependencies สำหรับ production
2.2 การจัดการ Environment Variables และ Secrets
Deno Deploy มีระบบจัดการ secrets ในตัว แต่ในการพัฒนาและ Code Review เราควรใช้ไฟล์ .env สำหรับ local development:
// config/env.ts
import { config } from "dotenv/mod.ts";
const env = config({ safe: true, export: true });
export const APP_CONFIG = {
port: parseInt(env.PORT || "8000"),
databaseUrl: env.DATABASE_URL,
jwtSecret: env.JWT_SECRET,
apiKey: env.API_KEY,
environment: env.ENVIRONMENT || "development",
};
// ตรวจสอบว่า secrets ถูกตั้งค่าครบถ้วน
export function validateConfig(): void {
const required = ["DATABASE_URL", "JWT_SECRET", "API_KEY"];
for (const key of required) {
if (!env[key]) {
throw new Error(`Missing required environment variable: ${key}`);
}
}
}
สิ่งที่ต้องตรวจสอบใน Code Review:
- ไม่มี hardcoded secrets ในโค้ด (API keys, tokens, passwords)
- ไฟล์
.envถูกเพิ่มใน.gitignore - มีการ validate environment variables ก่อนเริ่มแอปพลิเคชัน
- ใช้ Deno Deploy Dashboard ในการตั้งค่า secrets สำหรับ production
2.3 การจัดการ Permissions (Deno Permissions)
Deno มีระบบ permissions ที่เข้มงวด ซึ่งแตกต่างจาก Node.js โดยสิ้นเชิง ในการทำ Code Review ต้องตรวจสอบว่าโค้ดร้องขอ permissions ที่จำเป็นเท่านั้น:
| Permission | การใช้งาน | ข้อควรระวัง |
|---|---|---|
--allow-net |
เชื่อมต่อเครือข่าย (HTTP requests) | ควรระบุ domain ที่อนุญาต เช่น --allow-net=api.example.com |
--allow-read |
อ่านไฟล์จากระบบ | ควรจำกัดเฉพาะ directory ที่จำเป็น |
--allow-write |
เขียนไฟล์ | หลีกเลี่ยงถ้าไม่จำเป็นจริงๆ |
--allow-env |
เข้าถึง environment variables | ควรระบุเฉพาะตัวแปรที่จำเป็น |
--allow-run |
รันคำสั่งภายนอก | ⚠️ อันตรายมาก ควรหลีกเลี่ยงใน Deno Deploy |
ตัวอย่างการตรวจสอบ permissions ใน Code Review:
// ดี: ระบุ permissions เฉพาะที่จำเป็น
// deno run --allow-net=api.example.com --allow-read=./config --allow-env=PORT,DATABASE_URL src/main.ts
// ไม่ดี: ใช้ permissions แบบกว้างเกินไป
// deno run --allow-net --allow-read --allow-env src/main.ts
3. การเขียนโค้ดที่ปลอดภัยและมีประสิทธิภาพสำหรับ Deno Deploy
3.1 การใช้ TypeScript อย่างเคร่งครัด
Deno รองรับ TypeScript โดยตรงโดยไม่ต้องตั้งค่าเพิ่มเติม การใช้ TypeScript อย่างถูกต้องช่วยลดบั๊กและทำให้ Code Review มีประสิทธิภาพมากขึ้น:
// ดี: ใช้ type annotations และ interfaces อย่างชัดเจน
interface User {
id: string;
name: string;
email: string;
createdAt: Date;
}
interface CreateUserRequest {
name: string;
email: string;
password: string;
}
async function createUser(data: CreateUserRequest): Promise<User> {
// validation
if (!data.email.includes('@')) {
throw new Error('Invalid email format');
}
const user: User = {
id: crypto.randomUUID(),
name: data.name,
email: data.email,
createdAt: new Date(),
};
// บันทึกลง database
await db.insert('users', user);
return user;
}
// ไม่ดี: ใช้ any หรือไม่ระบุ type
async function createUser(data: any) {
return await db.insert('users', data);
}
สิ่งที่ต้องตรวจสอบใน Code Review:
- หลีกเลี่ยงการใช้
anyยกเว้นในกรณีที่จำเป็นจริงๆ (และควรมี comment อธิบาย) - ใช้
strictmode ในdeno.json(เปิดโดยค่าเริ่มต้น) - ตรวจสอบว่า function signatures มี type annotations ครบถ้วน
- ใช้ utility types เช่น
Partial<T>,Pick<T>,Omit<T>อย่างเหมาะสม
3.2 การจัดการ Error Handling
Deno Deploy มีข้อจำกัดเรื่อง runtime environment ที่แตกต่างจาก Node.js การจัดการข้อผิดพลาดอย่างถูกต้องเป็นสิ่งสำคัญ:
// middleware/errorHandler.ts
import { Context, Next } from "oak/mod.ts";
interface AppError {
status: number;
message: string;
code?: string;
details?: unknown;
}
export class HTTPError extends Error {
status: number;
code: string;
constructor(status: number, message: string, code?: string) {
super(message);
this.status = status;
this.code = code || "UNKNOWN_ERROR";
this.name = "HTTPError";
}
}
export async function errorHandler(ctx: Context, next: Next) {
try {
await next();
} catch (error) {
if (error instanceof HTTPError) {
ctx.response.status = error.status;
ctx.response.body = {
error: {
message: error.message,
code: error.code,
},
};
} else if (error instanceof SyntaxError) {
ctx.response.status = 400;
ctx.response.body = {
error: {
message: "Invalid request syntax",
code: "INVALID_SYNTAX",
},
};
} else {
// Log error สำหรับ debugging
console.error("Unhandled error:", error);
ctx.response.status = 500;
ctx.response.body = {
error: {
message: "Internal server error",
code: "INTERNAL_ERROR",
},
};
}
}
}
ข้อควรจำ: ใน Deno Deploy เราไม่สามารถใช้ stack traces แบบละเอียดเหมือน local development เพราะโค้ดถูกรันใน sandbox environment ดังนั้นการ log errors อย่างมีโครงสร้างจึงสำคัญมาก
3.3 การใช้ Async/Await อย่างถูกต้อง
Deno Deploy เป็น single-threaded event loop เช่นเดียวกับ Node.js แต่มีประสิทธิภาพสูงกว่าในการจัดการ I/O การใช้ async/await อย่างถูกต้องช่วยป้องกันปัญหา race conditions และ blocking:
// ดี: ใช้ Promise.all สำหรับ tasks ที่ไม่ต้องรอซึ่งกันและกัน
async function fetchUserData(userId: string) {
const [user, posts, comments] = await Promise.all([
db.getUser(userId),
db.getPosts(userId),
db.getComments(userId),
]);
return { user, posts, comments };
}
// ไม่ดี: รอทีละอัน ทำให้เสียเวลา
async function fetchUserData(userId: string) {
const user = await db.getUser(userId);
const posts = await db.getPosts(userId);
const comments = await db.getComments(userId);
return { user, posts, comments };
}
// ดี: ใช้ AbortController สำหรับ timeout
async function fetchWithTimeout(url: string, timeoutMs: number = 5000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
try {
const response = await fetch(url, {
signal: controller.signal,
});
return await response.json();
} finally {
clearTimeout(timeoutId);
}
}
4. การเขียนเทสต์และการตรวจสอบคุณภาพโค้ด
4.1 การตั้งค่า Testing Framework
Deno มี testing framework ในตัว โดยไม่ต้องติดตั้ง dependencies เพิ่มเติม ซึ่งเป็นข้อดีที่ทำให้ Code Review ง่ายขึ้นเพราะทุกคนใช้มาตรฐานเดียวกัน:
// tests/unit/helpers_test.ts
import { assertEquals, assertRejects, assertThrows } from "std/testing/asserts.ts";
import { describe, it, beforeEach, afterEach } from "std/testing/bdd.ts";
import { validateEmail, hashPassword, generateToken } from "../../src/utils/helpers.ts";
describe("validateEmail", () => {
it("should return true for valid email", () => {
assertEquals(validateEmail("[email protected]"), true);
assertEquals(validateEmail("[email protected]"), true);
});
it("should return false for invalid email", () => {
assertEquals(validateEmail("not-an-email"), false);
assertEquals(validateEmail("user@"), false);
assertEquals(validateEmail(""), false);
});
it("should handle edge cases", () => {
assertEquals(validateEmail("[email protected]"), true);
assertEquals(validateEmail("user@localhost"), false); // ต้องมี dot
});
});
describe("hashPassword", () => {
it("should return a hashed string", async () => {
const hash = await hashPassword("myPassword123");
assertEquals(typeof hash, "string");
assertEquals(hash.length > 20, true);
});
it("should produce different hashes for same password", async () => {
const hash1 = await hashPassword("test");
const hash2 = await hashPassword("test");
assertEquals(hash1 !== hash2, true); // เนื่องจากใช้ salt
});
it("should reject empty password", async () => {
await assertRejects(
() => hashPassword(""),
Error,
"Password cannot be empty"
);
});
});
describe("generateToken", () => {
it("should generate a JWT token", () => {
const payload = { userId: "123", role: "admin" };
const token = generateToken(payload);
assertEquals(token.split(".").length, 3); // JWT มี 3 parts
});
});
4.2 การเขียน Integration Tests สำหรับ Deno Deploy
การทดสอบ integration บน Deno Deploy ต้องคำนึงถึงข้อจำกัดของ platform เช่น ไม่มี filesystem จริง, ไม่มี socket ท้องถิ่น:
// tests/integration/api_test.ts
import { assertEquals, assertExists } from "std/testing/asserts.ts";
import { Application, Router } from "oak/mod.ts";
import { superoak } from "https://deno.land/x/[email protected]/mod.ts";
// สร้างแอปพลิเคชันเสมือนสำหรับทดสอบ
function createTestApp(): Application {
const app = new Application();
const router = new Router();
router.get("/api/v1/health", (ctx) => {
ctx.response.body = { status: "ok", timestamp: new Date().toISOString() };
});
router.post("/api/v1/users", async (ctx) => {
const body = await ctx.request.body().value;
if (!body.name || !body.email) {
ctx.response.status = 400;
ctx.response.body = { error: "Missing required fields" };
return;
}
ctx.response.status = 201;
ctx.response.body = { id: crypto.randomUUID(), ...body };
});
app.use(router.routes());
return app;
}
Deno.test("GET /api/v1/health should return 200", async () => {
const app = createTestApp();
const request = await superoak(app);
const response = await request.get("/api/v1/health");
assertEquals(response.status, 200);
assertEquals(response.body.status, "ok");
assertExists(response.body.timestamp);
});
Deno.test("POST /api/v1/users should create user", async () => {
const app = createTestApp();
const request = await superoak(app);
const response = await request
.post("/api/v1/users")
.send({ name: "John Doe", email: "[email protected]" });
assertEquals(response.status, 201);
assertEquals(response.body.name, "John Doe");
assertExists(response.body.id);
});
Deno.test("POST /api/v1/users should reject missing fields", async () => {
const app = createTestApp();
const request = await superoak(app);
const response = await request
.post("/api/v1/users")
.send({ name: "John Doe" }); // missing email
assertEquals(response.status, 400);
assertEquals(response.body.error, "Missing required fields");
});
4.3 การใช้ Linting และ Formatting อัตโนมัติ
Deno มีเครื่องมือ lint และ format ในตัวที่ทรงพลัง การตั้งค่าให้ทำงานอัตโนมัติใน CI/CD ช่วยลดความขัดแย้งใน Code Review:
// .github/workflows/ci.yml
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: denoland/setup-deno@v1
with:
deno-version: v1.x
- name: Check formatting
run: deno fmt --check
- name: Lint code
run: deno lint
- name: Type check
run: deno check src/main.ts
- name: Run tests
run: deno test --allow-net --allow-read --coverage=coverage
- name: Generate coverage report
run: deno coverage coverage --lcov > coverage.lcov
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
file: ./coverage.lcov
5. การปรับแต่งประสิทธิภาพและ Best Practices สำหรับ Production
5.1 การเพิ่มประสิทธิภาพการทำงานบน Edge
Deno Deploy รันโค้ดบน edge network ทั่วโลก ดังนั้นการออกแบบให้ทำงานได้ดีกับ distributed environment จึงสำคัญ:
| แนวปฏิบัติ | คำอธิบาย | ผลกระทบ |
|---|---|---|
| ใช้ Cache อย่างชาญฉลาด | ใช้ Cache-Control headers และ Deno Deploy’s built-in cache | ลด latency 50-80% สำหรับ requests ซ้ำ |
| หลีกเลี่ยง Stateful Operations | ใช้ external database หรือ KV store แทน in-memory state | ป้องกัน data loss เมื่อ instance ถูกย้าย |
| Optimize Cold Starts | ลดขนาดโค้ด ใช้ lazy loading modules | ลด cold start time จาก 200ms เหลือ 50ms |
| ใช้ HTTP/2 และ Connection Pooling | Deno Deploy รองรับ HTTP/2 โดยค่าเริ่มต้น | เพิ่ม throughput สำหรับ concurrent requests |
| Minify และ Bundle โค้ด | ใช้ deno bundle หรือ esbuild สำหรับ production |
ลดขนาด deploy artifact และ improve load time |
5.2 การใช้ Deno KV สำหรับ State Management
Deno Deploy มี Deno KV ซึ่งเป็น distributed key-value store ที่ทำงานได้ดีกับ edge environment:
// src/utils/kv.ts
import { openKv } from "deno/kv/mod.ts";
interface Session {
userId: string;
token: string;
expiresAt: number;
}
export class SessionStore {
private kv: Deno.Kv | null = null;
async initialize(): Promise<void> {
this.kv = await openKv();
}
async createSession(userId: string, ttlMs: number = 3600000): Promise<Session> {
const token = crypto.randomUUID();
const expiresAt = Date.now() + ttlMs;
const session: Session = { userId, token, expiresAt };
// ใช้ atomic operation เพื่อป้องกัน race conditions
const result = await this.kv!.atomic()
.set(["sessions", token], session)
.set(["user_sessions", userId, token], { createdAt: Date.now() })
.commit();
if (!result.ok) {
throw new Error("Failed to create session");
}
return session;
}
async getSession(token: string): Promise<Session | null> {
const result = await this.kv!.get<Session>(["sessions", token]);
if (!result.value) return null;
// ตรวจสอบ expiration
if (result.value.expiresAt < Date.now()) {
await this.deleteSession(token);
return null;
}
return result.value;
}
async deleteSession(token: string): Promise<void> {
const session = await this.getSession(token);
if (session) {
await this.kv!.atomic()
.delete(["sessions", token])
.delete(["user_sessions", session.userId, token])
.commit();
}
}
async listUserSessions(userId: string): Promise<Session[]> {
const entries = this.kv!.list<{ createdAt: number }>({
prefix: ["user_sessions", userId],
});
const sessions: Session[] = [];
for await (const entry of entries) {
const token = entry.key[2] as string;
const session = await this.getSession(token);
if (session) {
sessions.push(session);
}
}
return sessions;
}
}
// Singleton instance
export const sessionStore = new SessionStore();
5.3 การจัดการ Rate Limiting และ DDoS Protection
ถึงแม้ Deno Deploy จะมีการป้องกัน DDoS ในระดับ infrastructure แต่การ implement rate limiting ที่ application layer ก็ยังจำเป็น:
// middleware/rateLimiter.ts
import { Context, Next } from "oak/mod.ts";
import { openKv } from "deno/kv/mod.ts";
interface RateLimitConfig {
windowMs: number; // ระยะเวลาในหน่วย ms
maxRequests: number; // จำนวน requests สูงสุดในช่วงเวลา
message?: string; // ข้อความ error
}
const defaultConfig: RateLimitConfig = {
windowMs: 60000, // 1 นาที
maxRequests: 100, // 100 requests
message: "Too many requests, please try again later.",
};
export function rateLimiter(config: Partial<RateLimitConfig> = {}) {
const options = { ...defaultConfig, ...config };
const kv = await openKv();
return async function rateLimitMiddleware(ctx: Context, next: Next) {
const clientIp = ctx.request.ip;
const now = Date.now();
const windowKey = Math.floor(now / options.windowMs);
// สร้าง key สำหรับ rate limit
const key = ["rate_limit", clientIp, windowKey];
// ใช้ atomic operation เพื่อเพิ่ม counter
const result = await kv.atomic()
.mutate({
type: "sum",
key,
value: new Deno.KvU64(1n),
})
.commit();
if (!result.ok) {
// ถ้า atomic ล้มเหลว ให้ผ่านไป (fail open)
await next();
return;
}
// อ่านค่า current count
const entry = await kv.get<Deno.KvU64>(key);
const currentCount = entry.value ? Number(entry.value) : 0;
// ตั้ง TTL ให้ key อัตโนมัติ (Deno KV รองรับ TTL)
await kv.set(key, new Deno.KvU64(BigInt(currentCount)), {
expireIn: options.windowMs,
});
if (currentCount > options.maxRequests) {
ctx.response.status = 429;
ctx.response.body = {
error: options.message,
retryAfter: Math.ceil(options.windowMs / 1000),
};
ctx.response.headers.set("Retry-After", String(Math.ceil(options.windowMs / 1000)));
return;
}
// เพิ่ม headers สำหรับ monitoring
ctx.response.headers.set("X-RateLimit-Limit", String(options.maxRequests));
ctx.response.headers.set("X-RateLimit-Remaining", String(options.maxRequests - currentCount));
ctx.response.headers.set("X-RateLimit-Reset", String(windowKey * options.windowMs + options.windowMs));
await next();
};
}
6. การทำ Code Review ที่มีประสิทธิภาพสำหรับทีม
6.1 Checklist สำหรับ Code Review บน Deno Deploy
การใช้ checklist ช่วยให้การตรวจสอบโค้ดเป็นระบบและไม่พลาดประเด็นสำคัญ:
- Security
- ✅ ไม่มี hardcoded secrets หรือ credentials
- ✅ ใช้ permissions เฉพาะที่จำเป็นเท่านั้น
- ✅ มี input validation สำหรับทุก endpoint
- ✅ ใช้ HTTPS และ secure headers
- ✅ ไม่ใช้
eval()หรือ dynamic imports ที่ไม่ปลอดภัย
- Performance
- ✅ ใช้ caching อย่างเหมาะสม
- ✅ หลีกเลี่ยง blocking operations
- ✅ ใช้ connection pooling สำหรับ database
- ✅ มีการจัดการ memory leak (โดยเฉพาะใน long-running processes)
- Code Quality
- ✅ ใช้ TypeScript types อย่างถูกต้อง
- ✅ มี unit tests และ integration tests
- ✅ ใช้ async/await แทน callbacks
- ✅ มี error handling ที่ครอบคลุม
- Deno-Specific
- ✅ ใช้ import map อย่างถูกต้อง
- ✅ ระบุ version ของ dependencies
- ✅ ใช้ Deno APIs แทน Node.js compatibility layer
- ✅ ตรวจสอบว่าโค้ดรันบน Deno Deploy ได้ (ไม่มี Node.js-specific APIs)
6.2 การใช้ Automation Tools ช่วยใน Code Review
เครื่องมือต่อไปนี้ช่วยลดภาระของมนุษย์ในการตรวจสอบโค้ด:
| เครื่องมือ | การใช้งาน | ประโยชน์ |
|---|---|---|
| Deno Lint | ตรวจสอบ syntax และ best practices | จับปัญหาพื้นฐานก่อน reviewer อ่าน |
| Deno Format | จัดรูปแบบโค้ดอัตโนมัติ | ลด argument เรื่องการจัดรูปแบบ |
| TypeScript Compiler | ตรวจสอบ type errors | ป้องกัน type-related bugs |
| GitHub Actions / CI | รัน tests และ lint อัตโนมัติ | ตรวจสอบทุก pull request |
| CodeQL | วิเคราะห์ security vulnerabilities | ตรวจจับช่องโหว่ที่ซับซ้อน |
| Dependabot | แจ้งเตือน dependencies ที่ล้าสมัย | รักษาความปลอดภัยของ dependencies |
6.3 Real-world Use Case: การ Deploy API สำหรับ E-commerce
ตัวอย่างการประยุกต์ใช้แนวปฏิบัติทั้งหมดกับโปรเจกต์จริง:
// src/main.ts - ตัวอย่าง API สำหรับร้านค้าออนไลน์
import { Application, Router, Context, Next } from "oak/mod.ts";
import { oakCors } from "https://deno.land/x/[email protected]/mod.ts";
import { errorHandler } from "./middleware/errorHandler.ts";
import { rateLimiter } from "./middleware/rateLimiter.ts";
import { authMiddleware } from "./middleware/auth.ts";
import { sessionStore } from "./utils/kv.ts";
import { validateConfig } from "./config/env.ts";
// ตรวจสอบ configuration ก่อนเริ่ม
validateConfig();
const app = new Application();
const router = new Router();
// Global middleware
app.use(oakCors({ origin: "https://example.com" }));
app.use(errorHandler);
app.use(rateLimiter({ windowMs: 60000, maxRequests: 200 }));
// Public routes
router.get("/api/v1/products", async (ctx) => {
const products = await fetchProducts();
ctx.response.body = products;
});
router.get("/api/v1/products/:id", async (ctx) => {
const { id } = ctx.params;
const product = await fetchProduct(id);
if (!product) {
ctx.response.status = 404;
ctx.response.body = { error: "Product not found" };
return;
}
ctx.response.body = product;
});
// Protected routes
router.post("/api/v1/orders", authMiddleware, async (ctx) => {
const userId = ctx.state.userId;
const body = await ctx.request.body().value;
// Validation
if (!body.items || !Array.isArray(body.items) || body.items.length === 0) {
ctx.response.status = 400;
ctx.response.body = { error: "Order must contain at least one item" };
return;
}
const order = await createOrder(userId, body.items);
ctx.response.status = 201;
ctx.response.body = order;
});
router.get("/api/v1/orders", authMiddleware, async (ctx) => {
const userId = ctx.state.userId;
const orders = await getUserOrders(userId);
ctx.response.body = orders;
});
app.use(router.routes());
app.use(router.allowedMethods());
// เริ่ม server
const port = parseInt(Deno.env.get("PORT") || "8000");
console.log(`Server running on port ${port}`);
await app.listen({ port });
สิ่งที่ Code Review ควรตรวจสอบในตัวอย่างนี้:
- มีการใช้ CORS อย่าง