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でスキーマを指定してはじめて使用可能なので注意