HTTP λ©μλλ APIμ μλλ₯Ό λͺ
ννκ² νννκ³ RESTful μμΉμ λ°λ₯΄λλ‘ ν©λλ€.
HTTP λ©μλ κ°μ
GET
λ°μ΄ν° μ‘°ν μμ νκ³ λ©±λ±μ±
POST
λ°μ΄ν° μμ± λΉλ©±λ±μ±
PUT
λ°μ΄ν° μμ λ©±λ±μ±
DELETE
λ°μ΄ν° μμ λ©±λ±μ±
GET - μ‘°ν
λ°μ΄ν°λ₯Ό μ‘°νν λ μ¬μ©ν©λλ€. μλ² μνλ₯Ό λ³κ²½νμ§ μμ΅λλ€.
λ¨μΌ 리μμ€ μ‘°ν
class UserModel extends BaseModelClass {
@api({ httpMethod: "GET" })
async get(id: number): Promise<User> {
const rdb = this.getPuri("r");
const user = await rdb.table("users").where("id", id).first();
if (!user) {
throw new Error("User not found");
}
return user;
}
}
// νΈμΆ: GET /api/user/get?id=1
λͺ©λ‘ μ‘°ν
interface UserListParams {
page?: number;
pageSize?: number;
search?: string;
role?: UserRole;
sortBy?: "created_at" | "username";
sortOrder?: "asc" | "desc";
}
class UserModel extends BaseModelClass {
@api({ httpMethod: "GET" })
async list(params: UserListParams): Promise<{
users: User[];
total: number;
page: number;
pageSize: number;
}> {
const rdb = this.getPuri("r");
// κΈ°λ³Έκ° μ€μ
const page = params.page || 1;
const pageSize = params.pageSize || 20;
const sortBy = params.sortBy || "created_at";
const sortOrder = params.sortOrder || "desc";
// 쿼리 λΉλ
let query = rdb.table("users");
// κ²μ 쑰건
if (params.search) {
query = query.where((qb) => {
qb.where("username", "like", `%${params.search}%`).orWhere(
"email",
"like",
`%${params.search}%`,
);
});
}
// μν νν°
if (params.role) {
query = query.where("role", params.role);
}
// μ λ ¬
query = query.orderBy(sortBy, sortOrder);
// νμ΄μ§λ€μ΄μ
const users = await query
.limit(pageSize)
.offset((page - 1) * pageSize)
.select("*");
// μ 체 κ°μ
const [{ count }] = await rdb.table("users").count({ count: "*" });
return {
users,
total: count,
page,
pageSize,
};
}
}
// νΈμΆ: GET /api/user/list?page=1&pageSize=20&search=john&role=admin&sortBy=username&sortOrder=asc
κ΄κ³ λ°μ΄ν° μ‘°ν
class UserModel extends BaseModelClass {
@api({ httpMethod: "GET" })
async getWithProfile(id: number): Promise<{
user: User;
profile: Profile | null;
posts: Post[];
}> {
const rdb = this.getPuri("r");
// User μ‘°ν
const user = await rdb.table("users").where("id", id).first();
if (!user) {
throw new Error("User not found");
}
// Profile μ‘°ν (1:1)
const profile = await rdb.table("profiles").where("user_id", id).first();
// Posts μ‘°ν (1:N)
const posts = await rdb
.table("posts")
.where("user_id", id)
.orderBy("created_at", "desc")
.select("*");
return {
user,
profile: profile || null,
posts,
};
}
}
GET λ©μλ νΉμ§: - μμ ν¨ (Safe): μλ² μνλ₯Ό λ³κ²½νμ§ μμ - λ©±λ±μ± (Idempotent): κ°μ μμ²μ
μ¬λ¬ λ² ν΄λ κ²°κ³Ό λμΌ - μΊμ κ°λ₯ (Cacheable): λΈλΌμ°μ /νλ‘μμμ μΊμ κ°λ₯ - νλΌλ―Έν°λ URL
쿼리 μ€νΈλ§μΌλ‘ μ λ¬
POST - μμ±
μλ‘μ΄ λ¦¬μμ€λ₯Ό μμ±ν λ μ¬μ©ν©λλ€.
λ¨μ μμ±
interface CreateUserParams {
email: string;
username: string;
password: string;
role: UserRole;
}
class UserModel extends BaseModelClass {
@api({ httpMethod: "POST" })
@transactional()
async create(params: CreateUserParams): Promise<{
userId: number;
}> {
const wdb = this.getPuri("w");
// μ€λ³΅ 체ν¬
const existing = await wdb.table("users").where("email", params.email).first();
if (existing) {
throw new Error("Email already exists");
}
// User μμ±
const [user] = await wdb
.table("users")
.insert({
email: params.email,
username: params.username,
password: params.password, // μ€μ λ‘λ ν΄μ νμ
role: params.role,
})
.returning({ id: "id" });
return { userId: user.id };
}
}
// νΈμΆ: POST /api/user/create
// Body: { "email": "...", "username": "...", "password": "...", "role": "normal" }
볡μ‘ν μμ± (κ΄κ³ λ°μ΄ν° ν¬ν¨)
interface RegisterParams {
email: string;
username: string;
password: string;
profile: {
bio: string;
avatarUrl?: string;
};
}
class UserModel extends BaseModelClass {
@api({ httpMethod: "POST" })
@transactional()
async register(params: RegisterParams): Promise<{
userId: number;
profileId: number;
}> {
const wdb = this.getPuri("w");
// User μμ±
const userRef = wdb.ubRegister("users", {
email: params.email,
username: params.username,
password: params.password,
role: "normal",
});
// Profile μμ±
wdb.ubRegister("profiles", {
user_id: userRef,
bio: params.profile.bio,
avatar_url: params.profile.avatarUrl || null,
});
// μ μ₯
const [userId] = await wdb.ubUpsert("users");
const [profileId] = await wdb.ubUpsert("profiles");
return { userId, profileId };
}
}
λ°°μΉ μμ±
class UserModel extends BaseModelClass {
@api({ httpMethod: "POST" })
@transactional()
async createBatch(users: CreateUserParams[]): Promise<{
userIds: number[];
count: number;
}> {
const wdb = this.getPuri("w");
// λͺ¨λ User λ±λ‘
users.forEach((user) => {
wdb.ubRegister("users", {
email: user.email,
username: user.username,
password: user.password,
role: user.role,
});
});
// μΌκ΄ μ μ₯
const userIds = await wdb.ubUpsert("users");
return {
userIds,
count: userIds.length,
};
}
}
// νΈμΆ: POST /api/user/createBatch
// Body: [
// { "email": "user1@test.com", "username": "user1", ... },
// { "email": "user2@test.com", "username": "user2", ... }
// ]
POST λ©μλ νΉμ§: - λΉμμ ν¨: μλ² μνλ₯Ό λ³κ²½ν¨ - λΉλ©±λ±μ±: κ°μ μμ²μ μ¬λ¬ λ² νλ©΄ 리μμ€κ°
μ€λ³΅ μμ±λ μ μμ - μΊμ λΆκ°λ₯ - νλΌλ―Έν°λ Bodyλ‘ μ λ¬ - μΌλ°μ μΌλ‘ μμ±λ 리μμ€μ IDλ₯Ό λ°ν
PUT - μμ
κΈ°μ‘΄ 리μμ€λ₯Ό μμ ν λ μ¬μ©ν©λλ€.
μ 체 μμ
interface UpdateUserParams {
email: string;
username: string;
role: UserRole;
bio: string | null;
}
class UserModel extends BaseModelClass {
@api({ httpMethod: "PUT" })
@transactional()
async update(id: number, params: UpdateUserParams): Promise<void> {
const wdb = this.getPuri("w");
// User μ‘΄μ¬ νμΈ
const user = await wdb.table("users").where("id", id).first();
if (!user) {
throw new Error("User not found");
}
// μ 체 νλ μ
λ°μ΄νΈ
await wdb.table("users").where("id", id).update({
email: params.email,
username: params.username,
role: params.role,
bio: params.bio,
});
}
}
// νΈμΆ: PUT /api/user/update?id=1
// Body: { "email": "...", "username": "...", "role": "admin", "bio": "..." }
λΆλΆ μμ (PATCH μ€νμΌ)
class UserModel extends BaseModelClass {
@api({ httpMethod: "PUT" })
@transactional()
async updatePartial(id: number, params: Partial<UpdateUserParams>): Promise<void> {
const wdb = this.getPuri("w");
// User μ‘΄μ¬ νμΈ
const user = await wdb.table("users").where("id", id).first();
if (!user) {
throw new Error("User not found");
}
// μ λ¬λ νλλ§ μ
λ°μ΄νΈ
const updateData: any = {};
if (params.email !== undefined) updateData.email = params.email;
if (params.username !== undefined) updateData.username = params.username;
if (params.role !== undefined) updateData.role = params.role;
if (params.bio !== undefined) updateData.bio = params.bio;
if (Object.keys(updateData).length > 0) {
await wdb.table("users").where("id", id).update(updateData);
}
}
}
// νΈμΆ: PUT /api/user/updatePartial?id=1
// Body: { "bio": "Updated bio" } // λ€λ₯Έ νλλ λ³κ²½ μ λ¨
μν λ³κ²½
class OrderModel extends BaseModelClass {
@api({ httpMethod: "PUT" })
@transactional()
async updateStatus(
orderId: number,
status: "pending" | "paid" | "shipped" | "delivered" | "cancelled",
): Promise<void> {
const wdb = this.getPuri("w");
const order = await wdb.table("orders").where("id", orderId).first();
if (!order) {
throw new Error("Order not found");
}
// μν μ ν κ²μ¦
if (!this.isValidStatusTransition(order.status, status)) {
throw new Error(`Cannot change status from ${order.status} to ${status}`);
}
await wdb.table("orders").where("id", orderId).update({ status });
}
private isValidStatusTransition(from: string, to: string): boolean {
const transitions: Record<string, string[]> = {
pending: ["paid", "cancelled"],
paid: ["shipped", "cancelled"],
shipped: ["delivered"],
delivered: [],
cancelled: [],
};
return transitions[from]?.includes(to) || false;
}
}
PUT λ©μλ νΉμ§: - λΉμμ ν¨: μλ² μνλ₯Ό λ³κ²½ν¨ - λ©±λ±μ±: κ°μ μμ²μ μ¬λ¬ λ² ν΄λ κ²°κ³Ό λμΌ -
μΌλ°μ μΌλ‘ 리μμ€ μ 체λ₯Ό κ΅μ²΄ - νλΌλ―Έν°λ Bodyλ‘ μ λ¬ - μ±κ³΅ μ λΉ μλ΅ λλ μ
λ°μ΄νΈλ 리μμ€
λ°ν
DELETE - μμ
리μμ€λ₯Ό μμ ν λ μ¬μ©ν©λλ€.
λ¨μΌ μμ
class UserModel extends BaseModelClass {
@api({ httpMethod: "DELETE" })
@transactional()
async remove(id: number): Promise<void> {
const wdb = this.getPuri("w");
// User μ‘΄μ¬ νμΈ
const user = await wdb.table("users").where("id", id).first();
if (!user) {
throw new Error("User not found");
}
// μμ
await wdb.table("users").where("id", id).delete();
}
}
// νΈμΆ: DELETE /api/user/remove?id=1
μννΈ μμ
class UserModel extends BaseModelClass {
@api({ httpMethod: "DELETE" })
@transactional()
async softDelete(id: number): Promise<void> {
const wdb = this.getPuri("w");
const user = await wdb.table("users").where("id", id).whereNull("deleted_at").first();
if (!user) {
throw new Error("User not found");
}
// deleted_at μ€μ (μ€μ μμ μ ν¨)
await wdb.table("users").where("id", id).update({
deleted_at: new Date(),
});
}
// μννΈ μμ 볡ꡬ
@api({ httpMethod: "PUT" })
@transactional()
async restore(id: number): Promise<void> {
const wdb = this.getPuri("w");
const user = await wdb.table("users").where("id", id).whereNotNull("deleted_at").first();
if (!user) {
throw new Error("User not found or not deleted");
}
await wdb.table("users").where("id", id).update({
deleted_at: null,
});
}
}
κ΄λ ¨ λ°μ΄ν°μ ν¨κ» μμ
class UserModel extends BaseModelClass {
@api({ httpMethod: "DELETE" })
@transactional()
async removeWithRelations(id: number): Promise<void> {
const wdb = this.getPuri("w");
const user = await wdb.table("users").where("id", id).first();
if (!user) {
throw new Error("User not found");
}
// 1. Profile μμ
await wdb.table("profiles").where("user_id", id).delete();
// 2. Posts μμ
await wdb.table("posts").where("user_id", id).delete();
// 3. User μμ
await wdb.table("users").where("id", id).delete();
}
}
λ°°μΉ μμ
class UserModel extends BaseModelClass {
@api({ httpMethod: "DELETE" })
@transactional()
async removeBatch(ids: number[]): Promise<{
deletedCount: number;
}> {
const wdb = this.getPuri("w");
const deletedCount = await wdb.table("users").whereIn("id", ids).delete();
return { deletedCount };
}
}
// νΈμΆ: DELETE /api/user/removeBatch
// Body: { "ids": [1, 2, 3] }
DELETE λ©μλ νΉμ§: - λΉμμ ν¨: μλ² μνλ₯Ό λ³κ²½ν¨ - λ©±λ±μ±: μ΄λ―Έ μμ λ 리μμ€λ₯Ό λ€μ
μμ ν΄λ κ²°κ³Ό λμΌ - μ±κ³΅ μ μΌλ°μ μΌλ‘ λΉ μλ΅ (204 No Content) - μννΈ μμ κ³ λ € (deleted_at
μ»¬λΌ μ¬μ©)
λ©μλ μ ν κ°μ΄λ
μ ν κΈ°μ€
| μμ
| λ©μλ | μμ |
|---|
| λ¨μΌ μ‘°ν | GET | /api/user/get?id=1 |
| λͺ©λ‘ μ‘°ν | GET | /api/user/list?page=1 |
| μλ‘ μμ± | POST | /api/user/create |
| μ 체 μμ | PUT | /api/user/update?id=1 |
| λΆλΆ μμ | PUT | /api/user/updatePartial?id=1 |
| μμ | DELETE | /api/user/remove?id=1 |
RESTful μμΉ
μ’μ μ
class UserModel extends BaseModelClass {
// β
GET: μ‘°ν
@api({ httpMethod: "GET" })
async list(): Promise<User[]> {
/* ... */
}
// β
POST: μμ±
@api({ httpMethod: "POST" })
async create(params: CreateUserParams): Promise<{ userId: number }> {
/* ... */
}
// β
PUT: μμ
@api({ httpMethod: "PUT" })
async update(id: number, params: UpdateUserParams): Promise<void> {
/* ... */
}
// β
DELETE: μμ
@api({ httpMethod: "DELETE" })
async remove(id: number): Promise<void> {
/* ... */
}
}
λμ μ
class UserModel extends BaseModelClass {
// β GETμΌλ‘ λ°μ΄ν° λ³κ²½
@api({ httpMethod: "GET" })
async deleteUser(id: number): Promise<void> {
// GETμ μμ ν΄μΌ ν¨ (μν λ³κ²½ X)
}
// β POSTλ‘ μ‘°ν
@api({ httpMethod: "POST" })
async getUser(id: number): Promise<User> {
// POSTλ μμ±μ©
}
// β DELETEλ‘ μμ
@api({ httpMethod: "DELETE" })
async updateUser(id: number, params: any): Promise<void> {
// DELETEλ μμ μ©
}
}
λ€μ λ¨κ³
νλΌλ―Έν°
νμ
μ μ λ° κ²μ¦
λ°ν νμ
μλ΅ νμ
μ μνκΈ°
@api λ°μ½λ μ΄ν°
κΈ°λ³Έ μ¬μ©λ²
μλ¬ μ²λ¦¬
API μλ¬ νΈλ€λ§