FullStackOpen Part4-c User Administration メモ

References across collections

ドキュメント型データベースでは複数のコレクションにまたがって参照するとき、以下のようにユーザーに対してノートオブジェクトをそのまま持たせることができる。

[
  {
    username: 'mluukkai',
    _id: 123456,
    notes: [
      {
        content: 'HTML is easy',
        important: false,
      },
      {
        content: 'The most important operations of HTTP protocol are GET and POST',
        important: true,
      },
    ],
  },
  {
    username: 'hellas',
    _id: 141414,
    notes: [
      {
        content:
          'A proper dinosaur codes with Java',
        important: false,
      },
    ],
  },
]

Mongoose Schema for users

今回の構成ではユーザーにノートを持たせる
ユーザーモデルを作成

const mongoose = require('mongoose')

const userSchema = new mongoose.Schema({
  username: String,
  name: String,
  passwordHash: String,
  notes: [
    {
      type: mongoose.Schema.Types.ObjectId,
      ref: 'Note'
    }
  ],
})

userSchema.set('toJSON', {
  transform: (document, returnedObject) => {
    returnedObject.id = returnedObject._id.toString()
    delete returnedObject._id
    delete returnedObject.__v
    // the passwordHash should not be revealed
    delete returnedObject.passwordHash
  }
})

const User = mongoose.model('User', userSchema)

module.exports = User

ユーザーモデルにnotes配列を持たせる

ノートは以下のような形で持たせる
typeはオブジェクトIDとして定義

{
  type: mongoose.Schema.Types.ObjectId,
  ref: 'Note'
}

Creating Users

パスワードを平文で管理するのはよくないのでbcryptを使う
npm install bcrypt

ユーザー用のルーター(./controllers/users.js)を作成

const bcrypt = require('bcrypt')
const usersRouter = require('express').Router()
const User = require('../models/user')

usersRouter.post('/', async (request, response) => {
    const { username, name, password } = request.body

    const saltRounds = 10
    const passwordHash = await bcrypt.hash(password, saltRounds)

    const user = new User({
        username,
        name,
        passwordHash,
    })

    const savedUser = await user.save()

    response.status(201).json(savedUser)
})

module.exports = usersRouter

これをapp.js内で使用

const usersRouter = require('./controllers/users')

// ...

app.use('/api/users', usersRouter)

ユーザー名などユニークである必要であるものをチェックする機能がmongooseには無い
そのためmongoose-unique-validatorを使用する
npm install mongoose-unique-validator

以下のような形でインポートし、userSchema.plugin(uniqueValidator)とすると使用できる

const mongoose = require('mongoose')
const uniqueValidator = require('mongoose-unique-validator')

const userSchema = mongoose.Schema({
  username: {
    type: String,
    required: true,
    unique: true
  },
  name: String,
  passwordHash: String,
  notes: [
    {
      type: mongoose.Schema.Types.ObjectId,
      ref: 'Note'
    }
  ],
})

userSchema.plugin(uniqueValidator)

// ...

Creating a new note

ノートをPOSTするときにUserIdを持たせる

const User = require('../models/user')

//...

notesRouter.post('/', async (request, response) => {
  const body = request.body

  const user = await User.findById(body.userId)

  const note = new Note({
    content: body.content,
    important: body.important === undefined ? false : body.important,
    user: user.id
  })

  const savedNote = await note.save()
  user.notes = user.notes.concat(savedNote._id)
  await user.save()
  
  response.json(savedNote)
})

Noteスキーマも併せて変更

const noteSchema = new mongoose.Schema({
  content: {
    type: String,
    minLength: 5,
    required: true,
  },
  important: Boolean,
  user: String,})

Populate

userを取得したときに関連するnoteを取得するときはpopulate関数を使う

usersRouter.get('/', async (request, response) => {
  const users = await User    .find({}).populate('notes')
  response.json(users)
})

必要なものだけ取りたいときはpopulate関数に必要なプロパティを指定

notesRouter.get('/', async (request, response) => {
  const notes = await Note
    .find({}).populate('user', { username: 1, name: 1 })

  response.json(notes)
})

populate関数はスキーマでtypeでmongoose.Schema.Types.ObjectIdを指定し、refでスキーマを指定してはじめて使用可能なので注意


この記事が気に入ったらサポートをしてみませんか?