diff --git a/Rocket.toml b/Rocket.toml
index 87e1da47bf4e5c14f8d519826524634908e08575..bc6d28f3945da0b84c567f22183121e1b0f45636 100644
--- a/Rocket.toml
+++ b/Rocket.toml
@@ -1,3 +1,7 @@
 [development]
 address = "192.168.0.10"
 port = 5500
+
+[production]
+address = "192.168.0.10"
+port = 5500
diff --git a/src/database/guild.rs b/src/database/guild.rs
index 0939d1bde1930968725bdbe2b559b80cefad9f76..cdbdef2a9cc07bba04e4a0a26dd44f2b8ca7b3f4 100644
--- a/src/database/guild.rs
+++ b/src/database/guild.rs
@@ -1,7 +1,7 @@
 use bson::{bson, doc};
 use serde::{Deserialize, Serialize};
 
-use super::{get_collection, MemberPermissions};
+use super::get_collection;
 use mongodb::options::FindOneOptions;
 
 pub fn find_member_permissions<C: Into<Option<String>>>(
diff --git a/src/database/mod.rs b/src/database/mod.rs
index 91ad67a19803e220dc6be9fb3849179c3f5000bd..ecc10c59b099699d2b56870d458980c538205015 100644
--- a/src/database/mod.rs
+++ b/src/database/mod.rs
@@ -24,10 +24,10 @@ pub fn get_collection(collection: &str) -> Collection {
     get_db().collection(collection)
 }
 
-pub mod permissions;
 pub mod channel;
 pub mod guild;
 pub mod message;
+pub mod permissions;
 pub mod user;
 
 pub use permissions::*;
diff --git a/src/database/permissions.rs b/src/database/permissions.rs
index d915cf6237818ba5fbe612f521edcb79ae05de95..48f62b70c2c2e1b700cc46e3d1f1e8918a99c325 100644
--- a/src/database/permissions.rs
+++ b/src/database/permissions.rs
@@ -9,6 +9,107 @@ bitfield! {
     pub get_send_messages, set_send_messages: 2;
 }
 
-//struct PermissionCalculator {
-    //channel: Option<>,
-//}
\ No newline at end of file
+use super::get_collection;
+use crate::guards::channel::ChannelRef;
+use crate::guards::guild::GuildRef;
+
+use bson::{bson, doc};
+use mongodb::options::FindOneOptions;
+
+pub struct PermissionCalculator {
+    pub user_id: String,
+    pub channel: Option<ChannelRef>,
+    pub guild: Option<GuildRef>,
+}
+
+impl PermissionCalculator {
+    pub fn new(user_id: String) -> PermissionCalculator {
+        PermissionCalculator {
+            user_id,
+            channel: None,
+            guild: None,
+        }
+    }
+
+    pub fn channel(self, channel: ChannelRef) -> PermissionCalculator {
+        PermissionCalculator {
+            channel: Some(channel),
+            ..self
+        }
+    }
+
+    pub fn guild(self, guild: GuildRef) -> PermissionCalculator {
+        PermissionCalculator {
+            guild: Some(guild),
+            ..self
+        }
+    }
+
+    pub fn calculate(self) -> u8 {
+        let guild = if let Some(value) = self.guild {
+            Some(value)
+        } else if let Some(channel) = &self.channel {
+            match channel.channel_type {
+                0..=1 => None,
+                2 => {
+                    if let Some(id) = &channel.guild {
+                        GuildRef::from(id.clone())
+                    } else {
+                        None
+                    }
+                }
+                _ => None,
+            }
+        } else {
+            None
+        };
+
+        let mut permissions = 0;
+        if let Some(guild) = guild {
+            if let Some(_data) = guild.fetch_data_given(
+                doc! {
+                    "members": {
+                        "$elemMatch": {
+                            "id": &self.user_id,
+                        }
+                    }
+                },
+                doc! { }
+            ) {
+                if guild.owner == self.user_id {
+                    return u8::MAX;
+                }
+
+                permissions = guild.default_permissions;
+            }
+        }
+
+        if let Some(channel) = &self.channel {
+            match channel.channel_type {
+                0 => {
+                    if let Some(arr) = &channel.recipients {
+                        for item in arr {
+                            if item == &self.user_id {
+                                permissions = 49;
+                                break;
+                            }
+                        }
+                    }
+                },
+                1 => {
+                    unreachable!()
+                },
+                2 => {
+                    // nothing implemented yet
+                },
+                _ => {}
+            }
+        }
+
+        permissions as u8
+    }
+
+    pub fn as_permission(self) -> MemberPermissions<[u8; 1]> {
+        MemberPermissions([ self.calculate() ])
+    }
+}
diff --git a/src/guards/auth.rs b/src/guards/auth.rs
index c9381f18166d20b3fd40dd1d1f15f5ac5aa2cc63..66df8ea8f5f29e5af6515c23ef64231e8b610769 100644
--- a/src/guards/auth.rs
+++ b/src/guards/auth.rs
@@ -1,11 +1,51 @@
 use rocket::http::{RawStr, Status};
 use rocket::request::{self, FromParam, FromRequest, Request};
 use rocket::Outcome;
-
-use bson::{bson, doc, from_bson};
+use bson::{bson, doc, from_bson, Document};
+use serde::{Deserialize, Serialize};
+use mongodb::options::FindOneOptions;
 
 use crate::database;
-use database::user::User;
+use database::user::{User, UserRelationship};
+
+#[derive(Serialize, Deserialize, Debug)]
+pub struct UserRef {
+    pub id: String,
+    pub username: String,
+    pub email_verified: bool,
+}
+
+impl UserRef {
+    pub fn fetch_data(&self, projection: Document) -> Option<Document> {
+        database::get_collection("users")
+            .find_one(
+                doc! { "_id": &self.id },
+                FindOneOptions::builder().projection(projection).build(),
+            )
+            .expect("Failed to fetch user from database.")
+    }
+
+    pub fn fetch_relationships(&self) -> Option<Vec<UserRelationship>> {
+        let user = database::get_collection("users")
+            .find_one(
+                doc! { "_id": &self.id },
+                FindOneOptions::builder().projection(doc! { "relations": 1 }).build(),
+            )
+            .expect("Failed to fetch user relationships from database.")
+            .expect("Missing user document.");
+        
+        if let Ok(arr) = user.get_array("relations") {
+            let mut relationships = vec![];
+            for item in arr {
+                relationships.push(from_bson(item.clone()).unwrap());
+            }
+
+            Some(relationships)
+        } else {
+            None
+        }
+    }
+}
 
 #[derive(Debug)]
 pub enum AuthError {
@@ -14,6 +54,45 @@ pub enum AuthError {
     Invalid,
 }
 
+impl<'a, 'r> FromRequest<'a, 'r> for UserRef {
+    type Error = AuthError;
+
+    fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, Self::Error> {
+        let keys: Vec<_> = request.headers().get("x-auth-token").collect();
+        match keys.len() {
+            0 => Outcome::Failure((Status::Forbidden, AuthError::Missing)),
+            1 => {
+                let key = keys[0];
+                let result = database::get_collection("users")
+                    .find_one(
+                        doc! { "access_token": key },
+                        FindOneOptions::builder()
+                            .projection(doc! {
+                                "_id": 1,
+                                "username": 1,
+                                "email_verification.verified": 1,
+                            })
+                            .build(),
+                    )
+                    .unwrap();
+
+                if let Some(user) = result {
+                    Outcome::Success(
+                        UserRef {
+                            id: user.get_str("_id").unwrap().to_string(),
+                            username: user.get_str("username").unwrap().to_string(),
+                            email_verified: user.get_document("email_verification").unwrap().get_bool("verified").unwrap(),
+                        }
+                    )
+                } else {
+                    Outcome::Failure((Status::Forbidden, AuthError::Invalid))
+                }
+            }
+            _ => Outcome::Failure((Status::BadRequest, AuthError::BadCount)),
+        }
+    }
+}
+
 impl<'a, 'r> FromRequest<'a, 'r> for User {
     type Error = AuthError;
 
@@ -39,6 +118,38 @@ impl<'a, 'r> FromRequest<'a, 'r> for User {
     }
 }
 
+impl<'r> FromParam<'r> for UserRef {
+    type Error = &'r RawStr;
+
+    fn from_param(param: &'r RawStr) -> Result<Self, Self::Error> {
+        let col = database::get_db().collection("users");
+        let result = database::get_collection("users")
+                    .find_one(
+                        doc! { "_id": param.to_string() },
+                        FindOneOptions::builder()
+                            .projection(doc! {
+                                "_id": 1,
+                                "username": 1,
+                                "email_verification.verified": 1,
+                            })
+                            .build(),
+                    )
+                    .unwrap();
+
+        if let Some(user) = result {
+            Ok(
+                UserRef {
+                    id: user.get_str("_id").unwrap().to_string(),
+                    username: user.get_str("username").unwrap().to_string(),
+                    email_verified: user.get_document("email_verification").unwrap().get_bool("verified").unwrap(),
+                }
+            )
+        } else {
+            Err(param)
+        }
+    }
+}
+
 impl<'r> FromParam<'r> for User {
     type Error = &'r RawStr;
 
diff --git a/src/guards/channel.rs b/src/guards/channel.rs
index ee42bf4ea667f2e59cd8566cd28a379e1533623d..254ded4b3c750d45f7b58c557489b030b0aae034 100644
--- a/src/guards/channel.rs
+++ b/src/guards/channel.rs
@@ -1,6 +1,8 @@
-use bson::{bson, doc, from_bson};
+use bson::{bson, doc, from_bson, Document};
+use mongodb::options::FindOneOptions;
 use rocket::http::RawStr;
 use rocket::request::FromParam;
+use serde::{Deserialize, Serialize};
 
 use crate::database;
 
@@ -24,6 +26,56 @@ impl<'r> FromParam<'r> for Channel {
     }
 }
 
+#[derive(Serialize, Deserialize, Debug, Clone)]
+pub struct ChannelRef {
+    #[serde(rename = "_id")]
+    pub id: String,
+    #[serde(rename = "type")]
+    pub channel_type: u8,
+
+    // information required for permission calculations
+    pub recipients: Option<Vec<String>>,
+    pub guild: Option<String>,
+}
+
+impl ChannelRef {
+    pub fn fetch_data(&self, projection: Document) -> Option<Document> {
+        database::get_collection("channels")
+            .find_one(
+                doc! { "_id": &self.id },
+                FindOneOptions::builder().projection(projection).build(),
+            )
+            .expect("Failed to fetch channel from database.")
+    }
+}
+
+impl<'r> FromParam<'r> for ChannelRef {
+    type Error = &'r RawStr;
+
+    fn from_param(param: &'r RawStr) -> Result<Self, Self::Error> {
+        let id = param.to_string();
+        let result = database::get_collection("channels")
+            .find_one(
+                doc! { "_id": id },
+                FindOneOptions::builder()
+                    .projection(doc! {
+                        "_id": 1,
+                        "type": 1,
+                        "recipients": 1,
+                        "guild": 1,
+                    })
+                    .build(),
+            )
+            .unwrap();
+
+        if let Some(channel) = result {
+            Ok(from_bson(bson::Bson::Document(channel)).expect("Failed to deserialize channel."))
+        } else {
+            Err(param)
+        }
+    }
+}
+
 impl<'r> FromParam<'r> for Message {
     type Error = &'r RawStr;
 
diff --git a/src/guards/guild.rs b/src/guards/guild.rs
index 58c258386cae63365dfdc361ce0d48b612451e90..b4af1fd525afae2c54d00e1506dcaabfdf9922ac 100644
--- a/src/guards/guild.rs
+++ b/src/guards/guild.rs
@@ -1,10 +1,73 @@
-use bson::{bson, doc, from_bson};
+use bson::{bson, doc, from_bson, Document};
+use mongodb::options::FindOneOptions;
 use rocket::http::RawStr;
 use rocket::request::FromParam;
+use serde::{Deserialize, Serialize};
 
 use crate::database;
+use crate::database::guild::Guild;
 
-use database::guild::Guild;
+#[derive(Serialize, Deserialize, Debug, Clone)]
+pub struct GuildRef {
+    #[serde(rename = "_id")]
+    pub id: String,
+
+    pub owner: String,
+    pub default_permissions: i32,
+}
+
+impl GuildRef {
+    pub fn from(id: String) -> Option<GuildRef> {
+        match database::get_collection("guilds").find_one(
+            doc! { "_id": id },
+            FindOneOptions::builder()
+                .projection(doc! {
+                    "owner": 1,
+                    "default_permissions": 1
+                })
+                .build(),
+        ) {
+            Ok(result) => match result {
+                Some(doc) => {
+                    Some(from_bson(bson::Bson::Document(doc)).expect("Failed to unwrap guild."))
+                }
+                None => None,
+            },
+            Err(_) => None,
+        }
+    }
+
+    pub fn fetch_data(&self, projection: Document) -> Option<Document> {
+        database::get_collection("guilds")
+            .find_one(
+                doc! { "_id": &self.id },
+                FindOneOptions::builder().projection(projection).build(),
+            )
+            .expect("Failed to fetch guild from database.")
+    }
+
+    pub fn fetch_data_given(&self, mut filter: Document, projection: Document) -> Option<Document> {
+        filter.insert("_id", self.id.clone());
+        database::get_collection("guilds")
+            .find_one(
+                filter,
+                FindOneOptions::builder().projection(projection).build(),
+            )
+            .expect("Failed to fetch guild from database.")
+    }
+}
+
+impl<'r> FromParam<'r> for GuildRef {
+    type Error = &'r RawStr;
+
+    fn from_param(param: &'r RawStr) -> Result<Self, Self::Error> {
+        if let Some(guild) = GuildRef::from(param.to_string()) {
+            Ok(guild)
+        } else {
+            Err(param)
+        }
+    }
+}
 
 impl<'r> FromParam<'r> for Guild {
     type Error = &'r RawStr;
diff --git a/src/routes/channel.rs b/src/routes/channel.rs
index cd53de2fb44f5a6efcbbc110bba865c4dde7f1a8..5dc57bede840f7690af739355a326ce33123f560 100644
--- a/src/routes/channel.rs
+++ b/src/routes/channel.rs
@@ -1,5 +1,7 @@
 use super::Response;
-use crate::database::{self, channel::Channel, message::Message, user::User};
+use crate::database::{self, channel::Channel, message::Message, user::User, PermissionCalculator};
+use crate::guards::channel::ChannelRef;
+use crate::guards::auth::UserRef;
 use crate::websocket;
 
 use bson::{bson, doc, from_bson, Bson::UtcDatetime};
@@ -17,53 +19,65 @@ pub enum ChannelType {
     GUILDCHANNEL = 2,
 }
 
-fn has_permission(user: &User, target: &Channel) -> bool {
-    match target.channel_type {
-        0..=1 => {
-            if let Some(arr) = &target.recipients {
-                for item in arr {
-                    if item == &user.id {
-                        return true;
-                    }
-                }
+macro_rules! with_permissions {
+    ($user: expr, $target: expr) => {
+        {
+            let permissions = PermissionCalculator::new($user.id.clone())
+                .channel($target.clone())
+                .as_permission();
+            
+            if !permissions.get_access() {
+                return None;
             }
 
-            false
+            permissions
         }
-        2 => false,
-        _ => false,
-    }
-}
-
-fn get_recipients(target: &Channel) -> Vec<String> {
-    match target.channel_type {
-        0..=1 => target.recipients.clone().unwrap(),
-        _ => vec![],
-    }
+    };
 }
 
 /// fetch channel information
 #[get("/<target>")]
-pub fn channel(user: User, target: Channel) -> Option<Response> {
-    if !has_permission(&user, &target) {
-        return None;
-    }
+pub fn channel(user: UserRef, target: ChannelRef) -> Option<Response> {
+    with_permissions!(user, target);
 
-    Some(Response::Success(json!({
-        "id": target.id,
-        "type": target.channel_type,
-        "recipients": get_recipients(&target),
-    })))
+    match target.channel_type {
+        0..=1 => Some(Response::Success(
+            json!({
+                "id": target.id,
+                "type": target.channel_type,
+                "recipients": target.recipients,
+            })
+        )),
+        2 => {
+            if let Some(info) = target.fetch_data(
+                doc! {
+                    "name": 1,
+                    "description": 1,
+                }
+            ) {
+                Some(Response::Success(
+                    json!({
+                        "id": target.id,
+                        "type": target.channel_type,
+                        "guild": target.guild,
+                        "name": info.get_str("name").unwrap(),
+                        "description": info.get_str("description").unwrap_or(""),
+                    })
+                ))
+            } else {
+                None
+            }
+        },
+        _ => unreachable!()
+    }
 }
 
 /// delete channel
 /// or leave group DM
 /// or close DM conversation
 #[delete("/<target>")]
-pub fn delete(user: User, target: Channel) -> Option<Response> {
-    if !has_permission(&user, &target) {
-        return None;
-    }
+pub fn delete(user: UserRef, target: ChannelRef) -> Option<Response> {
+    with_permissions!(user, target);
 
     let col = database::get_collection("channels");
     Some(match target.channel_type {
@@ -93,10 +107,8 @@ pub fn delete(user: User, target: Channel) -> Option<Response> {
 
 /// fetch channel messages
 #[get("/<target>/messages")]
-pub fn messages(user: User, target: Channel) -> Option<Response> {
-    if !has_permission(&user, &target) {
-        return None;
-    }
+pub fn messages(user: UserRef, target: ChannelRef) -> Option<Response> {
+    with_permissions!(user, target);
 
     let col = database::get_collection("messages");
     let result = col.find(doc! { "channel": target.id }, None).unwrap();
@@ -124,10 +136,8 @@ pub struct SendMessage {
 
 /// send a message to a channel
 #[post("/<target>/messages", data = "<message>")]
-pub fn send_message(user: User, target: Channel, message: Json<SendMessage>) -> Option<Response> {
-    if !has_permission(&user, &target) {
-        return None;
-    }
+pub fn send_message(user: UserRef, target: ChannelRef, message: Json<SendMessage>) -> Option<Response> {
+    with_permissions!(user, target);
 
     let content: String = message.content.chars().take(2000).collect();
     let nonce: String = message.nonce.chars().take(32).collect();
@@ -164,7 +174,7 @@ pub fn send_message(user: User, target: Channel, message: Json<SendMessage>) ->
                 .unwrap();
             }
 
-            websocket::queue_message(
+            /*websocket::queue_message(
                 get_recipients(&target),
                 json!({
                     "type": "message",
@@ -177,7 +187,7 @@ pub fn send_message(user: User, target: Channel, message: Json<SendMessage>) ->
                     },
                 })
                 .to_string(),
-            );
+            );*/
 
             Response::Success(json!({ "id": id }))
         } else {
@@ -190,10 +200,8 @@ pub fn send_message(user: User, target: Channel, message: Json<SendMessage>) ->
 
 /// get a message
 #[get("/<target>/messages/<message>")]
-pub fn get_message(user: User, target: Channel, message: Message) -> Option<Response> {
-    if !has_permission(&user, &target) {
-        return None;
-    }
+pub fn get_message(user: UserRef, target: ChannelRef, message: Message) -> Option<Response> {
+    with_permissions!(user, target);
 
     let prev =
         // ! CHECK IF USER HAS PERMISSION TO VIEW EDITS OF MESSAGES
@@ -228,14 +236,12 @@ pub struct EditMessage {
 /// edit a message
 #[patch("/<target>/messages/<message>", data = "<edit>")]
 pub fn edit_message(
-    user: User,
-    target: Channel,
+    user: UserRef,
+    target: ChannelRef,
     message: Message,
     edit: Json<EditMessage>,
 ) -> Option<Response> {
-    if !has_permission(&user, &target) {
-        return None;
-    }
+    with_permissions!(user, target);
 
     Some(if message.author != user.id {
         Response::Unauthorized(json!({ "error": "You did not send this message." }))
@@ -266,7 +272,7 @@ pub fn edit_message(
             None,
         ) {
             Ok(_) => {
-                websocket::queue_message(
+                /*websocket::queue_message(
                     get_recipients(&target),
                     json!({
                         "type": "message_update",
@@ -278,7 +284,7 @@ pub fn edit_message(
                         },
                     })
                     .to_string(),
-                );
+                );*/
 
                 Response::Result(super::Status::Ok)
             }
@@ -291,10 +297,8 @@ pub fn edit_message(
 
 /// delete a message
 #[delete("/<target>/messages/<message>")]
-pub fn delete_message(user: User, target: Channel, message: Message) -> Option<Response> {
-    if !has_permission(&user, &target) {
-        return None;
-    }
+pub fn delete_message(user: UserRef, target: ChannelRef, message: Message) -> Option<Response> {
+    with_permissions!(user, target);
 
     Some(if message.author != user.id {
         Response::Unauthorized(json!({ "error": "You did not send this message." }))
@@ -303,7 +307,7 @@ pub fn delete_message(user: User, target: Channel, message: Message) -> Option<R
 
         match col.delete_one(doc! { "_id": message.id.clone() }, None) {
             Ok(_) => {
-                websocket::queue_message(
+                /*websocket::queue_message(
                     get_recipients(&target),
                     json!({
                         "type": "message_delete",
@@ -313,7 +317,7 @@ pub fn delete_message(user: User, target: Channel, message: Message) -> Option<R
                         },
                     })
                     .to_string(),
-                );
+                );*/
 
                 Response::Result(super::Status::Ok)
             }
diff --git a/src/routes/guild.rs b/src/routes/guild.rs
index 76b807c29ee4ec28314e95d43e40d6e1d04d2315..9457a3e7965e9300862a4c6425f2a79a7928a291 100644
--- a/src/routes/guild.rs
+++ b/src/routes/guild.rs
@@ -5,6 +5,7 @@ use crate::database::{
     guild::{find_member_permissions, Guild},
     user::User,
 };
+use crate::guards::auth::UserRef;
 
 use bson::{bson, doc, from_bson, Bson};
 use rocket_contrib::json::{Json, JsonValue};
@@ -15,7 +16,7 @@ use super::channel::ChannelType;
 
 /// fetch your guilds
 #[get("/@me")]
-pub fn my_guilds(user: User) -> Response {
+pub fn my_guilds(user: UserRef) -> Response {
     let col = database::get_collection("guilds");
     let guilds = col
         .find(
@@ -46,7 +47,7 @@ pub fn my_guilds(user: User) -> Response {
 
 /// fetch a guild
 #[get("/<target>")]
-pub fn guild(user: User, target: Guild) -> Option<Response> {
+pub fn guild(user: UserRef, target: Guild) -> Option<Response> {
     if find_member_permissions(user.id.clone(), target.id.clone(), None) == 0 {
         return None;
     }
@@ -72,7 +73,7 @@ pub fn guild(user: User, target: Guild) -> Option<Response> {
                     .expect("Failed to unwrap channel.");
 
                 channels.push(json!({
-                    "_id": channel.id,
+                    "id": channel.id,
                     "last_message": channel.last_message,
                     "name": channel.name,
                     "description": channel.description,
@@ -102,8 +103,8 @@ pub struct CreateGuild {
 
 /// create a new guild
 #[post("/create", data = "<info>")]
-pub fn create_guild(user: User, info: Json<CreateGuild>) -> Response {
-    if !user.email_verification.verified {
+pub fn create_guild(user: UserRef, info: Json<CreateGuild>) -> Response {
+    if !user.email_verified {
         return Response::Unauthorized(json!({ "error": "Email not verified!" }));
     }
 
diff --git a/src/routes/user.rs b/src/routes/user.rs
index 516745b7b9e31a5ecac2351c6e6f815f3b3c0777..01dcc3f2d8b7c1dcfb92c18d1e3ca00aece02604 100644
--- a/src/routes/user.rs
+++ b/src/routes/user.rs
@@ -1,27 +1,75 @@
 use super::Response;
-use crate::database::{self, channel::Channel, user::User};
+use crate::database::{self, channel::Channel, user::UserRelationship};
 use crate::routes::channel;
+use crate::guards::auth::UserRef;
 
 use bson::{bson, doc, from_bson};
 use mongodb::options::FindOptions;
-use rocket_contrib::json::{Json, JsonValue};
+use rocket_contrib::json::Json;
 use serde::{Deserialize, Serialize};
 use ulid::Ulid;
 
+enum Relationship {
+    FRIEND = 0,
+    OUTGOING = 1,
+    INCOMING = 2,
+    BLOCKED = 3,
+    BLOCKEDOTHER = 4,
+    NONE = 5,
+    SELF = 6,
+}
+
+fn get_relationship_internal(user_id: &str, target_id: &str, relationships: &Option<Vec<UserRelationship>>) -> Relationship {
+    if user_id == target_id {
+        return Relationship::SELF;
+    }
+
+    if let Some(arr) = &relationships {
+        for entry in arr {
+            if entry.id == target_id {
+                match entry.status {
+                    0 => return Relationship::FRIEND,
+                    1 => return Relationship::OUTGOING,
+                    2 => return Relationship::INCOMING,
+                    3 => return Relationship::BLOCKED,
+                    4 => return Relationship::BLOCKEDOTHER,
+                    _ => return Relationship::NONE,
+                }
+            }
+        }
+    }
+
+    Relationship::NONE
+}
+
+fn get_relationship(a: &UserRef, b: &UserRef) -> Relationship {
+    if a.id == b.id {
+        return Relationship::SELF;
+    }
+
+    get_relationship_internal(&a.id, &b.id, &a.fetch_relationships())
+}
+
 /// retrieve your user information
 #[get("/@me")]
-pub fn me(user: User) -> JsonValue {
-    json!({
-        "id": user.id,
-        "username": user.username,
-        "email": user.email,
-        "verified": user.email_verification.verified,
-    })
+pub fn me(user: UserRef) -> Response {
+    if let Some(info) = user.fetch_data(doc! { "email": 1 }) {
+        Response::Success(
+            json!({
+                "id": user.id,
+                "username": user.username,
+                "email": info.get_str("email").unwrap(),
+                "verified": user.email_verified,
+            })
+        )
+    } else {
+        Response::InternalServerError(json!({ "error": "Failed to fetch information from database." }))
+    }
 }
 
 /// retrieve another user's information
 #[get("/<target>")]
-pub fn user(user: User, target: User) -> Response {
+pub fn user(user: UserRef, target: UserRef) -> Response {
     Response::Success(json!({
         "id": target.id,
         "username": target.username,
@@ -37,25 +85,30 @@ pub struct Query {
 /// lookup a user on Revolt
 /// currently only supports exact username searches
 #[post("/lookup", data = "<query>")]
-pub fn lookup(user: User, query: Json<Query>) -> Response {
+pub fn lookup(user: UserRef, query: Json<Query>) -> Response {
+    let relationships = user.fetch_relationships();
     let col = database::get_collection("users");
 
     let users = col
         .find(
             doc! { "username": query.username.clone() },
-            FindOptions::builder().limit(10).build(),
+            FindOptions::builder()
+                .projection(doc! { "_id": 1, "username": 1 })
+                .limit(10)
+                .build(),
         )
         .expect("Failed user lookup");
 
     let mut results = Vec::new();
     for item in users {
-        let u: User =
-            from_bson(bson::Bson::Document(item.unwrap())).expect("Failed to unwrap user.");
-        results.push(json!({
-            "id": u.id,
-            "username": u.username,
-            "relationship": get_relationship(&user, &u) as u8
-        }));
+        if let Ok(doc) = item {
+            let id = doc.get_str("id").unwrap();
+            results.push(json!({
+                "id": id.clone(),
+                "username": doc.get_str("username").unwrap(),
+                "relationship": get_relationship_internal(&user.id, &id, &relationships) as u8
+            }));
+        }
     }
 
     Response::Success(json!(results))
@@ -63,7 +116,7 @@ pub fn lookup(user: User, query: Json<Query>) -> Response {
 
 /// retrieve all of your DMs
 #[get("/@me/dms")]
-pub fn dms(user: User) -> Response {
+pub fn dms(user: UserRef) -> Response {
     let col = database::get_collection("channels");
 
     let results = col
@@ -101,7 +154,7 @@ pub fn dms(user: User) -> Response {
 
 /// open a DM with a user
 #[get("/<target>/dm")]
-pub fn dm(user: User, target: User) -> Response {
+pub fn dm(user: UserRef, target: UserRef) -> Response {
     let col = database::get_collection("channels");
 
     match col.find_one(
@@ -128,44 +181,13 @@ pub fn dm(user: User, target: User) -> Response {
 	}
 }
 
-enum Relationship {
-    FRIEND = 0,
-    OUTGOING = 1,
-    INCOMING = 2,
-    BLOCKED = 3,
-    BLOCKEDOTHER = 4,
-    NONE = 5,
-    SELF = 6,
-}
-
-fn get_relationship(a: &User, b: &User) -> Relationship {
-    if a.id == b.id {
-        return Relationship::SELF;
-    }
-
-    if let Some(arr) = &b.relations {
-        for entry in arr {
-            if entry.id == a.id {
-                match entry.status {
-                    0 => return Relationship::FRIEND,
-                    1 => return Relationship::INCOMING,
-                    2 => return Relationship::OUTGOING,
-                    3 => return Relationship::BLOCKEDOTHER,
-                    4 => return Relationship::BLOCKED,
-                    _ => return Relationship::NONE,
-                }
-            }
-        }
-    }
-
-    Relationship::NONE
-}
-
 /// retrieve all of your friends
 #[get("/@me/friend")]
-pub fn get_friends(user: User) -> Response {
+pub fn get_friends(user: UserRef) -> Response {
+    let relationships = user.fetch_relationships();
+
     let mut results = Vec::new();
-    if let Some(arr) = user.relations {
+    if let Some(arr) = relationships {
         for item in arr {
             results.push(json!({
                 "id": item.id,
@@ -179,7 +201,7 @@ pub fn get_friends(user: User) -> Response {
 
 /// retrieve friend status with user
 #[get("/<target>/friend")]
-pub fn get_friend(user: User, target: User) -> Response {
+pub fn get_friend(user: UserRef, target: UserRef) -> Response {
     let relationship = get_relationship(&user, &target);
 
     Response::Success(json!({ "status": relationship as u8 }))
@@ -187,9 +209,9 @@ pub fn get_friend(user: User, target: User) -> Response {
 
 /// create or accept a friend request
 #[put("/<target>/friend")]
-pub fn add_friend(user: User, target: User) -> Response {
-    let col = database::get_collection("users");
+pub fn add_friend(user: UserRef, target: UserRef) -> Response {
     let relationship = get_relationship(&user, &target);
+    let col = database::get_collection("users");
 
     match relationship {
         Relationship::FRIEND => Response::BadRequest(json!({ "error": "Already friends." })),
@@ -300,9 +322,9 @@ pub fn add_friend(user: User, target: User) -> Response {
 
 /// remove a friend or deny a request
 #[delete("/<target>/friend")]
-pub fn remove_friend(user: User, target: User) -> Response {
-    let col = database::get_collection("users");
+pub fn remove_friend(user: UserRef, target: UserRef) -> Response {
     let relationship = get_relationship(&user, &target);
+    let col = database::get_collection("users");
 
     match relationship {
         Relationship::FRIEND | Relationship::OUTGOING | Relationship::INCOMING => {