---
# System prepended metadata

title: "\U0001F3C5 Day 35 - 實作新增、取消特定貼文讚"
tags: [Node.js 直播班 - 2024 春季班]

---

# 🏅 Day 35 - 實作新增、取消特定貼文讚

練習整合運用先前提到的技巧、知識：
- router
- Mongoose findOneAndUpdate()
- req.params
- JWT middleware

實作出設計稿 `3-2.全體動態牆-有留言` 頁面的「新增、取消特定貼文讚」的功能

### 新增按讚
設計 POST `/posts/:id/likes` 路由，需登入才能請求（id 為貼文  ID）
* 當使用者登入並驗證 JWT 正確，修改資料庫中該貼文的 likes 欄位，將使用者 ID 加入 likes 陣列中（使用 [`$addToSet`](https://www.mongodb.com/docs/manual/reference/operator/update/addToSet/#-addtoset)，避免加入重複的使用者 ID）
* 若新增成功，回傳成功訊息及修改的貼文 ID、新增的使用者 ID


### 移除按讚
設計 DELETE `/posts/:id/likes` 路由，需登入才能請求（id 為貼文  ID）
* 當使用者登入並驗證 JWT 正確，修改資料庫中該貼文的 likes 欄位，將使用者 ID 由 likes 陣列中移除（使用 [`$pull`](https://www.mongodb.com/docs/manual/reference/operator/update/pull/#-pull)）
* 若移除成功，回傳成功訊息及修改的貼文 ID、已移除的使用者 ID

依照上方需求完整以下程式碼，補上 `...` 的部分

`app.js`
```javascript=
const postsRouter = require('./routes/posts');
app.use('/posts', postsRouter);
```
`routes/posts.js`
```javascript=
// routes/posts.js

const router = express.Router();
const Post = require('../models/postsModel');
const { isAuth } = require('../service/auth');

// 新增按讚
router....('...', ..., handleErrorAsync(async (req, res, next) => {
  const _id = req.params....;
  await Post....(
    { _id },
    { $addToSet: { likes: req.user.... } }
  );
  res.status(201).json({
    status: 'success',
    postId: ...,
    userId: ...
  });
}));


// 移除按讚
router....('...', ..., handleErrorAsync(async (req, res, next) => {
  const _id = req.params....;
  await Post....(
    { _id },
    { $pull: { likes: req.user.... } }
  );
  res.status(201).json({
    status: 'success',
    postId: ...,
    userId: ...
  });
}))

module.exports = router;
```

## 回報流程

將答案寫在 CodePen 並複製 CodePen 連結貼至底下回報就算完成了喔! 
解答位置請參考下圖（需打開程式碼的部分觀看）

![](https://i.imgur.com/vftL5i0.png)

<!-- 解答：  

`app.js`
```javascript=
const postsRouter = require('./routes/posts');
app.use('/posts', postsRouter);
```
`routes/posts.js`
```javascript=
// routes/posts.js

const router = express.Router();
const Post = require('../models/postsModel');
const { isAuth } = require('../service/auth');

// 新增按讚
router.post('/:id/likes', isAuth, handleErrorAsync(async (req, res, next) => {
  const _id = req.params.id;
  await Post.findOneAndUpdate(
    { _id },
    { $addToSet: { likes: req.user.id } }
  );
  res.status(201).json({
    status: 'success',
    postId: _id,
    userId: req.user.id
  });
}));


// 移除按讚
router.delete('/:id/likes', isAuth, handleErrorAsync(async (req, res, next) => {
  const _id = req.params.id;
  await Post.findOneAndUpdate(
    { _id },
    { $pull: { likes: req.user.id } }
  );
  res.status(201).json({
    status: 'success',
    postId: _id,
    userId: req.user.id
  });
}))

module.exports = router;
```
-->




回報區
---
<!-- 
將答案貼至下方表格內，格式：
| Discord 暱稱 | [CodePen](連結) |
-->

|    Discord    |   CodePen ／ 答案  |
|:-------------:|:-----------------:|
| xxx | [CodePen]() |
| jenny7532 | [CodePen](https://codepen.io/wei-chen-wu/pen/gOJrEBB) |
| william威良 | [CodePen](https://codepen.io/snowman12320/pen/jOoqJQN) |
| Chia Pin | [CodePen](https://codepen.io/joker-cat/pen/oNRxRpg) |
| wei | [CodePen](https://hackmd.io/@xu7yoa5cSsqaron7h9XhUw/rJFX7WimR)|
|zaoannihao|[Codepen](https://codepen.io/ckhwdvrx-the-solid/pen/KKLMPMZ?editors=0010)|
|苡安|[hackmd](https://hackmd.io/@L7K9-66lSeagS28AP0_GjQ/BJX4-MnX0)|
| runweiting |[Codepen](https://codepen.io/weiting14/pen/LYobjvQ)|
| mei | [CodePen](https://codepen.io/l_umei/pen/yLWgmdL?editors=0010)|
| Fabio20 | [CodePen](https://codepen.io/fabio7621/pen/XWwMYKK?editors=0010) |
|Lobinda|[HackMD](https://hackmd.io/@Lobinda/ryxJLPY6VR)|
| Hank | [CodePen](https://codepen.io/tw1720/pen/KKLKqjx)|
