http://localhost:1337/api/products/90?populate=*
현재 위의 URL로 id
가 90인 product
에 대한 data를 API를 통해 요청하면 아래와 같이 모든 field의 데이터를 response으로 받는다.
"data": {
"id": 90,
"attributes": {
"productName": "아라비카",
"price": 45000,
"discountRate": 5,
"description": "아라비카 원두입니다.",
"createdAt": "2022-06-09T18:57:04.216Z",
"updatedAt": "2022-06-09T19:08:37.176Z",
"publishedAt": "2022-06-09T19:08:09.580Z",
"images": {...},
"reviews": {...},
"inquiries": {...}
}
하지만 프로젝트에서 특정 product
의 reviews
만 불러오는 API가 필요해서 아래와 같은 response를 반환하는 API를 만들려고 한다.
{
"id": 90,
"reviews": [...]
}
API를 생성하기 위해 필요한 routes
, services
, controller
폴더와 파일들을 직접 만들 수도 있지만 편하게 CLI를 사용하겠다.
yarn strapi generate
위 명령어를 입력하고,
api - Generate a basic API
선택product-reviews
로 정함n
입력 후 엔터과정이 완료되면 src/api
하위 경로에 product-reviews
라는 이름의 폴더가 생성된다.
URL을 통해 Strapi로 전송된 요청은 route에 의해 처리된다. Strapi는 기본적으로 모든 content-type에 대한 route를 생성하며, route는 추가되거나 수정될 수 있다. 요청이 route에 도달하면 handler
인 controller
의 코드가 실행된다.
/src/api/product-reviews/routes/product-reviews.js
의 코드를 아래와 같이 작성한다.
module.exports = {
routes: [
{
method: "GET",
path: "/product-reviews/:id",
handler: "product-reviews.productReviews",
config: {
policies: [],
middlewares: [],
},
},
],
};
service
는 재사용 가능한 함수들의 집합이다. controller
의 로직을 단순화하는 데 유용하다. service
는 Koa
의 ctx
(request와 response에 대한 모든 정보가 들어있는 객체)에 접근할 수 없다.
src/api/product-reviews/services/product-reviews.js
의 코드를 아래와 같이 작성한다.
module.exports = {
productReviews: async (productId) => {
try {
// strapi가 제공하는 entityService의 findOne 메소드로 productId에 해당하는 product를 찾는다.
const entries = await strapi.entityService.findOne(
"api::product.product",
productId,
// fields에 값으로 넣으면 어떠한 field도 response에 담기지 않는다.
// populate: "reviews"로 reviews만 response에 담기도록 한다.
{ fields: [], populate: "reviews" }
);
return entries;
} catch (err) {
return err;
}
},
};
controller
는 Strapi가 action
이라고 이름붙인 메소드가 모여있는 자바스크립트 파일이다.
route를 통해 클라이언트의 요청이 들어오면 action
이 비즈니스 로직을 실행하고 response
를 반환한다.
또한, controller
는 service
와 달리 Koa
의 ctx
객체에 접근 가능하다.
controller
의 비즈니스 로직이 너무 커지면 재사용 가능한 코드를 service
로 분리하는 것이 효율적이다.
src/api/product-reviews/controllers/product-reviews.js
를 아래와 같이 작성한다.
module.exports = {
async productReviews(ctx) {
// ctx 객체에서 url로 들어온 id를 뽑아 productId에 할당한다.
const productId = ctx.params.id;
try {
const data = await strapi
.service("api::product-reviews.product-reviews")
// services에서 만들어놓은 productReviews에 productId를 넘겨준다.
.productReviews(productId);
// service의 결과값인 data를 ctx 객체의 body에 할당한다.
ctx.body = data;
} catch (error) {
ctx.badRequest("product-reviews 컨트롤러에서 에러 발생", {
moreDetails: error,
});
}
},
};
controller
파일 작성까지 마친 뒤, http://localhost:1337/api/product-reviews/90
로 API 요청을 보내면 아래와 같은 403 에러를 반환한다.
이는 product-reviews
라는 content-type
에 대한 권한이 없어서 발생하는 에러이므로, admin panel
로 가서 각 role
의 product-reviews
에 대한 권한을 허용해준다.
그리고 동일한 URL로 다시 API 요청을 보내면 의도했던 형식의 response를 받을 수 있다.