20. The Friends Endpoints


 

Create Request Models

Create a subdirectory in src/models named requests. Inside src/models/requests add the following models.

Request.js

import { model, Schema } from 'mongoose'

const requestSchema = new Schema({
    sender: {
        username: {
            type: String,
            required: true
        },
        userId: {
            type: Schema.Types.ObjectId,
            ref: 'User'
        }
    },
    receiver: {
        username: {
            type: String,
            required: true
        },
        userId: {
            type: Schema.Types.ObjectId,
            ref: 'User'
        }
    }
},
{ 
    timestamps: true ,
    discriminatorKey: 'kind'
})

const Request = model('Request', requestSchema)

export default Request

FriendRequest.js

import { Schema } from "mongoose";
import Request from "./Request.js";

const friendRequestSchema = new Schema({ })

const FriendRequest = Request.discriminator('FriendRequest', friendRequestSchema)

export default FriendRequest

ChatInvite.js

import { Schema } from "mongoose";
import Request from "./Request.js";

const chatInviteSchema = new Schema({ 
    chat: {
        name: {
            type: String,
            required: true
        },
        chatId: {
            type: Schema.Types.ObjectId,
            ref: 'Chat',
            required: true
        }
    }
})

const ChatInvite = Request.discriminator('ChatInvite', chatInviteSchema)

export default ChatInvite

 

Update the User Model

Add the following properties to your User schema.

...
// Import new Models
import Request from './requests/Request.js'
import FriendRequest from './requests/FriendRequest.js'
import ChatInvite from './requests/ChatInvite.js'


const userSchema = new Schema({
  ...
  /* add new fields
  requests: [ Request.schema ],
  friends: [
    {
      username: {
        type: String,
        required: true
      },
      userId: {
        type: Schema.Types.ObjectId,
        required: true
      }
    }
  ]
})

...

/* Set up requests array to allow different type elements */

userSchema.path('requests').discriminator('FriendRequest', FriendRequest.schema)
userSchema.path('requests').discriminator('ChatInvite', ChatInvite.schema)

const User = model('User', userSchema)

export default User

 

Create Friends Router

Create a file named friends.js in src/routers and add the following code to the file.

import Router from 'express'
import User from '../models/User.js'
import FriendRequest from '../models/requests/FriendRequest.js'
import { auth } from '../middleware/auth.js'

const router = new Router()

router.post('/friend-request/:id', auth, async (req, res) => {
    let sender = req.user
    let receiver = await User.findById(req.params.id)

    if (!receiver) {
        return res.status(404).send('User not found')
    }

    const request = new FriendRequest({
        sender: {
            username: sender.username,
            userId: sender._id
        },
        receiver: {
            username: receiver.username,
            userId: receiver._id
        }
    })

    receiver.requests.push(request)

    try {
        await receiver.save()
        res.send()
    }
    catch (err) {
        console.log(err)
        res.status(500).send()
    }
})

router.patch('/friend-request/:id', auth, async (req, res) => {
    const user = req.user

    const index = user.requests.findIndex(item => item._id.equals(req.params.id));

    if (index === -1) {
        return res.status(404).send()
    }

    const arr = user.requests.splice(index, 1); // remove 1 element at index
    const request = arr[0]

    if (!req.query || !Object.hasOwn(req.query, 'accept')) {
        return res.status(400).send()
    }

    if (req.query.accept === 'false') {
        try {
            await user.save()
            return res.send(user.requests)
        }
        catch (err) {
            return res.status(500).send()
        }
    }

    const sender = await User.findById(request.sender.userId)

    if (sender) {
        try {
            if (!user.friends.some(item => item.userId.equals(sender._id))) {
                user.friends.push({ username: sender.username, userId: sender._id })
                await user.save()
            }
            if (!sender.friends.some(item => item.userId.equals(user._id))) {
                sender.friends.push({ username: user.username, userId: user._id })
                await sender.save()
            }
        }
        catch (err) {
            return res.status(500).send()
        }
    }

    res.send(user.requests)
})

router.get('/friend-requests', auth, async (req, res) => {
    const friendRequests = req.user.requests.filter(item => item.kind === 'FriendRequest')
    res.send(friendRequests)
})

router.delete('/friend/:userId', auth, async (req, res) => {
    const user = req.user

    // TODO: remove user from target's friends list

    user.friends = user.friends.filter(item => !item.userId.equals(req.params.userId))

    try {
        await user.save()
        res.send(user.friends)
    }
    catch (err) {
        console.log(err)
        res.status(500).send()
    }
})

export default router

 

Add Friends Router to app.js

Add the following code to app.js so that express uses the friends router.

...
import friendRouter from './routers/friends.js'
...
app.use(friendRouter)
...

 

Test the Friends Endpoints

Stop your API server, drop your database, and restart your API server.  

In Postman create a folder named Friends and inside the folder create requests to test the new endpoints.

Push to GitHub

Git add, commit, and push.