Ch 02. API 라우트 작성하기
이 장의 예제 코드는 Prisma를 사용합니다. Prisma 설치와 usePrisma() 유틸리티 작성은 Ch 04에서 다룹니다. 지금은 API 라우트 구조와 패턴에 집중하고, Ch 04를 마친 뒤 예제를 직접 실행해보세요.
Nitro의 API 라우트는 파일 이름이 곧 URL 주소입니다. 파일을 어디에 어떤 이름으로 만드는지에 따라 HTTP 메서드와 경로가 자동으로 결정됩니다. 별도의 라우터 설정이 필요 없습니다.
파일 이름 = URL 패턴
server/api/ 아래에 만드는 파일의 이름이 그대로 API 경로가 됩니다. 파일 이름에 HTTP 메서드를 붙이면 해당 메서드만 처리하는 엔드포인트가 됩니다.
| 파일 경로 | 엔드포인트 |
|---|---|
server/api/posts.ts |
GET /api/posts |
server/api/posts.post.ts |
POST /api/posts |
server/api/posts/[id].ts |
GET /api/posts/:id |
server/api/posts/[id].delete.ts |
DELETE /api/posts/:id |
server/api/posts/[id].put.ts |
PUT /api/posts/:id |
메서드 접미사(.get, .post, .put, .delete, .patch)를 붙이지 않으면 모든 HTTP 메서드를 처리합니다.
목록 조회와 생성 API
게시글 목록을 가져오는 GET과 새 게시글을 만드는 POST는 같은 경로(/api/posts)를 사용하지만 파일을 분리해서 작성합니다.
// server/api/posts/index.get.tsexport default defineEventHandler(async (event) => { const posts = await usePrisma().post.findMany() return posts})
// server/api/posts/index.post.tsexport default defineEventHandler(async (event) => { const body = await readBody(event) const post = await usePrisma().post.create({ data: body }) return post})
index.get.ts와 index.post.ts는 각각 GET /api/posts, POST /api/posts에 매핑됩니다. index 키워드가 상위 경로를 가리킵니다.
동적 파라미터 처리
URL 경로에 변하는 값이 들어오는 경우 대괄호([id])로 파일 이름을 만듭니다. getRouterParam 함수로 그 값을 꺼냅니다.
// server/api/posts/[id].get.tsexport default defineEventHandler(async (event) => { const id = Number(getRouterParam(event, 'id')) const post = await usePrisma().post.findUnique({ where: { id } }) return post})
getRouterParam의 반환값은 항상 문자열이므로, 숫자 ID가 필요하다면 Number()로 변환해야 합니다.
쿼리스트링 파싱
목록 조회 시 페이지 번호나 검색어를 쿼리스트링으로 전달받는 경우 getQuery를 사용합니다.
// server/api/posts/index.get.tsexport default defineEventHandler(async (event) => { const query = getQuery(event) const page = Number(query.page ?? 1) const limit = Number(query.limit ?? 10) const skip = (page - 1) * limit const posts = await usePrisma().post.findMany({ skip, take: limit, orderBy: { createdAt: 'desc' } }) return { posts, page, limit }})
/api/posts?page=2&limit=5처럼 요청하면 두 번째 페이지의 5개 게시글을 반환합니다.
삭제 API
특정 게시글을 삭제하는 엔드포인트는 파일 이름에 .delete를 붙입니다.
// server/api/posts/[id].delete.tsexport default defineEventHandler(async (event) => { const id = Number(getRouterParam(event, 'id')) await usePrisma().post.delete({ where: { id } }) return { success: true }})
파일 이름 하나로 메서드와 경로를 모두 표현할 수 있다는 점이 Nitro 파일 기반 라우팅의 핵심입니다. 폴더 구조가 그대로 API 설계서 역할을 해 주기 때문에 프로젝트가 커져도 구조를 파악하기 쉽습니다.