From 3aa3d75a1e04b6ed4bc7566f86f45e6883c5c39b Mon Sep 17 00:00:00 2001
From: Nutomic <me@nutomic.com>
Date: Tue, 3 May 2022 19:44:13 +0200
Subject: [PATCH] Add cargo feature for building lemmy_api_common with mininum
 deps (#2243)

---
 .drone.yml                                    |   5 +
 Cargo.lock                                    |  12 +-
 crates/api/Cargo.toml                         |  12 +-
 crates/api/src/comment/like.rs                |   7 +-
 crates/api/src/comment/mark_as_read.rs        |   5 +-
 crates/api/src/comment/save.rs                |   5 +-
 crates/api/src/comment_report/create.rs       |   6 +-
 crates/api/src/comment_report/list.rs         |   3 +-
 crates/api/src/comment_report/resolve.rs      |   6 +-
 crates/api/src/community/add_mod.rs           |   6 +-
 crates/api/src/community/ban.rs               |   7 +-
 crates/api/src/community/block.rs             |   5 +-
 crates/api/src/community/follow.rs            |  12 +-
 crates/api/src/community/hide.rs              |   6 +-
 crates/api/src/community/transfer.rs          |   9 +-
 crates/api/src/lib.rs                         |   4 +-
 crates/api/src/local_user/add_admin.rs        |   6 +-
 crates/api/src/local_user/ban_person.rs       |   7 +-
 crates/api/src/local_user/block.rs            |   5 +-
 crates/api/src/local_user/change_password.rs  |   4 +-
 .../local_user/change_password_after_reset.rs |   3 +-
 crates/api/src/local_user/get_captcha.rs      |   2 +-
 crates/api/src/local_user/list_banned.rs      |   6 +-
 crates/api/src/local_user/login.rs            |   5 +-
 .../local_user/notifications/list_mentions.rs |   5 +-
 .../local_user/notifications/list_replies.rs  |   5 +-
 .../local_user/notifications/mark_all_read.rs |   3 +-
 .../notifications/mark_mention_read.rs        |   5 +-
 .../local_user/notifications/unread_count.rs  |   7 +-
 crates/api/src/local_user/report_count.rs     |   5 +-
 crates/api/src/local_user/reset_password.rs   |   5 +-
 crates/api/src/local_user/save_settings.rs    |  14 +-
 crates/api/src/local_user/verify_email.rs     |   5 +-
 crates/api/src/post/get_link_metadata.rs      |   7 +-
 crates/api/src/post/like.rs                   |  14 +-
 crates/api/src/post/lock.rs                   |  12 +-
 crates/api/src/post/mark_read.rs              |   7 +-
 crates/api/src/post/save.rs                   |   6 +-
 crates/api/src/post/sticky.rs                 |  12 +-
 crates/api/src/post_report/create.rs          |   6 +-
 crates/api/src/post_report/list.rs            |   3 +-
 crates/api/src/post_report/resolve.rs         |   6 +-
 crates/api/src/private_message/mark_read.rs   |   3 +-
 crates/api/src/site/config/read.rs            |   3 +-
 crates/api/src/site/config/update.rs          |   3 +-
 crates/api/src/site/leave_admin.rs            |   9 +-
 crates/api/src/site/mod_log.rs                |  28 +-
 .../site/registration_applications/approve.rs |  14 +-
 .../site/registration_applications/list.rs    |   4 +-
 .../registration_applications/unread_count.rs |   6 +-
 crates/api/src/site/resolve_object.rs         |  10 +-
 crates/api/src/site/search.rs                 |   9 +-
 crates/api/src/websocket.rs                   |   2 +-
 crates/api_common/Cargo.toml                  |  27 +-
 crates/api_common/src/comment.rs              |   4 +-
 crates/api_common/src/community.rs            |   8 +-
 crates/api_common/src/lib.rs                  | 612 +-----------------
 crates/api_common/src/person.rs               |  14 +-
 crates/api_common/src/post.rs                 |  21 +-
 crates/api_common/src/request.rs              | 274 ++++++++
 crates/{utils => api_common}/src/sensitive.rs |   7 +-
 crates/api_common/src/site.rs                 |  52 +-
 crates/api_common/src/utils.rs                | 605 +++++++++++++++++
 crates/api_common/src/websocket.rs            |   2 +-
 crates/api_crud/Cargo.toml                    |   8 +-
 crates/api_crud/src/comment/create.rs         |  18 +-
 crates/api_crud/src/comment/delete.rs         | 114 +---
 crates/api_crud/src/comment/list.rs           |  10 +-
 crates/api_crud/src/comment/mod.rs            |   1 +
 crates/api_crud/src/comment/read.rs           |   8 +-
 crates/api_crud/src/comment/remove.rs         | 121 ++++
 crates/api_crud/src/comment/update.rs         |  17 +-
 crates/api_crud/src/community/create.rs       |   9 +-
 crates/api_crud/src/community/delete.rs       |  81 +--
 crates/api_crud/src/community/list.rs         |  10 +-
 crates/api_crud/src/community/mod.rs          |   1 +
 crates/api_crud/src/community/read.rs         |  11 +-
 crates/api_crud/src/community/remove.rs       |  80 +++
 crates/api_crud/src/community/update.rs       |   9 +-
 crates/api_crud/src/post/create.rs            |  20 +-
 crates/api_crud/src/post/delete.rs            |  99 +--
 crates/api_crud/src/post/list.rs              |   8 +-
 crates/api_crud/src/post/mod.rs               |   1 +
 crates/api_crud/src/post/read.rs              |  12 +-
 crates/api_crud/src/post/remove.rs            |  97 +++
 crates/api_crud/src/post/update.rs            |  17 +-
 crates/api_crud/src/private_message/create.rs |  14 +-
 crates/api_crud/src/private_message/delete.rs |   3 +-
 crates/api_crud/src/private_message/read.rs   |   3 +-
 crates/api_crud/src/private_message/update.rs |   3 +-
 crates/api_crud/src/site/create.rs            |  20 +-
 crates/api_crud/src/site/read.rs              |  20 +-
 crates/api_crud/src/site/update.rs            |  19 +-
 crates/api_crud/src/user/create.rs            |  13 +-
 crates/api_crud/src/user/delete.rs            |   5 +-
 crates/api_crud/src/user/read.rs              |  16 +-
 crates/apub/Cargo.toml                        |  10 +-
 .../apub/src/activities/block/block_user.rs   |   2 +-
 crates/apub/src/activities/block/mod.rs       |   4 +-
 .../src/activities/block/undo_block_user.rs   |   2 +-
 .../apub/src/activities/community/add_mod.rs  |   2 +-
 .../src/activities/community/remove_mod.rs    |   2 +-
 .../apub/src/activities/community/report.rs   |   4 +-
 .../apub/src/activities/community/update.rs   |   2 +-
 .../activities/create_or_update/comment.rs    |   2 +-
 .../src/activities/create_or_update/mod.rs    |   2 +-
 .../src/activities/create_or_update/post.rs   |   2 +-
 .../create_or_update/private_message.rs       |   2 +-
 crates/apub/src/activities/deletion/delete.rs |   2 +-
 .../src/activities/deletion/delete_user.rs    |   2 +-
 crates/apub/src/activities/deletion/mod.rs    |   2 +-
 .../src/activities/deletion/undo_delete.rs    |   2 +-
 .../apub/src/activities/following/accept.rs   |   2 +-
 .../apub/src/activities/following/follow.rs   |   2 +-
 .../src/activities/following/undo_follow.rs   |   2 +-
 crates/apub/src/activities/mod.rs             |   7 +-
 crates/apub/src/activities/voting/mod.rs      |   2 +-
 .../apub/src/activities/voting/undo_vote.rs   |   2 +-
 crates/apub/src/activities/voting/vote.rs     |   2 +-
 .../src/collections/community_moderators.rs   |   4 +-
 .../apub/src/collections/community_outbox.rs  |   2 +-
 .../apub/src/fetcher/deletable_apub_object.rs |   2 +-
 crates/apub/src/fetcher/mod.rs                |   2 +-
 crates/apub/src/fetcher/webfinger.rs          |  10 +-
 crates/apub/src/http/comment.rs               |   2 +-
 crates/apub/src/http/community.rs             |   2 +-
 crates/apub/src/http/mod.rs                   |   2 +-
 crates/apub/src/http/person.rs                |   2 +-
 crates/apub/src/http/post.rs                  |   2 +-
 crates/apub/src/http/site.rs                  |   2 +-
 crates/apub/src/lib.rs                        |   5 +-
 crates/apub/src/mentions.rs                   |   4 +-
 crates/apub/src/migrations.rs                 |   1 -
 crates/apub/src/objects/comment.rs            |   2 +-
 crates/apub/src/objects/community.rs          |   4 +-
 crates/apub/src/objects/instance.rs           |   4 +-
 crates/apub/src/objects/mod.rs                |   5 +-
 crates/apub/src/objects/person.rs             |   4 +-
 crates/apub/src/objects/post.rs               |   3 +-
 crates/apub/src/objects/private_message.rs    |   2 +-
 .../protocol/collections/group_followers.rs   |   4 +-
 crates/apub/src/protocol/objects/group.rs     |   2 +-
 crates/apub/src/protocol/objects/note.rs      |   2 +-
 crates/apub_lib/Cargo.toml                    |   2 +-
 crates/db_schema/Cargo.toml                   |  30 +-
 .../src/aggregates/comment_aggregates.rs      |  22 +-
 .../src/aggregates/community_aggregates.rs    |  26 +-
 crates/db_schema/src/aggregates/mod.rs        |   6 +
 .../src/aggregates/person_aggregates.rs       |  18 +-
 .../src/aggregates/post_aggregates.rs         |  22 +-
 .../src/aggregates/site_aggregates.rs         |  22 +-
 crates/db_schema/src/aggregates/structs.rs    |  83 +++
 crates/db_schema/src/impls/activity.rs        |   2 +-
 crates/db_schema/src/impls/comment.rs         |   4 +-
 crates/db_schema/src/impls/comment_report.rs  |   2 +-
 crates/db_schema/src/impls/community.rs       |   5 +-
 crates/db_schema/src/impls/local_user.rs      |   2 +-
 crates/db_schema/src/impls/moderator.rs       |   2 +-
 .../src/impls/password_reset_request.rs       |   2 +-
 crates/db_schema/src/impls/person.rs          |   5 +-
 crates/db_schema/src/impls/person_mention.rs  |   2 +-
 crates/db_schema/src/impls/post.rs            |   4 +-
 crates/db_schema/src/impls/post_report.rs     |   2 +-
 crates/db_schema/src/impls/private_message.rs |   4 +-
 crates/db_schema/src/impls/site.rs            |   2 +-
 crates/db_schema/src/lib.rs                   | 193 +-----
 crates/db_schema/src/newtypes.rs              |  85 +--
 crates/db_schema/src/source/activity.rs       |   2 +-
 crates/db_schema/src/source/comment.rs        |  55 +-
 crates/db_schema/src/source/comment_report.rs |  23 +-
 crates/db_schema/src/source/community.rs      |  59 +-
 .../db_schema/src/source/community_block.rs   |  22 +-
 .../src/source/email_verification.rs          |  14 +-
 crates/db_schema/src/source/local_user.rs     |  23 +-
 crates/db_schema/src/source/mod.rs            |   1 +
 crates/db_schema/src/source/moderator.rs      | 131 ++--
 .../src/source/password_reset_request.rs      |  14 +-
 crates/db_schema/src/source/person.rs         |  43 +-
 crates/db_schema/src/source/person_block.rs   |  19 +-
 crates/db_schema/src/source/person_mention.rs |  22 +-
 crates/db_schema/src/source/post.rs           |  53 +-
 crates/db_schema/src/source/post_report.rs    |  23 +-
 .../db_schema/src/source/private_message.rs   |  20 +-
 .../src/source/registration_application.rs    |  18 +-
 crates/db_schema/src/source/secret.rs         |   6 +-
 crates/db_schema/src/source/site.rs           |  15 +-
 crates/db_schema/src/traits.rs                |   5 +-
 crates/db_schema/src/utils.rs                 | 216 +++++++
 crates/db_views/Cargo.toml                    |   7 +-
 crates/db_views/src/comment_report_view.rs    |  24 +-
 crates/db_views/src/comment_view.rs           |  29 +-
 crates/db_views/src/lib.rs                    |   9 +
 crates/db_views/src/local_user_view.rs        |  20 +-
 crates/db_views/src/post_report_view.rs       |  23 +-
 crates/db_views/src/post_view.rs              |  30 +-
 crates/db_views/src/private_message_view.rs   |  11 +-
 .../src/registration_application_view.rs      |  14 +-
 crates/db_views/src/site_view.rs              |  10 +-
 crates/db_views/src/structs.rs                | 107 +++
 crates/db_views_actor/Cargo.toml              |   5 +-
 .../src/community_block_view.rs               |   8 +-
 .../src/community_follower_view.rs            |   8 +-
 .../src/community_moderator_view.rs           |   8 +-
 .../src/community_person_ban_view.rs          |   8 +-
 crates/db_views_actor/src/community_view.rs   |  19 +-
 crates/db_views_actor/src/lib.rs              |   9 +
 .../db_views_actor/src/person_block_view.rs   |   8 +-
 .../db_views_actor/src/person_mention_view.rs |  24 +-
 crates/db_views_actor/src/person_view.rs      |  14 +-
 crates/db_views_actor/src/structs.rs          |  71 ++
 crates/db_views_moderator/Cargo.toml          |   5 +-
 crates/db_views_moderator/src/lib.rs          |  12 +
 .../src/mod_add_community_view.rs             |  12 +-
 crates/db_views_moderator/src/mod_add_view.rs |  11 +-
 .../src/mod_ban_from_community_view.rs        |  12 +-
 crates/db_views_moderator/src/mod_ban_view.rs |  11 +-
 .../src/mod_hide_community_view.rs            |  11 +-
 .../src/mod_lock_post_view.rs                 |  12 +-
 .../src/mod_remove_comment_view.rs            |  14 +-
 .../src/mod_remove_community_view.rs          |  11 +-
 .../src/mod_remove_post_view.rs               |  12 +-
 .../src/mod_sticky_post_view.rs               |  12 +-
 .../src/mod_transfer_community_view.rs        |  12 +-
 crates/db_views_moderator/src/structs.rs      | 106 +++
 crates/routes/Cargo.toml                      |   2 +-
 crates/routes/src/feeds.rs                    |  16 +-
 crates/routes/src/nodeinfo.rs                 |   4 +-
 crates/routes/src/webfinger.rs                |   2 +-
 crates/utils/Cargo.toml                       |   8 +-
 crates/utils/src/lib.rs                       |   6 +-
 crates/utils/src/request.rs                   | 300 ---------
 crates/websocket/Cargo.toml                   |   8 +-
 crates/websocket/src/handlers.rs              |   2 +-
 crates/websocket/src/lib.rs                   |   2 +-
 crates/websocket/src/send.rs                  |  14 +-
 src/code_migrations.rs                        |   2 +-
 src/main.rs                                   |   8 +-
 src/scheduled_tasks.rs                        |   2 +-
 238 files changed, 2735 insertions(+), 2688 deletions(-)
 create mode 100644 crates/api_common/src/request.rs
 rename crates/{utils => api_common}/src/sensitive.rs (90%)
 create mode 100644 crates/api_common/src/utils.rs
 create mode 100644 crates/api_crud/src/comment/remove.rs
 create mode 100644 crates/api_crud/src/community/remove.rs
 create mode 100644 crates/api_crud/src/post/remove.rs
 delete mode 100644 crates/apub/src/migrations.rs
 create mode 100644 crates/db_schema/src/aggregates/structs.rs
 create mode 100644 crates/db_schema/src/utils.rs
 create mode 100644 crates/db_views/src/structs.rs
 create mode 100644 crates/db_views_actor/src/structs.rs
 create mode 100644 crates/db_views_moderator/src/structs.rs

diff --git a/.drone.yml b/.drone.yml
index 2b4ce894..7b909482 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -22,6 +22,11 @@ steps:
     commands:
       - /root/.cargo/bin/cargo fmt -- --check
 
+  - name: check lemmy_api_common with minimal deps
+    image: clux/muslrust:1.56.0
+    commands:
+      - cargo check -p lemmy_api_common
+
   - name: cargo clippy
     image: clux/muslrust:1.56.0
     commands:
diff --git a/Cargo.lock b/Cargo.lock
index 2d813647..da568355 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1831,18 +1831,24 @@ dependencies = [
 name = "lemmy_api_common"
 version = "0.16.3"
 dependencies = [
+ "actix-rt",
  "actix-web",
  "chrono",
  "diesel",
+ "encoding",
  "lemmy_db_schema",
  "lemmy_db_views",
  "lemmy_db_views_actor",
  "lemmy_db_views_moderator",
  "lemmy_utils",
+ "percent-encoding",
+ "reqwest",
+ "reqwest-middleware",
  "rosetta-i18n",
  "serde",
  "tracing",
  "url",
+ "webpage",
 ]
 
 [[package]]
@@ -2062,7 +2068,6 @@ dependencies = [
 name = "lemmy_utils"
 version = "0.16.3"
 dependencies = [
- "actix-rt",
  "actix-web",
  "anyhow",
  "chrono",
@@ -2070,7 +2075,6 @@ dependencies = [
  "deser-hjson",
  "diesel",
  "doku",
- "encoding",
  "futures",
  "html2text",
  "http",
@@ -2080,10 +2084,8 @@ dependencies = [
  "once_cell",
  "openssl",
  "parking_lot 0.12.0",
- "percent-encoding",
  "rand 0.8.5",
  "regex",
- "reqwest",
  "reqwest-middleware",
  "rosetta-build",
  "rosetta-i18n",
@@ -2092,12 +2094,10 @@ dependencies = [
  "smart-default",
  "strum",
  "strum_macros",
- "thiserror",
  "tracing",
  "tracing-error",
  "url",
  "uuid",
- "webpage",
 ]
 
 [[package]]
diff --git a/crates/api/Cargo.toml b/crates/api/Cargo.toml
index 4cf4f17d..dfad36fc 100644
--- a/crates/api/Cargo.toml
+++ b/crates/api/Cargo.toml
@@ -16,15 +16,15 @@ doctest = false
 lemmy_apub = { version = "=0.16.3", path = "../apub" }
 lemmy_apub_lib = { version = "=0.16.3", path = "../apub_lib" }
 lemmy_utils = { version = "=0.16.3", path = "../utils" }
-lemmy_db_schema = { version = "=0.16.3", path = "../db_schema" }
-lemmy_db_views = { version = "=0.16.3", path = "../db_views" }
-lemmy_db_views_moderator = { version = "=0.16.3", path = "../db_views_moderator" }
-lemmy_db_views_actor = { version = "=0.16.3", path = "../db_views_actor" }
-lemmy_api_common = { version = "=0.16.3", path = "../api_common" }
+lemmy_db_schema = { version = "=0.16.3", path = "../db_schema", features = ["full"] }
+lemmy_db_views = { version = "=0.16.3", path = "../db_views", features = ["full"] }
+lemmy_db_views_moderator = { version = "=0.16.3", path = "../db_views_moderator", features = ["full"] }
+lemmy_db_views_actor = { version = "=0.16.3", path = "../db_views_actor", features = ["full"] }
+lemmy_api_common = { version = "=0.16.3", path = "../api_common", features = ["full"] }
 lemmy_websocket = { version = "=0.16.3", path = "../websocket" }
 diesel = "1.4.8"
 bcrypt = "0.12.1"
-chrono = { version = "0.4.19", features = ["serde"] }
+chrono = { version = "0.4.19", features = ["serde"], default-features = false }
 serde_json = { version = "1.0.79", features = ["preserve_order"] }
 serde = { version = "1.0.136", features = ["derive"] }
 actix-web = { version = "4.0.1", default-features = false }
diff --git a/crates/api/src/comment/like.rs b/crates/api/src/comment/like.rs
index a5bb368a..cc97ebc4 100644
--- a/crates/api/src/comment/like.rs
+++ b/crates/api/src/comment/like.rs
@@ -1,11 +1,8 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  check_community_ban,
-  check_downvotes_enabled,
   comment::{CommentResponse, CreateCommentLike},
-  get_local_user_view_from_jwt,
+  utils::{blocking, check_community_ban, check_downvotes_enabled, get_local_user_view_from_jwt},
 };
 use lemmy_apub::{
   fetcher::post_or_comment::PostOrComment,
@@ -19,7 +16,7 @@ use lemmy_db_schema::{
   source::comment::{CommentLike, CommentLikeForm},
   traits::Likeable,
 };
-use lemmy_db_views::{comment_view::CommentView, local_user_view::LocalUserView};
+use lemmy_db_views::structs::{CommentView, LocalUserView};
 use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::{send::send_comment_ws_message, LemmyContext, UserOperation};
 use std::convert::TryInto;
diff --git a/crates/api/src/comment/mark_as_read.rs b/crates/api/src/comment/mark_as_read.rs
index 25c4fb43..a0dc6dd7 100644
--- a/crates/api/src/comment/mark_as_read.rs
+++ b/crates/api/src/comment/mark_as_read.rs
@@ -1,12 +1,11 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
   comment::{CommentResponse, MarkCommentAsRead},
-  get_local_user_view_from_jwt,
+  utils::{blocking, get_local_user_view_from_jwt},
 };
 use lemmy_db_schema::source::comment::Comment;
-use lemmy_db_views::comment_view::CommentView;
+use lemmy_db_views::structs::CommentView;
 use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::LemmyContext;
 
diff --git a/crates/api/src/comment/save.rs b/crates/api/src/comment/save.rs
index 6fc0f498..1ece524a 100644
--- a/crates/api/src/comment/save.rs
+++ b/crates/api/src/comment/save.rs
@@ -1,15 +1,14 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
   comment::{CommentResponse, SaveComment},
-  get_local_user_view_from_jwt,
+  utils::{blocking, get_local_user_view_from_jwt},
 };
 use lemmy_db_schema::{
   source::comment::{CommentSaved, CommentSavedForm},
   traits::Saveable,
 };
-use lemmy_db_views::comment_view::CommentView;
+use lemmy_db_views::structs::CommentView;
 use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::LemmyContext;
 
diff --git a/crates/api/src/comment_report/create.rs b/crates/api/src/comment_report/create.rs
index 4f4ef3bb..75e1323a 100644
--- a/crates/api/src/comment_report/create.rs
+++ b/crates/api/src/comment_report/create.rs
@@ -1,10 +1,8 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  check_community_ban,
   comment::{CommentReportResponse, CreateCommentReport},
-  get_local_user_view_from_jwt,
+  utils::{blocking, check_community_ban, get_local_user_view_from_jwt},
 };
 use lemmy_apub::protocol::activities::community::report::Report;
 use lemmy_apub_lib::object_id::ObjectId;
@@ -12,7 +10,7 @@ use lemmy_db_schema::{
   source::comment_report::{CommentReport, CommentReportForm},
   traits::Reportable,
 };
-use lemmy_db_views::{comment_report_view::CommentReportView, comment_view::CommentView};
+use lemmy_db_views::structs::{CommentReportView, CommentView};
 use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
 
diff --git a/crates/api/src/comment_report/list.rs b/crates/api/src/comment_report/list.rs
index b88aced7..296cb832 100644
--- a/crates/api/src/comment_report/list.rs
+++ b/crates/api/src/comment_report/list.rs
@@ -1,9 +1,8 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
   comment::{ListCommentReports, ListCommentReportsResponse},
-  get_local_user_view_from_jwt,
+  utils::{blocking, get_local_user_view_from_jwt},
 };
 use lemmy_db_views::comment_report_view::CommentReportQueryBuilder;
 use lemmy_utils::{ConnectionId, LemmyError};
diff --git a/crates/api/src/comment_report/resolve.rs b/crates/api/src/comment_report/resolve.rs
index 9446fb36..95481ace 100644
--- a/crates/api/src/comment_report/resolve.rs
+++ b/crates/api/src/comment_report/resolve.rs
@@ -1,13 +1,11 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
   comment::{CommentReportResponse, ResolveCommentReport},
-  get_local_user_view_from_jwt,
-  is_mod_or_admin,
+  utils::{blocking, get_local_user_view_from_jwt, is_mod_or_admin},
 };
 use lemmy_db_schema::{source::comment_report::CommentReport, traits::Reportable};
-use lemmy_db_views::comment_report_view::CommentReportView;
+use lemmy_db_views::structs::CommentReportView;
 use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
 
diff --git a/crates/api/src/community/add_mod.rs b/crates/api/src/community/add_mod.rs
index 6e790a0c..9ea9361a 100644
--- a/crates/api/src/community/add_mod.rs
+++ b/crates/api/src/community/add_mod.rs
@@ -1,10 +1,8 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
   community::{AddModToCommunity, AddModToCommunityResponse},
-  get_local_user_view_from_jwt,
-  is_mod_or_admin,
+  utils::{blocking, get_local_user_view_from_jwt, is_mod_or_admin},
 };
 use lemmy_apub::{
   objects::{community::ApubCommunity, person::ApubPerson},
@@ -18,7 +16,7 @@ use lemmy_db_schema::{
   },
   traits::{Crud, Joinable},
 };
-use lemmy_db_views_actor::community_moderator_view::CommunityModeratorView;
+use lemmy_db_views_actor::structs::CommunityModeratorView;
 use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::{messages::SendCommunityRoomMessage, LemmyContext, UserOperation};
 
diff --git a/crates/api/src/community/ban.rs b/crates/api/src/community/ban.rs
index ccdc1b9b..60fd3227 100644
--- a/crates/api/src/community/ban.rs
+++ b/crates/api/src/community/ban.rs
@@ -1,11 +1,8 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
   community::{BanFromCommunity, BanFromCommunityResponse},
-  get_local_user_view_from_jwt,
-  is_mod_or_admin,
-  remove_user_data_in_community,
+  utils::{blocking, get_local_user_view_from_jwt, is_mod_or_admin, remove_user_data_in_community},
 };
 use lemmy_apub::{
   activities::block::SiteOrCommunity,
@@ -26,7 +23,7 @@ use lemmy_db_schema::{
   },
   traits::{Bannable, Crud, Followable},
 };
-use lemmy_db_views_actor::person_view::PersonViewSafe;
+use lemmy_db_views_actor::structs::PersonViewSafe;
 use lemmy_utils::{utils::naive_from_unix, ConnectionId, LemmyError};
 use lemmy_websocket::{messages::SendCommunityRoomMessage, LemmyContext, UserOperation};
 
diff --git a/crates/api/src/community/block.rs b/crates/api/src/community/block.rs
index 02ece47f..4f740c91 100644
--- a/crates/api/src/community/block.rs
+++ b/crates/api/src/community/block.rs
@@ -1,9 +1,8 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
   community::{BlockCommunity, BlockCommunityResponse},
-  get_local_user_view_from_jwt,
+  utils::{blocking, get_local_user_view_from_jwt},
 };
 use lemmy_apub::protocol::activities::following::undo_follow::UndoFollowCommunity;
 use lemmy_db_schema::{
@@ -13,7 +12,7 @@ use lemmy_db_schema::{
   },
   traits::{Blockable, Crud, Followable},
 };
-use lemmy_db_views_actor::community_view::CommunityView;
+use lemmy_db_views_actor::structs::CommunityView;
 use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::LemmyContext;
 
diff --git a/crates/api/src/community/follow.rs b/crates/api/src/community/follow.rs
index 62eb34bb..aab21a9c 100644
--- a/crates/api/src/community/follow.rs
+++ b/crates/api/src/community/follow.rs
@@ -1,11 +1,13 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  check_community_ban,
-  check_community_deleted_or_removed,
   community::{CommunityResponse, FollowCommunity},
-  get_local_user_view_from_jwt,
+  utils::{
+    blocking,
+    check_community_ban,
+    check_community_deleted_or_removed,
+    get_local_user_view_from_jwt,
+  },
 };
 use lemmy_apub::{
   objects::community::ApubCommunity,
@@ -18,7 +20,7 @@ use lemmy_db_schema::{
   source::community::{Community, CommunityFollower, CommunityFollowerForm},
   traits::{Crud, Followable},
 };
-use lemmy_db_views_actor::community_view::CommunityView;
+use lemmy_db_views_actor::structs::CommunityView;
 use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::LemmyContext;
 
diff --git a/crates/api/src/community/hide.rs b/crates/api/src/community/hide.rs
index a3910010..ddf4f49a 100644
--- a/crates/api/src/community/hide.rs
+++ b/crates/api/src/community/hide.rs
@@ -1,19 +1,17 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
   community::{CommunityResponse, HideCommunity},
-  get_local_user_view_from_jwt,
-  is_admin,
+  utils::{blocking, get_local_user_view_from_jwt, is_admin},
 };
 use lemmy_apub::protocol::activities::community::update::UpdateCommunity;
 use lemmy_db_schema::{
-  naive_now,
   source::{
     community::{Community, CommunityForm},
     moderator::{ModHideCommunity, ModHideCommunityForm},
   },
   traits::Crud,
+  utils::naive_now,
 };
 use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud};
diff --git a/crates/api/src/community/transfer.rs b/crates/api/src/community/transfer.rs
index 05282f45..9d8044e3 100644
--- a/crates/api/src/community/transfer.rs
+++ b/crates/api/src/community/transfer.rs
@@ -2,9 +2,8 @@ use crate::Perform;
 use actix_web::web::Data;
 use anyhow::Context;
 use lemmy_api_common::{
-  blocking,
   community::{GetCommunityResponse, TransferCommunity},
-  get_local_user_view_from_jwt,
+  utils::{blocking, get_local_user_view_from_jwt},
 };
 use lemmy_db_schema::{
   source::{
@@ -13,11 +12,7 @@ use lemmy_db_schema::{
   },
   traits::{Crud, Joinable},
 };
-use lemmy_db_views_actor::{
-  community_moderator_view::CommunityModeratorView,
-  community_view::CommunityView,
-  person_view::PersonViewSafe,
-};
+use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView, PersonViewSafe};
 use lemmy_utils::{location_info, ConnectionId, LemmyError};
 use lemmy_websocket::LemmyContext;
 
diff --git a/crates/api/src/lib.rs b/crates/api/src/lib.rs
index d6de25cc..0c36890b 100644
--- a/crates/api/src/lib.rs
+++ b/crates/api/src/lib.rs
@@ -206,15 +206,15 @@ pub(crate) fn captcha_as_wav_base64(captcha: &Captcha) -> String {
 
 #[cfg(test)]
 mod tests {
-  use lemmy_api_common::check_validator_time;
+  use lemmy_api_common::utils::check_validator_time;
   use lemmy_db_schema::{
-    establish_unpooled_connection,
     source::{
       local_user::{LocalUser, LocalUserForm},
       person::{Person, PersonForm},
       secret::Secret,
     },
     traits::Crud,
+    utils::establish_unpooled_connection,
   };
   use lemmy_utils::{claims::Claims, settings::structs::Settings};
 
diff --git a/crates/api/src/local_user/add_admin.rs b/crates/api/src/local_user/add_admin.rs
index 0630e241..3834dbbe 100644
--- a/crates/api/src/local_user/add_admin.rs
+++ b/crates/api/src/local_user/add_admin.rs
@@ -1,10 +1,8 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  get_local_user_view_from_jwt,
-  is_admin,
   person::{AddAdmin, AddAdminResponse},
+  utils::{blocking, get_local_user_view_from_jwt, is_admin},
 };
 use lemmy_db_schema::{
   source::{
@@ -13,7 +11,7 @@ use lemmy_db_schema::{
   },
   traits::Crud,
 };
-use lemmy_db_views_actor::person_view::PersonViewSafe;
+use lemmy_db_views_actor::structs::PersonViewSafe;
 use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::{messages::SendAllMessage, LemmyContext, UserOperation};
 
diff --git a/crates/api/src/local_user/ban_person.rs b/crates/api/src/local_user/ban_person.rs
index 0397d090..d357e8eb 100644
--- a/crates/api/src/local_user/ban_person.rs
+++ b/crates/api/src/local_user/ban_person.rs
@@ -1,11 +1,8 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  get_local_user_view_from_jwt,
-  is_admin,
   person::{BanPerson, BanPersonResponse},
-  remove_user_data,
+  utils::{blocking, get_local_user_view_from_jwt, is_admin, remove_user_data},
 };
 use lemmy_apub::{
   activities::block::SiteOrCommunity,
@@ -19,7 +16,7 @@ use lemmy_db_schema::{
   },
   traits::Crud,
 };
-use lemmy_db_views_actor::person_view::PersonViewSafe;
+use lemmy_db_views_actor::structs::PersonViewSafe;
 use lemmy_utils::{utils::naive_from_unix, ConnectionId, LemmyError};
 use lemmy_websocket::{messages::SendAllMessage, LemmyContext, UserOperation};
 
diff --git a/crates/api/src/local_user/block.rs b/crates/api/src/local_user/block.rs
index 5dc68ddd..d0812fd9 100644
--- a/crates/api/src/local_user/block.rs
+++ b/crates/api/src/local_user/block.rs
@@ -1,15 +1,14 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  get_local_user_view_from_jwt,
   person::{BlockPerson, BlockPersonResponse},
+  utils::{blocking, get_local_user_view_from_jwt},
 };
 use lemmy_db_schema::{
   source::person_block::{PersonBlock, PersonBlockForm},
   traits::Blockable,
 };
-use lemmy_db_views_actor::person_view::PersonViewSafe;
+use lemmy_db_views_actor::structs::PersonViewSafe;
 use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::LemmyContext;
 
diff --git a/crates/api/src/local_user/change_password.rs b/crates/api/src/local_user/change_password.rs
index 2b66f2ea..71465998 100644
--- a/crates/api/src/local_user/change_password.rs
+++ b/crates/api/src/local_user/change_password.rs
@@ -2,10 +2,8 @@ use crate::Perform;
 use actix_web::web::Data;
 use bcrypt::verify;
 use lemmy_api_common::{
-  blocking,
-  get_local_user_view_from_jwt,
-  password_length_check,
   person::{ChangePassword, LoginResponse},
+  utils::{blocking, get_local_user_view_from_jwt, password_length_check},
 };
 use lemmy_db_schema::source::local_user::LocalUser;
 use lemmy_utils::{claims::Claims, ConnectionId, LemmyError};
diff --git a/crates/api/src/local_user/change_password_after_reset.rs b/crates/api/src/local_user/change_password_after_reset.rs
index 533a0d19..92e4f6ba 100644
--- a/crates/api/src/local_user/change_password_after_reset.rs
+++ b/crates/api/src/local_user/change_password_after_reset.rs
@@ -1,9 +1,8 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  password_length_check,
   person::{LoginResponse, PasswordChangeAfterReset},
+  utils::{blocking, password_length_check},
 };
 use lemmy_db_schema::source::{
   local_user::LocalUser,
diff --git a/crates/api/src/local_user/get_captcha.rs b/crates/api/src/local_user/get_captcha.rs
index efcb6689..5c691b8c 100644
--- a/crates/api/src/local_user/get_captcha.rs
+++ b/crates/api/src/local_user/get_captcha.rs
@@ -3,7 +3,7 @@ use actix_web::web::Data;
 use captcha::{gen, Difficulty};
 use chrono::Duration;
 use lemmy_api_common::person::{CaptchaResponse, GetCaptcha, GetCaptchaResponse};
-use lemmy_db_schema::naive_now;
+use lemmy_db_schema::utils::naive_now;
 use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::{messages::CaptchaItem, LemmyContext};
 
diff --git a/crates/api/src/local_user/list_banned.rs b/crates/api/src/local_user/list_banned.rs
index 1fef5593..4c39d0cd 100644
--- a/crates/api/src/local_user/list_banned.rs
+++ b/crates/api/src/local_user/list_banned.rs
@@ -1,12 +1,10 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  get_local_user_view_from_jwt,
-  is_admin,
   person::{BannedPersonsResponse, GetBannedPersons},
+  utils::{blocking, get_local_user_view_from_jwt, is_admin},
 };
-use lemmy_db_views_actor::person_view::PersonViewSafe;
+use lemmy_db_views_actor::structs::PersonViewSafe;
 use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::LemmyContext;
 
diff --git a/crates/api/src/local_user/login.rs b/crates/api/src/local_user/login.rs
index 5455b867..e1931d5a 100644
--- a/crates/api/src/local_user/login.rs
+++ b/crates/api/src/local_user/login.rs
@@ -2,12 +2,11 @@ use crate::Perform;
 use actix_web::web::Data;
 use bcrypt::verify;
 use lemmy_api_common::{
-  blocking,
-  check_registration_application,
   person::{Login, LoginResponse},
+  utils::{blocking, check_registration_application},
 };
 use lemmy_db_schema::source::site::Site;
-use lemmy_db_views::local_user_view::LocalUserView;
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_utils::{claims::Claims, ConnectionId, LemmyError};
 use lemmy_websocket::LemmyContext;
 
diff --git a/crates/api/src/local_user/notifications/list_mentions.rs b/crates/api/src/local_user/notifications/list_mentions.rs
index f47d3cc7..91409b6d 100644
--- a/crates/api/src/local_user/notifications/list_mentions.rs
+++ b/crates/api/src/local_user/notifications/list_mentions.rs
@@ -1,11 +1,10 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  get_local_user_view_from_jwt,
   person::{GetPersonMentions, GetPersonMentionsResponse},
+  utils::{blocking, get_local_user_view_from_jwt},
 };
-use lemmy_db_schema::{from_opt_str_to_opt_enum, SortType};
+use lemmy_db_schema::utils::{from_opt_str_to_opt_enum, SortType};
 use lemmy_db_views_actor::person_mention_view::PersonMentionQueryBuilder;
 use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::LemmyContext;
diff --git a/crates/api/src/local_user/notifications/list_replies.rs b/crates/api/src/local_user/notifications/list_replies.rs
index 9199421c..1d9aaa2f 100644
--- a/crates/api/src/local_user/notifications/list_replies.rs
+++ b/crates/api/src/local_user/notifications/list_replies.rs
@@ -1,11 +1,10 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  get_local_user_view_from_jwt,
   person::{GetReplies, GetRepliesResponse},
+  utils::{blocking, get_local_user_view_from_jwt},
 };
-use lemmy_db_schema::{from_opt_str_to_opt_enum, SortType};
+use lemmy_db_schema::utils::{from_opt_str_to_opt_enum, SortType};
 use lemmy_db_views::comment_view::CommentQueryBuilder;
 use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::LemmyContext;
diff --git a/crates/api/src/local_user/notifications/mark_all_read.rs b/crates/api/src/local_user/notifications/mark_all_read.rs
index dc3b00d1..d5caa02d 100644
--- a/crates/api/src/local_user/notifications/mark_all_read.rs
+++ b/crates/api/src/local_user/notifications/mark_all_read.rs
@@ -1,9 +1,8 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  get_local_user_view_from_jwt,
   person::{GetRepliesResponse, MarkAllAsRead},
+  utils::{blocking, get_local_user_view_from_jwt},
 };
 use lemmy_db_schema::source::{
   comment::Comment,
diff --git a/crates/api/src/local_user/notifications/mark_mention_read.rs b/crates/api/src/local_user/notifications/mark_mention_read.rs
index 33e427a6..4c9d0601 100644
--- a/crates/api/src/local_user/notifications/mark_mention_read.rs
+++ b/crates/api/src/local_user/notifications/mark_mention_read.rs
@@ -1,12 +1,11 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  get_local_user_view_from_jwt,
   person::{MarkPersonMentionAsRead, PersonMentionResponse},
+  utils::{blocking, get_local_user_view_from_jwt},
 };
 use lemmy_db_schema::{source::person_mention::PersonMention, traits::Crud};
-use lemmy_db_views_actor::person_mention_view::PersonMentionView;
+use lemmy_db_views_actor::structs::PersonMentionView;
 use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::LemmyContext;
 
diff --git a/crates/api/src/local_user/notifications/unread_count.rs b/crates/api/src/local_user/notifications/unread_count.rs
index be805563..a9c06ffc 100644
--- a/crates/api/src/local_user/notifications/unread_count.rs
+++ b/crates/api/src/local_user/notifications/unread_count.rs
@@ -1,12 +1,11 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  get_local_user_view_from_jwt,
   person::{GetUnreadCount, GetUnreadCountResponse},
+  utils::{blocking, get_local_user_view_from_jwt},
 };
-use lemmy_db_views::{comment_view::CommentView, private_message_view::PrivateMessageView};
-use lemmy_db_views_actor::person_mention_view::PersonMentionView;
+use lemmy_db_views::structs::{CommentView, PrivateMessageView};
+use lemmy_db_views_actor::structs::PersonMentionView;
 use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::LemmyContext;
 
diff --git a/crates/api/src/local_user/report_count.rs b/crates/api/src/local_user/report_count.rs
index 5aa2f46d..a07f8c05 100644
--- a/crates/api/src/local_user/report_count.rs
+++ b/crates/api/src/local_user/report_count.rs
@@ -1,11 +1,10 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  get_local_user_view_from_jwt,
   person::{GetReportCount, GetReportCountResponse},
+  utils::{blocking, get_local_user_view_from_jwt},
 };
-use lemmy_db_views::{comment_report_view::CommentReportView, post_report_view::PostReportView};
+use lemmy_db_views::structs::{CommentReportView, PostReportView};
 use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::LemmyContext;
 
diff --git a/crates/api/src/local_user/reset_password.rs b/crates/api/src/local_user/reset_password.rs
index 78b0364c..6903a8b8 100644
--- a/crates/api/src/local_user/reset_password.rs
+++ b/crates/api/src/local_user/reset_password.rs
@@ -1,11 +1,10 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
   person::{PasswordReset, PasswordResetResponse},
-  send_password_reset_email,
+  utils::{blocking, send_password_reset_email},
 };
-use lemmy_db_views::local_user_view::LocalUserView;
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::LemmyContext;
 
diff --git a/crates/api/src/local_user/save_settings.rs b/crates/api/src/local_user/save_settings.rs
index 15a65b01..cd34d6dd 100644
--- a/crates/api/src/local_user/save_settings.rs
+++ b/crates/api/src/local_user/save_settings.rs
@@ -1,22 +1,22 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  check_image_has_local_domain,
-  get_local_user_view_from_jwt,
   person::{LoginResponse, SaveUserSettings},
-  send_verification_email,
+  utils::{
+    blocking,
+    check_image_has_local_domain,
+    get_local_user_view_from_jwt,
+    send_verification_email,
+  },
 };
 use lemmy_db_schema::{
-  diesel_option_overwrite,
-  diesel_option_overwrite_to_url,
-  naive_now,
   source::{
     local_user::{LocalUser, LocalUserForm},
     person::{Person, PersonForm},
     site::Site,
   },
   traits::Crud,
+  utils::{diesel_option_overwrite, diesel_option_overwrite_to_url, naive_now},
 };
 use lemmy_utils::{
   claims::Claims,
diff --git a/crates/api/src/local_user/verify_email.rs b/crates/api/src/local_user/verify_email.rs
index 852e1cfa..c4af8e34 100644
--- a/crates/api/src/local_user/verify_email.rs
+++ b/crates/api/src/local_user/verify_email.rs
@@ -1,9 +1,8 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
   person::{VerifyEmail, VerifyEmailResponse},
-  send_email_verification_success,
+  utils::{blocking, send_email_verification_success},
 };
 use lemmy_db_schema::{
   source::{
@@ -12,7 +11,7 @@ use lemmy_db_schema::{
   },
   traits::Crud,
 };
-use lemmy_db_views::local_user_view::LocalUserView;
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_utils::LemmyError;
 use lemmy_websocket::LemmyContext;
 
diff --git a/crates/api/src/post/get_link_metadata.rs b/crates/api/src/post/get_link_metadata.rs
index a016bd2f..db121332 100644
--- a/crates/api/src/post/get_link_metadata.rs
+++ b/crates/api/src/post/get_link_metadata.rs
@@ -1,7 +1,10 @@
 use crate::Perform;
 use actix_web::web::Data;
-use lemmy_api_common::post::{GetSiteMetadata, GetSiteMetadataResponse};
-use lemmy_utils::{request::fetch_site_metadata, ConnectionId, LemmyError};
+use lemmy_api_common::{
+  post::{GetSiteMetadata, GetSiteMetadataResponse},
+  request::fetch_site_metadata,
+};
+use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::LemmyContext;
 
 #[async_trait::async_trait(?Send)]
diff --git a/crates/api/src/post/like.rs b/crates/api/src/post/like.rs
index 22eb4775..24d9337c 100644
--- a/crates/api/src/post/like.rs
+++ b/crates/api/src/post/like.rs
@@ -1,13 +1,15 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  check_community_ban,
-  check_community_deleted_or_removed,
-  check_downvotes_enabled,
-  get_local_user_view_from_jwt,
-  mark_post_as_read,
   post::{CreatePostLike, PostResponse},
+  utils::{
+    blocking,
+    check_community_ban,
+    check_community_deleted_or_removed,
+    check_downvotes_enabled,
+    get_local_user_view_from_jwt,
+    mark_post_as_read,
+  },
 };
 use lemmy_apub::{
   fetcher::post_or_comment::PostOrComment,
diff --git a/crates/api/src/post/lock.rs b/crates/api/src/post/lock.rs
index 7210d443..965e506f 100644
--- a/crates/api/src/post/lock.rs
+++ b/crates/api/src/post/lock.rs
@@ -1,12 +1,14 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  check_community_ban,
-  check_community_deleted_or_removed,
-  get_local_user_view_from_jwt,
-  is_mod_or_admin,
   post::{LockPost, PostResponse},
+  utils::{
+    blocking,
+    check_community_ban,
+    check_community_deleted_or_removed,
+    get_local_user_view_from_jwt,
+    is_mod_or_admin,
+  },
 };
 use lemmy_apub::{
   objects::post::ApubPost,
diff --git a/crates/api/src/post/mark_read.rs b/crates/api/src/post/mark_read.rs
index 927fc651..8777ebce 100644
--- a/crates/api/src/post/mark_read.rs
+++ b/crates/api/src/post/mark_read.rs
@@ -1,13 +1,10 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  get_local_user_view_from_jwt,
-  mark_post_as_read,
-  mark_post_as_unread,
   post::{MarkPostAsRead, PostResponse},
+  utils::{blocking, get_local_user_view_from_jwt, mark_post_as_read, mark_post_as_unread},
 };
-use lemmy_db_views::post_view::PostView;
+use lemmy_db_views::structs::PostView;
 use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::LemmyContext;
 
diff --git a/crates/api/src/post/save.rs b/crates/api/src/post/save.rs
index 973b99de..09baec98 100644
--- a/crates/api/src/post/save.rs
+++ b/crates/api/src/post/save.rs
@@ -1,16 +1,14 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  get_local_user_view_from_jwt,
-  mark_post_as_read,
   post::{PostResponse, SavePost},
+  utils::{blocking, get_local_user_view_from_jwt, mark_post_as_read},
 };
 use lemmy_db_schema::{
   source::post::{PostSaved, PostSavedForm},
   traits::Saveable,
 };
-use lemmy_db_views::post_view::PostView;
+use lemmy_db_views::structs::PostView;
 use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::LemmyContext;
 
diff --git a/crates/api/src/post/sticky.rs b/crates/api/src/post/sticky.rs
index db669252..dffe67e3 100644
--- a/crates/api/src/post/sticky.rs
+++ b/crates/api/src/post/sticky.rs
@@ -1,12 +1,14 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  check_community_ban,
-  check_community_deleted_or_removed,
-  get_local_user_view_from_jwt,
-  is_mod_or_admin,
   post::{PostResponse, StickyPost},
+  utils::{
+    blocking,
+    check_community_ban,
+    check_community_deleted_or_removed,
+    get_local_user_view_from_jwt,
+    is_mod_or_admin,
+  },
 };
 use lemmy_apub::{
   objects::post::ApubPost,
diff --git a/crates/api/src/post_report/create.rs b/crates/api/src/post_report/create.rs
index a85aa9a9..a960e2c5 100644
--- a/crates/api/src/post_report/create.rs
+++ b/crates/api/src/post_report/create.rs
@@ -1,10 +1,8 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  check_community_ban,
-  get_local_user_view_from_jwt,
   post::{CreatePostReport, PostReportResponse},
+  utils::{blocking, check_community_ban, get_local_user_view_from_jwt},
 };
 use lemmy_apub::protocol::activities::community::report::Report;
 use lemmy_apub_lib::object_id::ObjectId;
@@ -12,7 +10,7 @@ use lemmy_db_schema::{
   source::post_report::{PostReport, PostReportForm},
   traits::Reportable,
 };
-use lemmy_db_views::{post_report_view::PostReportView, post_view::PostView};
+use lemmy_db_views::structs::{PostReportView, PostView};
 use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
 
diff --git a/crates/api/src/post_report/list.rs b/crates/api/src/post_report/list.rs
index 7dfbb107..7ae0a5ee 100644
--- a/crates/api/src/post_report/list.rs
+++ b/crates/api/src/post_report/list.rs
@@ -1,9 +1,8 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  get_local_user_view_from_jwt,
   post::{ListPostReports, ListPostReportsResponse},
+  utils::{blocking, get_local_user_view_from_jwt},
 };
 use lemmy_db_views::post_report_view::PostReportQueryBuilder;
 use lemmy_utils::{ConnectionId, LemmyError};
diff --git a/crates/api/src/post_report/resolve.rs b/crates/api/src/post_report/resolve.rs
index f0eb3fdb..0b50999d 100644
--- a/crates/api/src/post_report/resolve.rs
+++ b/crates/api/src/post_report/resolve.rs
@@ -1,13 +1,11 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  get_local_user_view_from_jwt,
-  is_mod_or_admin,
   post::{PostReportResponse, ResolvePostReport},
+  utils::{blocking, get_local_user_view_from_jwt, is_mod_or_admin},
 };
 use lemmy_db_schema::{source::post_report::PostReport, traits::Reportable};
-use lemmy_db_views::post_report_view::PostReportView;
+use lemmy_db_views::structs::PostReportView;
 use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
 
diff --git a/crates/api/src/private_message/mark_read.rs b/crates/api/src/private_message/mark_read.rs
index d46280bd..57896783 100644
--- a/crates/api/src/private_message/mark_read.rs
+++ b/crates/api/src/private_message/mark_read.rs
@@ -1,9 +1,8 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  get_local_user_view_from_jwt,
   person::{MarkPrivateMessageAsRead, PrivateMessageResponse},
+  utils::{blocking, get_local_user_view_from_jwt},
 };
 use lemmy_db_schema::{source::private_message::PrivateMessage, traits::Crud};
 use lemmy_utils::{ConnectionId, LemmyError};
diff --git a/crates/api/src/site/config/read.rs b/crates/api/src/site/config/read.rs
index 76117d49..8626fd5f 100644
--- a/crates/api/src/site/config/read.rs
+++ b/crates/api/src/site/config/read.rs
@@ -1,9 +1,8 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  get_local_user_view_from_jwt,
-  is_admin,
   site::{GetSiteConfig, GetSiteConfigResponse},
+  utils::{get_local_user_view_from_jwt, is_admin},
 };
 use lemmy_utils::{settings::structs::Settings, ConnectionId, LemmyError};
 use lemmy_websocket::LemmyContext;
diff --git a/crates/api/src/site/config/update.rs b/crates/api/src/site/config/update.rs
index b36c69ce..98f2b1f3 100644
--- a/crates/api/src/site/config/update.rs
+++ b/crates/api/src/site/config/update.rs
@@ -1,9 +1,8 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  get_local_user_view_from_jwt,
-  is_admin,
   site::{GetSiteConfigResponse, SaveSiteConfig},
+  utils::{get_local_user_view_from_jwt, is_admin},
 };
 use lemmy_utils::{settings::structs::Settings, ConnectionId, LemmyError};
 use lemmy_websocket::LemmyContext;
diff --git a/crates/api/src/site/leave_admin.rs b/crates/api/src/site/leave_admin.rs
index ee714e86..10a3adc4 100644
--- a/crates/api/src/site/leave_admin.rs
+++ b/crates/api/src/site/leave_admin.rs
@@ -1,11 +1,8 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  build_federated_instances,
-  get_local_user_view_from_jwt,
-  is_admin,
   site::{GetSiteResponse, LeaveAdmin},
+  utils::{blocking, build_federated_instances, get_local_user_view_from_jwt, is_admin},
 };
 use lemmy_db_schema::{
   source::{
@@ -14,8 +11,8 @@ use lemmy_db_schema::{
   },
   traits::Crud,
 };
-use lemmy_db_views::site_view::SiteView;
-use lemmy_db_views_actor::person_view::PersonViewSafe;
+use lemmy_db_views::structs::SiteView;
+use lemmy_db_views_actor::structs::PersonViewSafe;
 use lemmy_utils::{version, ConnectionId, LemmyError};
 use lemmy_websocket::LemmyContext;
 
diff --git a/crates/api/src/site/mod_log.rs b/crates/api/src/site/mod_log.rs
index 6dd06c16..45335d5d 100644
--- a/crates/api/src/site/mod_log.rs
+++ b/crates/api/src/site/mod_log.rs
@@ -1,23 +1,21 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  check_private_instance,
-  get_local_user_view_from_jwt_opt,
   site::{GetModlog, GetModlogResponse},
+  utils::{blocking, check_private_instance, get_local_user_view_from_jwt_opt},
 };
-use lemmy_db_views_moderator::{
-  mod_add_community_view::ModAddCommunityView,
-  mod_add_view::ModAddView,
-  mod_ban_from_community_view::ModBanFromCommunityView,
-  mod_ban_view::ModBanView,
-  mod_hide_community_view::ModHideCommunityView,
-  mod_lock_post_view::ModLockPostView,
-  mod_remove_comment_view::ModRemoveCommentView,
-  mod_remove_community_view::ModRemoveCommunityView,
-  mod_remove_post_view::ModRemovePostView,
-  mod_sticky_post_view::ModStickyPostView,
-  mod_transfer_community_view::ModTransferCommunityView,
+use lemmy_db_views_moderator::structs::{
+  ModAddCommunityView,
+  ModAddView,
+  ModBanFromCommunityView,
+  ModBanView,
+  ModHideCommunityView,
+  ModLockPostView,
+  ModRemoveCommentView,
+  ModRemoveCommunityView,
+  ModRemovePostView,
+  ModStickyPostView,
+  ModTransferCommunityView,
 };
 use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::LemmyContext;
diff --git a/crates/api/src/site/registration_applications/approve.rs b/crates/api/src/site/registration_applications/approve.rs
index 238da7e5..2e3577de 100644
--- a/crates/api/src/site/registration_applications/approve.rs
+++ b/crates/api/src/site/registration_applications/approve.rs
@@ -1,24 +1,18 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  get_local_user_view_from_jwt,
-  is_admin,
-  send_application_approved_email,
-  site::*,
+  site::{ApproveRegistrationApplication, RegistrationApplicationResponse},
+  utils::{blocking, get_local_user_view_from_jwt, is_admin, send_application_approved_email},
 };
 use lemmy_db_schema::{
-  diesel_option_overwrite,
   source::{
     local_user::{LocalUser, LocalUserForm},
     registration_application::{RegistrationApplication, RegistrationApplicationForm},
   },
   traits::Crud,
+  utils::diesel_option_overwrite,
 };
-use lemmy_db_views::{
-  local_user_view::LocalUserView,
-  registration_application_view::RegistrationApplicationView,
-};
+use lemmy_db_views::structs::{LocalUserView, RegistrationApplicationView};
 use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::LemmyContext;
 
diff --git a/crates/api/src/site/registration_applications/list.rs b/crates/api/src/site/registration_applications/list.rs
index bd5170d6..27633aa6 100644
--- a/crates/api/src/site/registration_applications/list.rs
+++ b/crates/api/src/site/registration_applications/list.rs
@@ -1,10 +1,8 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  get_local_user_view_from_jwt,
-  is_admin,
   site::{ListRegistrationApplications, ListRegistrationApplicationsResponse},
+  utils::{blocking, get_local_user_view_from_jwt, is_admin},
 };
 use lemmy_db_schema::source::site::Site;
 use lemmy_db_views::registration_application_view::RegistrationApplicationQueryBuilder;
diff --git a/crates/api/src/site/registration_applications/unread_count.rs b/crates/api/src/site/registration_applications/unread_count.rs
index 03584baf..06f99523 100644
--- a/crates/api/src/site/registration_applications/unread_count.rs
+++ b/crates/api/src/site/registration_applications/unread_count.rs
@@ -1,13 +1,11 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  get_local_user_view_from_jwt,
-  is_admin,
   site::{GetUnreadRegistrationApplicationCount, GetUnreadRegistrationApplicationCountResponse},
+  utils::{blocking, get_local_user_view_from_jwt, is_admin},
 };
 use lemmy_db_schema::source::site::Site;
-use lemmy_db_views::registration_application_view::RegistrationApplicationView;
+use lemmy_db_views::structs::RegistrationApplicationView;
 use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::LemmyContext;
 
diff --git a/crates/api/src/site/resolve_object.rs b/crates/api/src/site/resolve_object.rs
index 87f3c859..4e84e162 100644
--- a/crates/api/src/site/resolve_object.rs
+++ b/crates/api/src/site/resolve_object.rs
@@ -2,15 +2,13 @@ use crate::Perform;
 use actix_web::web::Data;
 use diesel::NotFound;
 use lemmy_api_common::{
-  blocking,
-  check_private_instance,
-  get_local_user_view_from_jwt_opt,
   site::{ResolveObject, ResolveObjectResponse},
+  utils::{blocking, check_private_instance, get_local_user_view_from_jwt_opt},
 };
 use lemmy_apub::fetcher::search::{search_by_apub_id, SearchableObjects};
-use lemmy_db_schema::{newtypes::PersonId, DbPool};
-use lemmy_db_views::{comment_view::CommentView, post_view::PostView};
-use lemmy_db_views_actor::{community_view::CommunityView, person_view::PersonViewSafe};
+use lemmy_db_schema::{newtypes::PersonId, utils::DbPool};
+use lemmy_db_views::structs::{CommentView, PostView};
+use lemmy_db_views_actor::structs::{CommunityView, PersonViewSafe};
 use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::LemmyContext;
 
diff --git a/crates/api/src/site/search.rs b/crates/api/src/site/search.rs
index f4f998d5..b4d2069c 100644
--- a/crates/api/src/site/search.rs
+++ b/crates/api/src/site/search.rs
@@ -1,19 +1,14 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  check_private_instance,
-  get_local_user_view_from_jwt_opt,
   site::{Search, SearchResponse},
+  utils::{blocking, check_private_instance, get_local_user_view_from_jwt_opt},
 };
 use lemmy_apub::{fetcher::resolve_actor_identifier, objects::community::ApubCommunity};
 use lemmy_db_schema::{
-  from_opt_str_to_opt_enum,
   source::community::Community,
   traits::DeleteableOrRemoveable,
-  ListingType,
-  SearchType,
-  SortType,
+  utils::{from_opt_str_to_opt_enum, ListingType, SearchType, SortType},
 };
 use lemmy_db_views::{comment_view::CommentQueryBuilder, post_view::PostQueryBuilder};
 use lemmy_db_views_actor::{
diff --git a/crates/api/src/websocket.rs b/crates/api/src/websocket.rs
index 00a7e686..aea443a9 100644
--- a/crates/api/src/websocket.rs
+++ b/crates/api/src/websocket.rs
@@ -1,6 +1,6 @@
 use crate::Perform;
 use actix_web::web::Data;
-use lemmy_api_common::{get_local_user_view_from_jwt, websocket::*};
+use lemmy_api_common::{utils::get_local_user_view_from_jwt, websocket::*};
 use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::{
   messages::{JoinCommunityRoom, JoinModRoom, JoinPostRoom, JoinUserRoom},
diff --git a/crates/api_common/Cargo.toml b/crates/api_common/Cargo.toml
index 40cd753b..2ff49922 100644
--- a/crates/api_common/Cargo.toml
+++ b/crates/api_common/Cargo.toml
@@ -12,16 +12,29 @@ name = "lemmy_api_common"
 path = "src/lib.rs"
 doctest = false
 
+[features]
+full = ["diesel", "tracing", "rosetta-i18n", "chrono", "actix-web", "lemmy_utils",
+    "lemmy_db_views/full", "lemmy_db_views_actor/full", "lemmy_db_views_moderator/full",
+    "percent-encoding", "encoding", "reqwest-middleware", "webpage"]
+
 [dependencies]
 lemmy_db_views = { version = "=0.16.3", path = "../db_views" }
 lemmy_db_views_moderator = { version = "=0.16.3", path = "../db_views_moderator" }
 lemmy_db_views_actor = { version = "=0.16.3", path = "../db_views_actor" }
-lemmy_db_schema = { version = "=0.16.3", path = "../db_schema" }
-lemmy_utils = { version = "=0.16.3", path = "../utils" }
+lemmy_db_schema = { version = "=0.16.3", path = "../db_schema", default-features = false }
+lemmy_utils = { version = "=0.16.3", path = "../utils", optional = true }
 serde = { version = "1.0.136", features = ["derive"] }
-diesel = "1.4.8"
-actix-web = { version = "4.0.1", default-features = false, features = ["cookies"] }
-chrono = { version = "0.4.19", features = ["serde"] }
-tracing = "0.1.32"
 url = "2.2.2"
-rosetta-i18n = "0.1.2"
+actix-web = { version = "4.0.1", default-features = false, features = ["cookies"], optional = true }
+chrono = { version = "0.4.19", features = ["serde"], optional = true }
+diesel = { version = "1.4.8", optional = true }
+tracing = { version = "0.1.32", optional = true }
+rosetta-i18n = { version = "0.1.2", optional = true }
+percent-encoding = { version = "2.1.0", optional = true }
+encoding = { version = "0.2.33", optional = true }
+reqwest-middleware = { version = "0.1.5", optional = true }
+webpage = { version = "1.4.0", default-features = false, features = ["serde"], optional = true }
+
+[dev-dependencies]
+actix-rt = { version = "2.7.0", default-features = false }
+reqwest = { version = "0.11.10", features = ["json"] }
diff --git a/crates/api_common/src/comment.rs b/crates/api_common/src/comment.rs
index 84717800..048a3439 100644
--- a/crates/api_common/src/comment.rs
+++ b/crates/api_common/src/comment.rs
@@ -1,6 +1,6 @@
+use crate::sensitive::Sensitive;
 use lemmy_db_schema::newtypes::{CommentId, CommentReportId, CommunityId, LocalUserId, PostId};
-use lemmy_db_views::{comment_report_view::CommentReportView, comment_view::CommentView};
-use lemmy_utils::Sensitive;
+use lemmy_db_views::structs::{CommentReportView, CommentView};
 use serde::{Deserialize, Serialize};
 
 #[derive(Debug, Serialize, Deserialize)]
diff --git a/crates/api_common/src/community.rs b/crates/api_common/src/community.rs
index 49df41fa..0635b9fa 100644
--- a/crates/api_common/src/community.rs
+++ b/crates/api_common/src/community.rs
@@ -1,13 +1,9 @@
+use crate::sensitive::Sensitive;
 use lemmy_db_schema::{
   newtypes::{CommunityId, PersonId},
   source::site::Site,
 };
-use lemmy_db_views_actor::{
-  community_moderator_view::CommunityModeratorView,
-  community_view::CommunityView,
-  person_view::PersonViewSafe,
-};
-use lemmy_utils::Sensitive;
+use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView, PersonViewSafe};
 use serde::{Deserialize, Serialize};
 
 #[derive(Debug, Serialize, Deserialize)]
diff --git a/crates/api_common/src/lib.rs b/crates/api_common/src/lib.rs
index 330c75bd..0dda34ec 100644
--- a/crates/api_common/src/lib.rs
+++ b/crates/api_common/src/lib.rs
@@ -2,612 +2,10 @@ pub mod comment;
 pub mod community;
 pub mod person;
 pub mod post;
+#[cfg(feature = "full")]
+pub mod request;
+mod sensitive;
 pub mod site;
+#[cfg(feature = "full")]
+pub mod utils;
 pub mod websocket;
-
-use crate::site::FederatedInstances;
-use lemmy_db_schema::{
-  newtypes::{CommunityId, DbUrl, LocalUserId, PersonId, PostId},
-  source::{
-    comment::Comment,
-    community::Community,
-    email_verification::{EmailVerification, EmailVerificationForm},
-    password_reset_request::PasswordResetRequest,
-    person::Person,
-    person_block::PersonBlock,
-    post::{Post, PostRead, PostReadForm},
-    registration_application::RegistrationApplication,
-    secret::Secret,
-    site::Site,
-  },
-  traits::{Crud, Readable},
-  DbPool,
-};
-use lemmy_db_views::{
-  comment_view::CommentQueryBuilder,
-  local_user_view::{LocalUserSettingsView, LocalUserView},
-};
-use lemmy_db_views_actor::{
-  community_moderator_view::CommunityModeratorView,
-  community_person_ban_view::CommunityPersonBanView,
-  community_view::CommunityView,
-};
-use lemmy_utils::{
-  claims::Claims,
-  email::{send_email, translations::Lang},
-  settings::structs::Settings,
-  utils::generate_random_string,
-  LemmyError,
-  Sensitive,
-};
-use rosetta_i18n::{Language, LanguageId};
-use tracing::warn;
-
-pub async fn blocking<F, T>(pool: &DbPool, f: F) -> Result<T, LemmyError>
-where
-  F: FnOnce(&diesel::PgConnection) -> T + Send + 'static,
-  T: Send + 'static,
-{
-  let pool = pool.clone();
-  let blocking_span = tracing::info_span!("blocking operation");
-  let res = actix_web::web::block(move || {
-    let entered = blocking_span.enter();
-    let conn = pool.get()?;
-    let res = (f)(&conn);
-    drop(entered);
-    Ok(res) as Result<T, LemmyError>
-  })
-  .await?;
-
-  res
-}
-
-#[tracing::instrument(skip_all)]
-pub async fn is_mod_or_admin(
-  pool: &DbPool,
-  person_id: PersonId,
-  community_id: CommunityId,
-) -> Result<(), LemmyError> {
-  let is_mod_or_admin = blocking(pool, move |conn| {
-    CommunityView::is_mod_or_admin(conn, person_id, community_id)
-  })
-  .await?;
-  if !is_mod_or_admin {
-    return Err(LemmyError::from_message("not_a_mod_or_admin"));
-  }
-  Ok(())
-}
-
-pub fn is_admin(local_user_view: &LocalUserView) -> Result<(), LemmyError> {
-  if !local_user_view.person.admin {
-    return Err(LemmyError::from_message("not_an_admin"));
-  }
-  Ok(())
-}
-
-#[tracing::instrument(skip_all)]
-pub async fn get_post(post_id: PostId, pool: &DbPool) -> Result<Post, LemmyError> {
-  blocking(pool, move |conn| Post::read(conn, post_id))
-    .await?
-    .map_err(|e| LemmyError::from_error_message(e, "couldnt_find_post"))
-}
-
-#[tracing::instrument(skip_all)]
-pub async fn mark_post_as_read(
-  person_id: PersonId,
-  post_id: PostId,
-  pool: &DbPool,
-) -> Result<PostRead, LemmyError> {
-  let post_read_form = PostReadForm { post_id, person_id };
-
-  blocking(pool, move |conn| {
-    PostRead::mark_as_read(conn, &post_read_form)
-  })
-  .await?
-  .map_err(|e| LemmyError::from_error_message(e, "couldnt_mark_post_as_read"))
-}
-
-#[tracing::instrument(skip_all)]
-pub async fn mark_post_as_unread(
-  person_id: PersonId,
-  post_id: PostId,
-  pool: &DbPool,
-) -> Result<usize, LemmyError> {
-  let post_read_form = PostReadForm { post_id, person_id };
-
-  blocking(pool, move |conn| {
-    PostRead::mark_as_unread(conn, &post_read_form)
-  })
-  .await?
-  .map_err(|e| LemmyError::from_error_message(e, "couldnt_mark_post_as_read"))
-}
-
-#[tracing::instrument(skip_all)]
-pub async fn get_local_user_view_from_jwt(
-  jwt: &str,
-  pool: &DbPool,
-  secret: &Secret,
-) -> Result<LocalUserView, LemmyError> {
-  let claims = Claims::decode(jwt, &secret.jwt_secret)
-    .map_err(|e| e.with_message("not_logged_in"))?
-    .claims;
-  let local_user_id = LocalUserId(claims.sub);
-  let local_user_view =
-    blocking(pool, move |conn| LocalUserView::read(conn, local_user_id)).await??;
-  // Check for a site ban
-  if local_user_view.person.is_banned() {
-    return Err(LemmyError::from_message("site_ban"));
-  }
-
-  // Check for user deletion
-  if local_user_view.person.deleted {
-    return Err(LemmyError::from_message("deleted"));
-  }
-
-  check_validator_time(&local_user_view.local_user.validator_time, &claims)?;
-
-  Ok(local_user_view)
-}
-
-/// Checks if user's token was issued before user's password reset.
-pub fn check_validator_time(
-  validator_time: &chrono::NaiveDateTime,
-  claims: &Claims,
-) -> Result<(), LemmyError> {
-  let user_validation_time = validator_time.timestamp();
-  if user_validation_time > claims.iat {
-    Err(LemmyError::from_message("not_logged_in"))
-  } else {
-    Ok(())
-  }
-}
-
-#[tracing::instrument(skip_all)]
-pub async fn get_local_user_view_from_jwt_opt(
-  jwt: Option<&Sensitive<String>>,
-  pool: &DbPool,
-  secret: &Secret,
-) -> Result<Option<LocalUserView>, LemmyError> {
-  match jwt {
-    Some(jwt) => Ok(Some(get_local_user_view_from_jwt(jwt, pool, secret).await?)),
-    None => Ok(None),
-  }
-}
-
-#[tracing::instrument(skip_all)]
-pub async fn get_local_user_settings_view_from_jwt(
-  jwt: &Sensitive<String>,
-  pool: &DbPool,
-  secret: &Secret,
-) -> Result<LocalUserSettingsView, LemmyError> {
-  let claims = Claims::decode(jwt.as_ref(), &secret.jwt_secret)
-    .map_err(|e| e.with_message("not_logged_in"))?
-    .claims;
-  let local_user_id = LocalUserId(claims.sub);
-  let local_user_view = blocking(pool, move |conn| {
-    LocalUserSettingsView::read(conn, local_user_id)
-  })
-  .await??;
-  // Check for a site ban
-  if local_user_view.person.is_banned() {
-    return Err(LemmyError::from_message("site_ban"));
-  }
-
-  check_validator_time(&local_user_view.local_user.validator_time, &claims)?;
-
-  Ok(local_user_view)
-}
-
-#[tracing::instrument(skip_all)]
-pub async fn get_local_user_settings_view_from_jwt_opt(
-  jwt: Option<&Sensitive<String>>,
-  pool: &DbPool,
-  secret: &Secret,
-) -> Result<Option<LocalUserSettingsView>, LemmyError> {
-  match jwt {
-    Some(jwt) => Ok(Some(
-      get_local_user_settings_view_from_jwt(jwt, pool, secret).await?,
-    )),
-    None => Ok(None),
-  }
-}
-
-#[tracing::instrument(skip_all)]
-pub async fn check_community_ban(
-  person_id: PersonId,
-  community_id: CommunityId,
-  pool: &DbPool,
-) -> Result<(), LemmyError> {
-  let is_banned =
-    move |conn: &'_ _| CommunityPersonBanView::get(conn, person_id, community_id).is_ok();
-  if blocking(pool, is_banned).await? {
-    Err(LemmyError::from_message("community_ban"))
-  } else {
-    Ok(())
-  }
-}
-
-#[tracing::instrument(skip_all)]
-pub async fn check_community_deleted_or_removed(
-  community_id: CommunityId,
-  pool: &DbPool,
-) -> Result<(), LemmyError> {
-  let community = blocking(pool, move |conn| Community::read(conn, community_id))
-    .await?
-    .map_err(|e| LemmyError::from_error_message(e, "couldnt_find_community"))?;
-  if community.deleted || community.removed {
-    Err(LemmyError::from_message("deleted"))
-  } else {
-    Ok(())
-  }
-}
-
-pub fn check_post_deleted_or_removed(post: &Post) -> Result<(), LemmyError> {
-  if post.deleted || post.removed {
-    Err(LemmyError::from_message("deleted"))
-  } else {
-    Ok(())
-  }
-}
-
-#[tracing::instrument(skip_all)]
-pub async fn check_person_block(
-  my_id: PersonId,
-  potential_blocker_id: PersonId,
-  pool: &DbPool,
-) -> Result<(), LemmyError> {
-  let is_blocked = move |conn: &'_ _| PersonBlock::read(conn, potential_blocker_id, my_id).is_ok();
-  if blocking(pool, is_blocked).await? {
-    Err(LemmyError::from_message("person_block"))
-  } else {
-    Ok(())
-  }
-}
-
-#[tracing::instrument(skip_all)]
-pub async fn check_downvotes_enabled(score: i16, pool: &DbPool) -> Result<(), LemmyError> {
-  if score == -1 {
-    let site = blocking(pool, Site::read_local_site).await??;
-    if !site.enable_downvotes {
-      return Err(LemmyError::from_message("downvotes_disabled"));
-    }
-  }
-  Ok(())
-}
-
-#[tracing::instrument(skip_all)]
-pub async fn check_private_instance(
-  local_user_view: &Option<LocalUserView>,
-  pool: &DbPool,
-) -> Result<(), LemmyError> {
-  if local_user_view.is_none() {
-    let site = blocking(pool, Site::read_local_site).await?;
-
-    // The site might not be set up yet
-    if let Ok(site) = site {
-      if site.private_instance {
-        return Err(LemmyError::from_message("instance_is_private"));
-      }
-    }
-  }
-  Ok(())
-}
-
-#[tracing::instrument(skip_all)]
-pub async fn build_federated_instances(
-  pool: &DbPool,
-  settings: &Settings,
-) -> Result<Option<FederatedInstances>, LemmyError> {
-  let federation_config = &settings.federation;
-  let hostname = &settings.hostname;
-  let federation = federation_config.to_owned();
-  if federation.enabled {
-    let distinct_communities = blocking(pool, move |conn| {
-      Community::distinct_federated_communities(conn)
-    })
-    .await??;
-
-    let allowed = federation.allowed_instances;
-    let blocked = federation.blocked_instances;
-
-    let mut linked = distinct_communities
-      .iter()
-      .map(|actor_id| Ok(actor_id.host_str().unwrap_or("").to_string()))
-      .collect::<Result<Vec<String>, LemmyError>>()?;
-
-    if let Some(allowed) = allowed.as_ref() {
-      linked.extend_from_slice(allowed);
-    }
-
-    if let Some(blocked) = blocked.as_ref() {
-      linked.retain(|a| !blocked.contains(a) && !a.eq(hostname));
-    }
-
-    // Sort and remove dupes
-    linked.sort_unstable();
-    linked.dedup();
-
-    Ok(Some(FederatedInstances {
-      linked,
-      allowed,
-      blocked,
-    }))
-  } else {
-    Ok(None)
-  }
-}
-
-/// Checks the password length
-pub fn password_length_check(pass: &str) -> Result<(), LemmyError> {
-  if !(10..=60).contains(&pass.len()) {
-    Err(LemmyError::from_message("invalid_password"))
-  } else {
-    Ok(())
-  }
-}
-
-/// Checks the site description length
-pub fn site_description_length_check(description: &str) -> Result<(), LemmyError> {
-  if description.len() > 150 {
-    Err(LemmyError::from_message("site_description_length_overflow"))
-  } else {
-    Ok(())
-  }
-}
-
-/// Checks for a honeypot. If this field is filled, fail the rest of the function
-pub fn honeypot_check(honeypot: &Option<String>) -> Result<(), LemmyError> {
-  if honeypot.is_some() {
-    Err(LemmyError::from_message("honeypot_fail"))
-  } else {
-    Ok(())
-  }
-}
-
-pub fn send_email_to_user(
-  local_user_view: &LocalUserView,
-  subject: &str,
-  body: &str,
-  settings: &Settings,
-) {
-  if local_user_view.person.banned || !local_user_view.local_user.send_notifications_to_email {
-    return;
-  }
-
-  if let Some(user_email) = &local_user_view.local_user.email {
-    match send_email(
-      subject,
-      user_email,
-      &local_user_view.person.name,
-      body,
-      settings,
-    ) {
-      Ok(_o) => _o,
-      Err(e) => warn!("{}", e),
-    };
-  }
-}
-
-pub async fn send_password_reset_email(
-  user: &LocalUserView,
-  pool: &DbPool,
-  settings: &Settings,
-) -> Result<(), LemmyError> {
-  // Generate a random token
-  let token = generate_random_string();
-
-  // Insert the row
-  let token2 = token.clone();
-  let local_user_id = user.local_user.id;
-  blocking(pool, move |conn| {
-    PasswordResetRequest::create_token(conn, local_user_id, &token2)
-  })
-  .await??;
-
-  let email = &user.local_user.email.to_owned().expect("email");
-  let lang = get_user_lang(user);
-  let subject = &lang.password_reset_subject(&user.person.name);
-  let protocol_and_hostname = settings.get_protocol_and_hostname();
-  let reset_link = format!("{}/password_change/{}", protocol_and_hostname, &token);
-  let body = &lang.password_reset_body(&user.person.name, reset_link);
-  send_email(subject, email, &user.person.name, body, settings)
-}
-
-/// Send a verification email
-pub async fn send_verification_email(
-  user: &LocalUserView,
-  new_email: &str,
-  pool: &DbPool,
-  settings: &Settings,
-) -> Result<(), LemmyError> {
-  let form = EmailVerificationForm {
-    local_user_id: user.local_user.id,
-    email: new_email.to_string(),
-    verification_token: generate_random_string(),
-  };
-  let verify_link = format!(
-    "{}/verify_email/{}",
-    settings.get_protocol_and_hostname(),
-    &form.verification_token
-  );
-  blocking(pool, move |conn| EmailVerification::create(conn, &form)).await??;
-
-  let lang = get_user_lang(user);
-  let subject = lang.verify_email_subject(&settings.hostname);
-  let body = lang.verify_email_body(&user.person.name, &settings.hostname, verify_link);
-  send_email(&subject, new_email, &user.person.name, &body, settings)?;
-
-  Ok(())
-}
-
-pub fn send_email_verification_success(
-  user: &LocalUserView,
-  settings: &Settings,
-) -> Result<(), LemmyError> {
-  let email = &user.local_user.email.to_owned().expect("email");
-  let lang = get_user_lang(user);
-  let subject = &lang.email_verified_subject(&user.person.actor_id);
-  let body = &lang.email_verified_body();
-  send_email(subject, email, &user.person.name, body, settings)
-}
-
-pub fn get_user_lang(user: &LocalUserView) -> Lang {
-  let user_lang = LanguageId::new(user.local_user.lang.clone());
-  Lang::from_language_id(&user_lang).unwrap_or_else(|| {
-    let en = LanguageId::new("en");
-    Lang::from_language_id(&en).expect("default language")
-  })
-}
-
-pub fn send_application_approved_email(
-  user: &LocalUserView,
-  settings: &Settings,
-) -> Result<(), LemmyError> {
-  let email = &user.local_user.email.to_owned().expect("email");
-  let lang = get_user_lang(user);
-  let subject = lang.registration_approved_subject(&user.person.actor_id);
-  let body = lang.registration_approved_body(&settings.hostname);
-  send_email(&subject, email, &user.person.name, &body, settings)
-}
-
-pub async fn check_registration_application(
-  site: &Site,
-  local_user_view: &LocalUserView,
-  pool: &DbPool,
-) -> Result<(), LemmyError> {
-  if site.require_application
-    && !local_user_view.local_user.accepted_application
-    && !local_user_view.person.admin
-  {
-    // Fetch the registration, see if its denied
-    let local_user_id = local_user_view.local_user.id;
-    let registration = blocking(pool, move |conn| {
-      RegistrationApplication::find_by_local_user_id(conn, local_user_id)
-    })
-    .await??;
-    if let Some(deny_reason) = registration.deny_reason {
-      let lang = get_user_lang(local_user_view);
-      let registration_denied_message = format!("{}: {}", lang.registration_denied(), &deny_reason);
-      return Err(LemmyError::from_message(&registration_denied_message));
-    } else {
-      return Err(LemmyError::from_message("registration_application_pending"));
-    }
-  }
-  Ok(())
-}
-
-/// TODO this check should be removed after https://github.com/LemmyNet/lemmy/issues/868 is done.
-pub async fn check_private_instance_and_federation_enabled(
-  pool: &DbPool,
-  settings: &Settings,
-) -> Result<(), LemmyError> {
-  let site_opt = blocking(pool, Site::read_local_site).await?;
-
-  if let Ok(site) = site_opt {
-    if site.private_instance && settings.federation.enabled {
-      return Err(LemmyError::from_message(
-        "Cannot have both private instance and federation enabled.",
-      ));
-    }
-  }
-  Ok(())
-}
-
-pub async fn remove_user_data(banned_person_id: PersonId, pool: &DbPool) -> Result<(), LemmyError> {
-  // Posts
-  blocking(pool, move |conn: &'_ _| {
-    Post::update_removed_for_creator(conn, banned_person_id, None, true)
-  })
-  .await??;
-
-  // Communities
-  // Remove all communities where they're the top mod
-  // for now, remove the communities manually
-  let first_mod_communities = blocking(pool, move |conn: &'_ _| {
-    CommunityModeratorView::get_community_first_mods(conn)
-  })
-  .await??;
-
-  // Filter to only this banned users top communities
-  let banned_user_first_communities: Vec<CommunityModeratorView> = first_mod_communities
-    .into_iter()
-    .filter(|fmc| fmc.moderator.id == banned_person_id)
-    .collect();
-
-  for first_mod_community in banned_user_first_communities {
-    blocking(pool, move |conn: &'_ _| {
-      Community::update_removed(conn, first_mod_community.community.id, true)
-    })
-    .await??;
-  }
-
-  // Comments
-  blocking(pool, move |conn: &'_ _| {
-    Comment::update_removed_for_creator(conn, banned_person_id, true)
-  })
-  .await??;
-
-  Ok(())
-}
-
-pub async fn remove_user_data_in_community(
-  community_id: CommunityId,
-  banned_person_id: PersonId,
-  pool: &DbPool,
-) -> Result<(), LemmyError> {
-  // Posts
-  blocking(pool, move |conn| {
-    Post::update_removed_for_creator(conn, banned_person_id, Some(community_id), true)
-  })
-  .await??;
-
-  // Comments
-  // TODO Diesel doesn't allow updates with joins, so this has to be a loop
-  let comments = blocking(pool, move |conn| {
-    CommentQueryBuilder::create(conn)
-      .creator_id(banned_person_id)
-      .community_id(community_id)
-      .limit(std::i64::MAX)
-      .list()
-  })
-  .await??;
-
-  for comment_view in &comments {
-    let comment_id = comment_view.comment.id;
-    blocking(pool, move |conn| {
-      Comment::update_removed(conn, comment_id, true)
-    })
-    .await??;
-  }
-
-  Ok(())
-}
-
-pub async fn delete_user_account(person_id: PersonId, pool: &DbPool) -> Result<(), LemmyError> {
-  // Comments
-  let permadelete = move |conn: &'_ _| Comment::permadelete_for_creator(conn, person_id);
-  blocking(pool, permadelete)
-    .await?
-    .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?;
-
-  // Posts
-  let permadelete = move |conn: &'_ _| Post::permadelete_for_creator(conn, person_id);
-  blocking(pool, permadelete)
-    .await?
-    .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_post"))?;
-
-  blocking(pool, move |conn| Person::delete_account(conn, person_id)).await??;
-
-  Ok(())
-}
-
-pub fn check_image_has_local_domain(url: &Option<DbUrl>) -> Result<(), LemmyError> {
-  if let Some(url) = url {
-    let settings = Settings::get();
-    let domain = url.domain().expect("url has domain");
-    if domain != settings.hostname {
-      return Err(LemmyError::from_message("image_not_local"));
-    }
-  }
-  Ok(())
-}
diff --git a/crates/api_common/src/person.rs b/crates/api_common/src/person.rs
index 898efbf5..3bc09f77 100644
--- a/crates/api_common/src/person.rs
+++ b/crates/api_common/src/person.rs
@@ -1,14 +1,6 @@
-use lemmy_db_views::{
-  comment_view::CommentView,
-  post_view::PostView,
-  private_message_view::PrivateMessageView,
-};
-use lemmy_db_views_actor::{
-  community_moderator_view::CommunityModeratorView,
-  person_mention_view::PersonMentionView,
-  person_view::PersonViewSafe,
-};
-use lemmy_utils::Sensitive;
+use crate::sensitive::Sensitive;
+use lemmy_db_views::structs::{CommentView, PostView, PrivateMessageView};
+use lemmy_db_views_actor::structs::{CommunityModeratorView, PersonMentionView, PersonViewSafe};
 use serde::{Deserialize, Serialize};
 
 #[derive(Debug, Serialize, Deserialize)]
diff --git a/crates/api_common/src/post.rs b/crates/api_common/src/post.rs
index 7f8ea319..afab5e30 100644
--- a/crates/api_common/src/post.rs
+++ b/crates/api_common/src/post.rs
@@ -1,14 +1,7 @@
+use crate::sensitive::Sensitive;
 use lemmy_db_schema::newtypes::{CommunityId, PostId, PostReportId};
-use lemmy_db_views::{
-  comment_view::CommentView,
-  post_report_view::PostReportView,
-  post_view::PostView,
-};
-use lemmy_db_views_actor::{
-  community_moderator_view::CommunityModeratorView,
-  community_view::CommunityView,
-};
-use lemmy_utils::{request::SiteMetadata, Sensitive};
+use lemmy_db_views::structs::{CommentView, PostReportView, PostView};
+use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView};
 use serde::{Deserialize, Serialize};
 use url::Url;
 
@@ -164,3 +157,11 @@ pub struct GetSiteMetadata {
 pub struct GetSiteMetadataResponse {
   pub metadata: SiteMetadata,
 }
+
+#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
+pub struct SiteMetadata {
+  pub title: Option<String>,
+  pub description: Option<String>,
+  pub(crate) image: Option<Url>,
+  pub html: Option<String>,
+}
diff --git a/crates/api_common/src/request.rs b/crates/api_common/src/request.rs
new file mode 100644
index 00000000..7e3b2152
--- /dev/null
+++ b/crates/api_common/src/request.rs
@@ -0,0 +1,274 @@
+use crate::post::SiteMetadata;
+use encoding::{all::encodings, DecoderTrap};
+use lemmy_utils::{settings::structs::Settings, version::VERSION, LemmyError, REQWEST_TIMEOUT};
+use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
+use reqwest_middleware::ClientWithMiddleware;
+use serde::Deserialize;
+use tracing::info;
+use url::Url;
+use webpage::HTML;
+
+/// Fetches the post link html tags (like title, description, image, etc)
+#[tracing::instrument(skip_all)]
+pub async fn fetch_site_metadata(
+  client: &ClientWithMiddleware,
+  url: &Url,
+) -> Result<SiteMetadata, LemmyError> {
+  info!("Fetching site metadata for url: {}", url);
+  let response = client
+    .get(url.as_str())
+    .timeout(REQWEST_TIMEOUT)
+    .send()
+    .await?;
+
+  // Can't use .text() here, because it only checks the content header, not the actual bytes
+  // https://github.com/LemmyNet/lemmy/issues/1964
+  let html_bytes = response.bytes().await.map_err(LemmyError::from)?.to_vec();
+
+  let tags = html_to_site_metadata(&html_bytes)?;
+
+  Ok(tags)
+}
+
+fn html_to_site_metadata(html_bytes: &[u8]) -> Result<SiteMetadata, LemmyError> {
+  let html = String::from_utf8_lossy(html_bytes);
+
+  // Make sure the first line is doctype html
+  let first_line = html
+    .trim_start()
+    .lines()
+    .into_iter()
+    .next()
+    .ok_or_else(|| LemmyError::from_message("No lines in html"))?
+    .to_lowercase();
+
+  if !first_line.starts_with("<!doctype html>") {
+    return Err(LemmyError::from_message(
+      "Site metadata page fetch is not DOCTYPE html",
+    ));
+  }
+
+  let mut page = HTML::from_string(html.to_string(), None)?;
+
+  // If the web page specifies that it isn't actually UTF-8, re-decode the received bytes with the
+  // proper encoding. If the specified encoding cannot be found, fall back to the original UTF-8
+  // version.
+  if let Some(charset) = page.meta.get("charset") {
+    if charset.to_lowercase() != "utf-8" {
+      if let Some(encoding_ref) = encodings().iter().find(|e| e.name() == charset) {
+        if let Ok(html_with_encoding) = encoding_ref.decode(html_bytes, DecoderTrap::Replace) {
+          page = HTML::from_string(html_with_encoding, None)?;
+        }
+      }
+    }
+  }
+
+  let page_title = page.title;
+  let page_description = page.description;
+
+  let og_description = page
+    .opengraph
+    .properties
+    .get("description")
+    .map(|t| t.to_string());
+  let og_title = page
+    .opengraph
+    .properties
+    .get("title")
+    .map(|t| t.to_string());
+  let og_image = page
+    .opengraph
+    .images
+    .get(0)
+    .and_then(|ogo| Url::parse(&ogo.url).ok());
+
+  let title = og_title.or(page_title);
+  let description = og_description.or(page_description);
+  let image = og_image;
+
+  Ok(SiteMetadata {
+    title,
+    description,
+    image,
+    html: None,
+  })
+}
+
+#[derive(Deserialize, Debug, Clone)]
+pub(crate) struct PictrsResponse {
+  files: Vec<PictrsFile>,
+  msg: String,
+}
+
+#[derive(Deserialize, Debug, Clone)]
+pub(crate) struct PictrsFile {
+  file: String,
+  #[allow(dead_code)]
+  delete_token: String,
+}
+
+#[tracing::instrument(skip_all)]
+pub(crate) async fn fetch_pictrs(
+  client: &ClientWithMiddleware,
+  settings: &Settings,
+  image_url: &Url,
+) -> Result<PictrsResponse, LemmyError> {
+  if let Some(pictrs_url) = settings.pictrs_url.to_owned() {
+    is_image_content_type(client, image_url).await?;
+
+    let fetch_url = format!(
+      "{}/image/download?url={}",
+      pictrs_url,
+      utf8_percent_encode(image_url.as_str(), NON_ALPHANUMERIC) // TODO this might not be needed
+    );
+
+    let response = client
+      .get(&fetch_url)
+      .timeout(REQWEST_TIMEOUT)
+      .send()
+      .await?;
+
+    let response: PictrsResponse = response.json().await.map_err(LemmyError::from)?;
+
+    if response.msg == "ok" {
+      Ok(response)
+    } else {
+      Err(LemmyError::from_message(&response.msg))
+    }
+  } else {
+    Err(LemmyError::from_message("pictrs_url not set up in config"))
+  }
+}
+
+/// Both are options, since the URL might be either an html page, or an image
+/// Returns the SiteMetadata, and a Pictrs URL, if there is a picture associated
+#[tracing::instrument(skip_all)]
+pub async fn fetch_site_data(
+  client: &ClientWithMiddleware,
+  settings: &Settings,
+  url: Option<&Url>,
+) -> (Option<SiteMetadata>, Option<Url>) {
+  match &url {
+    Some(url) => {
+      // Fetch metadata
+      // Ignore errors, since it may be an image, or not have the data.
+      // Warning, this may ignore SSL errors
+      let metadata_option = fetch_site_metadata(client, url).await.ok();
+
+      // Fetch pictrs thumbnail
+      let pictrs_hash = match &metadata_option {
+        Some(metadata_res) => match &metadata_res.image {
+          // Metadata, with image
+          // Try to generate a small thumbnail if there's a full sized one from post-links
+          Some(metadata_image) => fetch_pictrs(client, settings, metadata_image)
+            .await
+            .map(|r| r.files[0].file.to_owned()),
+          // Metadata, but no image
+          None => fetch_pictrs(client, settings, url)
+            .await
+            .map(|r| r.files[0].file.to_owned()),
+        },
+        // No metadata, try to fetch the URL as an image
+        None => fetch_pictrs(client, settings, url)
+          .await
+          .map(|r| r.files[0].file.to_owned()),
+      };
+
+      // The full urls are necessary for federation
+      let pictrs_thumbnail = pictrs_hash
+        .map(|p| {
+          Url::parse(&format!(
+            "{}/pictrs/image/{}",
+            settings.get_protocol_and_hostname(),
+            p
+          ))
+          .ok()
+        })
+        .ok()
+        .flatten();
+
+      (metadata_option, pictrs_thumbnail)
+    }
+    None => (None, None),
+  }
+}
+
+#[tracing::instrument(skip_all)]
+async fn is_image_content_type(client: &ClientWithMiddleware, url: &Url) -> Result<(), LemmyError> {
+  let response = client
+    .get(url.as_str())
+    .timeout(REQWEST_TIMEOUT)
+    .send()
+    .await?;
+  if response
+    .headers()
+    .get("Content-Type")
+    .ok_or_else(|| LemmyError::from_message("No Content-Type header"))?
+    .to_str()?
+    .starts_with("image/")
+  {
+    Ok(())
+  } else {
+    Err(LemmyError::from_message("Not an image type."))
+  }
+}
+
+pub fn build_user_agent(settings: &Settings) -> String {
+  format!(
+    "Lemmy/{}; +{}",
+    VERSION,
+    settings.get_protocol_and_hostname()
+  )
+}
+
+#[cfg(test)]
+mod tests {
+  use crate::request::{build_user_agent, fetch_site_metadata, SiteMetadata};
+  use lemmy_utils::settings::structs::Settings;
+  use url::Url;
+
+  // These helped with testing
+  #[actix_rt::test]
+  async fn test_site_metadata() {
+    let settings = Settings::init().unwrap();
+    let client = reqwest::Client::builder()
+      .user_agent(build_user_agent(&settings))
+      .build()
+      .unwrap()
+      .into();
+    let sample_url = Url::parse("https://gitlab.com/IzzyOnDroid/repo/-/wikis/FAQ").unwrap();
+    let sample_res = fetch_site_metadata(&client, &sample_url).await.unwrap();
+    assert_eq!(
+      SiteMetadata {
+        title: Some("FAQ · Wiki · IzzyOnDroid / repo · GitLab".to_string()),
+        description: Some(
+          "The F-Droid compatible repo at https://apt.izzysoft.de/fdroid/".to_string()
+        ),
+        image: Some(
+          Url::parse("https://gitlab.com/uploads/-/system/project/avatar/4877469/iod_logo.png")
+            .unwrap()
+        ),
+        html: None,
+      },
+      sample_res
+    );
+
+    let youtube_url = Url::parse("https://www.youtube.com/watch?v=IquO_TcMZIQ").unwrap();
+    let youtube_res = fetch_site_metadata(&client, &youtube_url).await.unwrap();
+    assert_eq!(
+      SiteMetadata {
+        title: Some("A Hard Look at Rent and Rent Seeking with Michael Hudson & Pepe Escobar".to_string()),
+        description: Some("An interactive discussion on wealth inequality and the “Great Game” on the control of natural resources.In this webinar organized jointly by the Henry George...".to_string()),
+        image: Some(Url::parse("https://i.ytimg.com/vi/IquO_TcMZIQ/maxresdefault.jpg").unwrap()),
+        html: None,
+      }, youtube_res);
+  }
+
+  // #[test]
+  // fn test_pictshare() {
+  //   let res = fetch_pictshare("https://upload.wikimedia.org/wikipedia/en/2/27/The_Mandalorian_logo.jpg");
+  //   assert!(res.is_ok());
+  //   let res_other = fetch_pictshare("https://upload.wikimedia.org/wikipedia/en/2/27/The_Mandalorian_logo.jpgaoeu");
+  //   assert!(res_other.is_err());
+  // }
+}
diff --git a/crates/utils/src/sensitive.rs b/crates/api_common/src/sensitive.rs
similarity index 90%
rename from crates/utils/src/sensitive.rs
rename to crates/api_common/src/sensitive.rs
index c71f357e..7713bc82 100644
--- a/crates/utils/src/sensitive.rs
+++ b/crates/api_common/src/sensitive.rs
@@ -1,9 +1,10 @@
+use serde::{Deserialize, Serialize};
 use std::{
   borrow::Borrow,
   ops::{Deref, DerefMut},
 };
 
-#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Deserialize, serde::Serialize)]
+#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
 #[serde(transparent)]
 pub struct Sensitive<T>(T);
 
@@ -12,8 +13,8 @@ impl<T> Sensitive<T> {
     Sensitive(item)
   }
 
-  pub fn into_inner(this: Self) -> T {
-    this.0
+  pub fn into_inner(self) -> T {
+    self.0
   }
 }
 
diff --git a/crates/api_common/src/site.rs b/crates/api_common/src/site.rs
index f7329822..da17eb05 100644
--- a/crates/api_common/src/site.rs
+++ b/crates/api_common/src/site.rs
@@ -1,33 +1,33 @@
+use crate::sensitive::Sensitive;
 use lemmy_db_schema::newtypes::{CommunityId, PersonId};
-use lemmy_db_views::{
-  comment_view::CommentView,
-  local_user_view::LocalUserSettingsView,
-  post_view::PostView,
-  registration_application_view::RegistrationApplicationView,
-  site_view::SiteView,
+use lemmy_db_views::structs::{
+  CommentView,
+  LocalUserSettingsView,
+  PostView,
+  RegistrationApplicationView,
+  SiteView,
 };
-use lemmy_db_views_actor::{
-  community_block_view::CommunityBlockView,
-  community_follower_view::CommunityFollowerView,
-  community_moderator_view::CommunityModeratorView,
-  community_view::CommunityView,
-  person_block_view::PersonBlockView,
-  person_view::PersonViewSafe,
+use lemmy_db_views_actor::structs::{
+  CommunityBlockView,
+  CommunityFollowerView,
+  CommunityModeratorView,
+  CommunityView,
+  PersonBlockView,
+  PersonViewSafe,
 };
-use lemmy_db_views_moderator::{
-  mod_add_community_view::ModAddCommunityView,
-  mod_add_view::ModAddView,
-  mod_ban_from_community_view::ModBanFromCommunityView,
-  mod_ban_view::ModBanView,
-  mod_hide_community_view::ModHideCommunityView,
-  mod_lock_post_view::ModLockPostView,
-  mod_remove_comment_view::ModRemoveCommentView,
-  mod_remove_community_view::ModRemoveCommunityView,
-  mod_remove_post_view::ModRemovePostView,
-  mod_sticky_post_view::ModStickyPostView,
-  mod_transfer_community_view::ModTransferCommunityView,
+use lemmy_db_views_moderator::structs::{
+  ModAddCommunityView,
+  ModAddView,
+  ModBanFromCommunityView,
+  ModBanView,
+  ModHideCommunityView,
+  ModLockPostView,
+  ModRemoveCommentView,
+  ModRemoveCommunityView,
+  ModRemovePostView,
+  ModStickyPostView,
+  ModTransferCommunityView,
 };
-use lemmy_utils::Sensitive;
 use serde::{Deserialize, Serialize};
 
 #[derive(Serialize, Deserialize, Debug)]
diff --git a/crates/api_common/src/utils.rs b/crates/api_common/src/utils.rs
new file mode 100644
index 00000000..38ea9a86
--- /dev/null
+++ b/crates/api_common/src/utils.rs
@@ -0,0 +1,605 @@
+use crate::{sensitive::Sensitive, site::FederatedInstances};
+use lemmy_db_schema::{
+  newtypes::{CommunityId, DbUrl, LocalUserId, PersonId, PostId},
+  source::{
+    comment::Comment,
+    community::Community,
+    email_verification::{EmailVerification, EmailVerificationForm},
+    password_reset_request::PasswordResetRequest,
+    person::Person,
+    person_block::PersonBlock,
+    post::{Post, PostRead, PostReadForm},
+    registration_application::RegistrationApplication,
+    secret::Secret,
+    site::Site,
+  },
+  traits::{Crud, Readable},
+  utils::DbPool,
+};
+use lemmy_db_views::{
+  comment_view::CommentQueryBuilder,
+  structs::{LocalUserSettingsView, LocalUserView},
+};
+use lemmy_db_views_actor::structs::{
+  CommunityModeratorView,
+  CommunityPersonBanView,
+  CommunityView,
+};
+use lemmy_utils::{
+  claims::Claims,
+  email::{send_email, translations::Lang},
+  settings::structs::Settings,
+  utils::generate_random_string,
+  LemmyError,
+};
+use rosetta_i18n::{Language, LanguageId};
+use tracing::warn;
+
+pub async fn blocking<F, T>(pool: &DbPool, f: F) -> Result<T, LemmyError>
+where
+  F: FnOnce(&diesel::PgConnection) -> T + Send + 'static,
+  T: Send + 'static,
+{
+  let pool = pool.clone();
+  let blocking_span = tracing::info_span!("blocking operation");
+  let res = actix_web::web::block(move || {
+    let entered = blocking_span.enter();
+    let conn = pool.get()?;
+    let res = (f)(&conn);
+    drop(entered);
+    Ok(res) as Result<T, LemmyError>
+  })
+  .await?;
+
+  res
+}
+
+#[tracing::instrument(skip_all)]
+pub async fn is_mod_or_admin(
+  pool: &DbPool,
+  person_id: PersonId,
+  community_id: CommunityId,
+) -> Result<(), LemmyError> {
+  let is_mod_or_admin = blocking(pool, move |conn| {
+    CommunityView::is_mod_or_admin(conn, person_id, community_id)
+  })
+  .await?;
+  if !is_mod_or_admin {
+    return Err(LemmyError::from_message("not_a_mod_or_admin"));
+  }
+  Ok(())
+}
+
+pub fn is_admin(local_user_view: &LocalUserView) -> Result<(), LemmyError> {
+  if !local_user_view.person.admin {
+    return Err(LemmyError::from_message("not_an_admin"));
+  }
+  Ok(())
+}
+
+#[tracing::instrument(skip_all)]
+pub async fn get_post(post_id: PostId, pool: &DbPool) -> Result<Post, LemmyError> {
+  blocking(pool, move |conn| Post::read(conn, post_id))
+    .await?
+    .map_err(|e| LemmyError::from_error_message(e, "couldnt_find_post"))
+}
+
+#[tracing::instrument(skip_all)]
+pub async fn mark_post_as_read(
+  person_id: PersonId,
+  post_id: PostId,
+  pool: &DbPool,
+) -> Result<PostRead, LemmyError> {
+  let post_read_form = PostReadForm { post_id, person_id };
+
+  blocking(pool, move |conn| {
+    PostRead::mark_as_read(conn, &post_read_form)
+  })
+  .await?
+  .map_err(|e| LemmyError::from_error_message(e, "couldnt_mark_post_as_read"))
+}
+
+#[tracing::instrument(skip_all)]
+pub async fn mark_post_as_unread(
+  person_id: PersonId,
+  post_id: PostId,
+  pool: &DbPool,
+) -> Result<usize, LemmyError> {
+  let post_read_form = PostReadForm { post_id, person_id };
+
+  blocking(pool, move |conn| {
+    PostRead::mark_as_unread(conn, &post_read_form)
+  })
+  .await?
+  .map_err(|e| LemmyError::from_error_message(e, "couldnt_mark_post_as_read"))
+}
+
+#[tracing::instrument(skip_all)]
+pub async fn get_local_user_view_from_jwt(
+  jwt: &str,
+  pool: &DbPool,
+  secret: &Secret,
+) -> Result<LocalUserView, LemmyError> {
+  let claims = Claims::decode(jwt, &secret.jwt_secret)
+    .map_err(|e| e.with_message("not_logged_in"))?
+    .claims;
+  let local_user_id = LocalUserId(claims.sub);
+  let local_user_view =
+    blocking(pool, move |conn| LocalUserView::read(conn, local_user_id)).await??;
+  // Check for a site ban
+  if local_user_view.person.is_banned() {
+    return Err(LemmyError::from_message("site_ban"));
+  }
+
+  // Check for user deletion
+  if local_user_view.person.deleted {
+    return Err(LemmyError::from_message("deleted"));
+  }
+
+  check_validator_time(&local_user_view.local_user.validator_time, &claims)?;
+
+  Ok(local_user_view)
+}
+
+/// Checks if user's token was issued before user's password reset.
+pub fn check_validator_time(
+  validator_time: &chrono::NaiveDateTime,
+  claims: &Claims,
+) -> Result<(), LemmyError> {
+  let user_validation_time = validator_time.timestamp();
+  if user_validation_time > claims.iat {
+    Err(LemmyError::from_message("not_logged_in"))
+  } else {
+    Ok(())
+  }
+}
+
+#[tracing::instrument(skip_all)]
+pub async fn get_local_user_view_from_jwt_opt(
+  jwt: Option<&Sensitive<String>>,
+  pool: &DbPool,
+  secret: &Secret,
+) -> Result<Option<LocalUserView>, LemmyError> {
+  match jwt {
+    Some(jwt) => Ok(Some(get_local_user_view_from_jwt(jwt, pool, secret).await?)),
+    None => Ok(None),
+  }
+}
+
+#[tracing::instrument(skip_all)]
+pub async fn get_local_user_settings_view_from_jwt(
+  jwt: &Sensitive<String>,
+  pool: &DbPool,
+  secret: &Secret,
+) -> Result<LocalUserSettingsView, LemmyError> {
+  let claims = Claims::decode(jwt.as_ref(), &secret.jwt_secret)
+    .map_err(|e| e.with_message("not_logged_in"))?
+    .claims;
+  let local_user_id = LocalUserId(claims.sub);
+  let local_user_view = blocking(pool, move |conn| {
+    LocalUserSettingsView::read(conn, local_user_id)
+  })
+  .await??;
+  // Check for a site ban
+  if local_user_view.person.is_banned() {
+    return Err(LemmyError::from_message("site_ban"));
+  }
+
+  check_validator_time(&local_user_view.local_user.validator_time, &claims)?;
+
+  Ok(local_user_view)
+}
+
+#[tracing::instrument(skip_all)]
+pub async fn get_local_user_settings_view_from_jwt_opt(
+  jwt: Option<&Sensitive<String>>,
+  pool: &DbPool,
+  secret: &Secret,
+) -> Result<Option<LocalUserSettingsView>, LemmyError> {
+  match jwt {
+    Some(jwt) => Ok(Some(
+      get_local_user_settings_view_from_jwt(jwt, pool, secret).await?,
+    )),
+    None => Ok(None),
+  }
+}
+
+#[tracing::instrument(skip_all)]
+pub async fn check_community_ban(
+  person_id: PersonId,
+  community_id: CommunityId,
+  pool: &DbPool,
+) -> Result<(), LemmyError> {
+  let is_banned =
+    move |conn: &'_ _| CommunityPersonBanView::get(conn, person_id, community_id).is_ok();
+  if blocking(pool, is_banned).await? {
+    Err(LemmyError::from_message("community_ban"))
+  } else {
+    Ok(())
+  }
+}
+
+#[tracing::instrument(skip_all)]
+pub async fn check_community_deleted_or_removed(
+  community_id: CommunityId,
+  pool: &DbPool,
+) -> Result<(), LemmyError> {
+  let community = blocking(pool, move |conn| Community::read(conn, community_id))
+    .await?
+    .map_err(|e| LemmyError::from_error_message(e, "couldnt_find_community"))?;
+  if community.deleted || community.removed {
+    Err(LemmyError::from_message("deleted"))
+  } else {
+    Ok(())
+  }
+}
+
+pub fn check_post_deleted_or_removed(post: &Post) -> Result<(), LemmyError> {
+  if post.deleted || post.removed {
+    Err(LemmyError::from_message("deleted"))
+  } else {
+    Ok(())
+  }
+}
+
+#[tracing::instrument(skip_all)]
+pub async fn check_person_block(
+  my_id: PersonId,
+  potential_blocker_id: PersonId,
+  pool: &DbPool,
+) -> Result<(), LemmyError> {
+  let is_blocked = move |conn: &'_ _| PersonBlock::read(conn, potential_blocker_id, my_id).is_ok();
+  if blocking(pool, is_blocked).await? {
+    Err(LemmyError::from_message("person_block"))
+  } else {
+    Ok(())
+  }
+}
+
+#[tracing::instrument(skip_all)]
+pub async fn check_downvotes_enabled(score: i16, pool: &DbPool) -> Result<(), LemmyError> {
+  if score == -1 {
+    let site = blocking(pool, Site::read_local_site).await??;
+    if !site.enable_downvotes {
+      return Err(LemmyError::from_message("downvotes_disabled"));
+    }
+  }
+  Ok(())
+}
+
+#[tracing::instrument(skip_all)]
+pub async fn check_private_instance(
+  local_user_view: &Option<LocalUserView>,
+  pool: &DbPool,
+) -> Result<(), LemmyError> {
+  if local_user_view.is_none() {
+    let site = blocking(pool, Site::read_local_site).await?;
+
+    // The site might not be set up yet
+    if let Ok(site) = site {
+      if site.private_instance {
+        return Err(LemmyError::from_message("instance_is_private"));
+      }
+    }
+  }
+  Ok(())
+}
+
+#[tracing::instrument(skip_all)]
+pub async fn build_federated_instances(
+  pool: &DbPool,
+  settings: &Settings,
+) -> Result<Option<FederatedInstances>, LemmyError> {
+  let federation_config = &settings.federation;
+  let hostname = &settings.hostname;
+  let federation = federation_config.to_owned();
+  if federation.enabled {
+    let distinct_communities = blocking(pool, move |conn| {
+      Community::distinct_federated_communities(conn)
+    })
+    .await??;
+
+    let allowed = federation.allowed_instances;
+    let blocked = federation.blocked_instances;
+
+    let mut linked = distinct_communities
+      .iter()
+      .map(|actor_id| Ok(actor_id.host_str().unwrap_or("").to_string()))
+      .collect::<Result<Vec<String>, LemmyError>>()?;
+
+    if let Some(allowed) = allowed.as_ref() {
+      linked.extend_from_slice(allowed);
+    }
+
+    if let Some(blocked) = blocked.as_ref() {
+      linked.retain(|a| !blocked.contains(a) && !a.eq(hostname));
+    }
+
+    // Sort and remove dupes
+    linked.sort_unstable();
+    linked.dedup();
+
+    Ok(Some(FederatedInstances {
+      linked,
+      allowed,
+      blocked,
+    }))
+  } else {
+    Ok(None)
+  }
+}
+
+/// Checks the password length
+pub fn password_length_check(pass: &str) -> Result<(), LemmyError> {
+  if !(10..=60).contains(&pass.len()) {
+    Err(LemmyError::from_message("invalid_password"))
+  } else {
+    Ok(())
+  }
+}
+
+/// Checks the site description length
+pub fn site_description_length_check(description: &str) -> Result<(), LemmyError> {
+  if description.len() > 150 {
+    Err(LemmyError::from_message("site_description_length_overflow"))
+  } else {
+    Ok(())
+  }
+}
+
+/// Checks for a honeypot. If this field is filled, fail the rest of the function
+pub fn honeypot_check(honeypot: &Option<String>) -> Result<(), LemmyError> {
+  if honeypot.is_some() {
+    Err(LemmyError::from_message("honeypot_fail"))
+  } else {
+    Ok(())
+  }
+}
+
+pub fn send_email_to_user(
+  local_user_view: &LocalUserView,
+  subject: &str,
+  body: &str,
+  settings: &Settings,
+) {
+  if local_user_view.person.banned || !local_user_view.local_user.send_notifications_to_email {
+    return;
+  }
+
+  if let Some(user_email) = &local_user_view.local_user.email {
+    match send_email(
+      subject,
+      user_email,
+      &local_user_view.person.name,
+      body,
+      settings,
+    ) {
+      Ok(_o) => _o,
+      Err(e) => warn!("{}", e),
+    };
+  }
+}
+
+pub async fn send_password_reset_email(
+  user: &LocalUserView,
+  pool: &DbPool,
+  settings: &Settings,
+) -> Result<(), LemmyError> {
+  // Generate a random token
+  let token = generate_random_string();
+
+  // Insert the row
+  let token2 = token.clone();
+  let local_user_id = user.local_user.id;
+  blocking(pool, move |conn| {
+    PasswordResetRequest::create_token(conn, local_user_id, &token2)
+  })
+  .await??;
+
+  let email = &user.local_user.email.to_owned().expect("email");
+  let lang = get_user_lang(user);
+  let subject = &lang.password_reset_subject(&user.person.name);
+  let protocol_and_hostname = settings.get_protocol_and_hostname();
+  let reset_link = format!("{}/password_change/{}", protocol_and_hostname, &token);
+  let body = &lang.password_reset_body(&user.person.name, reset_link);
+  send_email(subject, email, &user.person.name, body, settings)
+}
+
+/// Send a verification email
+pub async fn send_verification_email(
+  user: &LocalUserView,
+  new_email: &str,
+  pool: &DbPool,
+  settings: &Settings,
+) -> Result<(), LemmyError> {
+  let form = EmailVerificationForm {
+    local_user_id: user.local_user.id,
+    email: new_email.to_string(),
+    verification_token: generate_random_string(),
+  };
+  let verify_link = format!(
+    "{}/verify_email/{}",
+    settings.get_protocol_and_hostname(),
+    &form.verification_token
+  );
+  blocking(pool, move |conn| EmailVerification::create(conn, &form)).await??;
+
+  let lang = get_user_lang(user);
+  let subject = lang.verify_email_subject(&settings.hostname);
+  let body = lang.verify_email_body(&user.person.name, &settings.hostname, verify_link);
+  send_email(&subject, new_email, &user.person.name, &body, settings)?;
+
+  Ok(())
+}
+
+pub fn send_email_verification_success(
+  user: &LocalUserView,
+  settings: &Settings,
+) -> Result<(), LemmyError> {
+  let email = &user.local_user.email.to_owned().expect("email");
+  let lang = get_user_lang(user);
+  let subject = &lang.email_verified_subject(&user.person.actor_id);
+  let body = &lang.email_verified_body();
+  send_email(subject, email, &user.person.name, body, settings)
+}
+
+pub fn get_user_lang(user: &LocalUserView) -> Lang {
+  let user_lang = LanguageId::new(user.local_user.lang.clone());
+  Lang::from_language_id(&user_lang).unwrap_or_else(|| {
+    let en = LanguageId::new("en");
+    Lang::from_language_id(&en).expect("default language")
+  })
+}
+
+pub fn send_application_approved_email(
+  user: &LocalUserView,
+  settings: &Settings,
+) -> Result<(), LemmyError> {
+  let email = &user.local_user.email.to_owned().expect("email");
+  let lang = get_user_lang(user);
+  let subject = lang.registration_approved_subject(&user.person.actor_id);
+  let body = lang.registration_approved_body(&settings.hostname);
+  send_email(&subject, email, &user.person.name, &body, settings)
+}
+
+pub async fn check_registration_application(
+  site: &Site,
+  local_user_view: &LocalUserView,
+  pool: &DbPool,
+) -> Result<(), LemmyError> {
+  if site.require_application
+    && !local_user_view.local_user.accepted_application
+    && !local_user_view.person.admin
+  {
+    // Fetch the registration, see if its denied
+    let local_user_id = local_user_view.local_user.id;
+    let registration = blocking(pool, move |conn| {
+      RegistrationApplication::find_by_local_user_id(conn, local_user_id)
+    })
+    .await??;
+    if let Some(deny_reason) = registration.deny_reason {
+      let lang = get_user_lang(local_user_view);
+      let registration_denied_message = format!("{}: {}", lang.registration_denied(), &deny_reason);
+      return Err(LemmyError::from_message(&registration_denied_message));
+    } else {
+      return Err(LemmyError::from_message("registration_application_pending"));
+    }
+  }
+  Ok(())
+}
+
+/// TODO this check should be removed after https://github.com/LemmyNet/lemmy/issues/868 is done.
+pub async fn check_private_instance_and_federation_enabled(
+  pool: &DbPool,
+  settings: &Settings,
+) -> Result<(), LemmyError> {
+  let site_opt = blocking(pool, Site::read_local_site).await?;
+
+  if let Ok(site) = site_opt {
+    if site.private_instance && settings.federation.enabled {
+      return Err(LemmyError::from_message(
+        "Cannot have both private instance and federation enabled.",
+      ));
+    }
+  }
+  Ok(())
+}
+
+pub async fn remove_user_data(banned_person_id: PersonId, pool: &DbPool) -> Result<(), LemmyError> {
+  // Posts
+  blocking(pool, move |conn: &'_ _| {
+    Post::update_removed_for_creator(conn, banned_person_id, None, true)
+  })
+  .await??;
+
+  // Communities
+  // Remove all communities where they're the top mod
+  // for now, remove the communities manually
+  let first_mod_communities = blocking(pool, move |conn: &'_ _| {
+    CommunityModeratorView::get_community_first_mods(conn)
+  })
+  .await??;
+
+  // Filter to only this banned users top communities
+  let banned_user_first_communities: Vec<CommunityModeratorView> = first_mod_communities
+    .into_iter()
+    .filter(|fmc| fmc.moderator.id == banned_person_id)
+    .collect();
+
+  for first_mod_community in banned_user_first_communities {
+    blocking(pool, move |conn: &'_ _| {
+      Community::update_removed(conn, first_mod_community.community.id, true)
+    })
+    .await??;
+  }
+
+  // Comments
+  blocking(pool, move |conn: &'_ _| {
+    Comment::update_removed_for_creator(conn, banned_person_id, true)
+  })
+  .await??;
+
+  Ok(())
+}
+
+pub async fn remove_user_data_in_community(
+  community_id: CommunityId,
+  banned_person_id: PersonId,
+  pool: &DbPool,
+) -> Result<(), LemmyError> {
+  // Posts
+  blocking(pool, move |conn| {
+    Post::update_removed_for_creator(conn, banned_person_id, Some(community_id), true)
+  })
+  .await??;
+
+  // Comments
+  // TODO Diesel doesn't allow updates with joins, so this has to be a loop
+  let comments = blocking(pool, move |conn| {
+    CommentQueryBuilder::create(conn)
+      .creator_id(banned_person_id)
+      .community_id(community_id)
+      .limit(std::i64::MAX)
+      .list()
+  })
+  .await??;
+
+  for comment_view in &comments {
+    let comment_id = comment_view.comment.id;
+    blocking(pool, move |conn| {
+      Comment::update_removed(conn, comment_id, true)
+    })
+    .await??;
+  }
+
+  Ok(())
+}
+
+pub async fn delete_user_account(person_id: PersonId, pool: &DbPool) -> Result<(), LemmyError> {
+  // Comments
+  let permadelete = move |conn: &'_ _| Comment::permadelete_for_creator(conn, person_id);
+  blocking(pool, permadelete)
+    .await?
+    .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?;
+
+  // Posts
+  let permadelete = move |conn: &'_ _| Post::permadelete_for_creator(conn, person_id);
+  blocking(pool, permadelete)
+    .await?
+    .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_post"))?;
+
+  blocking(pool, move |conn| Person::delete_account(conn, person_id)).await??;
+
+  Ok(())
+}
+
+pub fn check_image_has_local_domain(url: &Option<DbUrl>) -> Result<(), LemmyError> {
+  if let Some(url) = url {
+    let settings = Settings::get();
+    let domain = url.domain().expect("url has domain");
+    if domain != settings.hostname {
+      return Err(LemmyError::from_message("image_not_local"));
+    }
+  }
+  Ok(())
+}
diff --git a/crates/api_common/src/websocket.rs b/crates/api_common/src/websocket.rs
index 06665512..80729fcc 100644
--- a/crates/api_common/src/websocket.rs
+++ b/crates/api_common/src/websocket.rs
@@ -1,5 +1,5 @@
+use crate::sensitive::Sensitive;
 use lemmy_db_schema::newtypes::{CommunityId, PostId};
-use lemmy_utils::Sensitive;
 use serde::{Deserialize, Serialize};
 
 #[derive(Serialize, Deserialize, Debug)]
diff --git a/crates/api_crud/Cargo.toml b/crates/api_crud/Cargo.toml
index 523da0ac..f808ea63 100644
--- a/crates/api_crud/Cargo.toml
+++ b/crates/api_crud/Cargo.toml
@@ -11,10 +11,10 @@ documentation = "https://join-lemmy.org/docs/en/index.html"
 lemmy_apub = { version = "=0.16.3", path = "../apub" }
 lemmy_apub_lib = { version = "=0.16.3", path = "../apub_lib" }
 lemmy_utils = { version = "=0.16.3", path = "../utils" }
-lemmy_db_schema = { version = "=0.16.3", path = "../db_schema" }
-lemmy_db_views = { version = "=0.16.3", path = "../db_views" }
-lemmy_db_views_actor = { version = "=0.16.3", path = "../db_views_actor" }
-lemmy_api_common = { version = "=0.16.3", path = "../api_common" }
+lemmy_db_schema = { version = "=0.16.3", path = "../db_schema", features = ["full"] }
+lemmy_db_views = { version = "=0.16.3", path = "../db_views", features = ["full"] }
+lemmy_db_views_actor = { version = "=0.16.3", path = "../db_views_actor", features = ["full"] }
+lemmy_api_common = { version = "=0.16.3", path = "../api_common", features = ["full"] }
 lemmy_websocket = { version = "=0.16.3", path = "../websocket" }
 bcrypt = "0.12.1"
 serde_json = { version = "1.0.79", features = ["preserve_order"] }
diff --git a/crates/api_crud/src/comment/create.rs b/crates/api_crud/src/comment/create.rs
index ac374466..12e208e8 100644
--- a/crates/api_crud/src/comment/create.rs
+++ b/crates/api_crud/src/comment/create.rs
@@ -1,13 +1,15 @@
 use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  check_community_ban,
-  check_community_deleted_or_removed,
-  check_post_deleted_or_removed,
-  comment::*,
-  get_local_user_view_from_jwt,
-  get_post,
+  comment::{CommentResponse, CreateComment},
+  utils::{
+    blocking,
+    check_community_ban,
+    check_community_deleted_or_removed,
+    check_post_deleted_or_removed,
+    get_local_user_view_from_jwt,
+    get_post,
+  },
 };
 use lemmy_apub::{
   generate_local_apub_endpoint,
@@ -22,7 +24,7 @@ use lemmy_db_schema::{
   },
   traits::{Crud, Likeable},
 };
-use lemmy_db_views::comment_view::CommentView;
+use lemmy_db_views::structs::CommentView;
 use lemmy_utils::{
   utils::{remove_slurs, scrape_text_for_mentions},
   ConnectionId,
diff --git a/crates/api_crud/src/comment/delete.rs b/crates/api_crud/src/comment/delete.rs
index 9ed58946..f4fb5d40 100644
--- a/crates/api_crud/src/comment/delete.rs
+++ b/crates/api_crud/src/comment/delete.rs
@@ -1,23 +1,15 @@
 use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  check_community_ban,
-  comment::*,
-  get_local_user_view_from_jwt,
-  is_mod_or_admin,
+  comment::{CommentResponse, DeleteComment},
+  utils::{blocking, check_community_ban, get_local_user_view_from_jwt},
 };
 use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects};
 use lemmy_db_schema::{
-  source::{
-    comment::Comment,
-    community::Community,
-    moderator::{ModRemoveComment, ModRemoveCommentForm},
-    post::Post,
-  },
+  source::{comment::Comment, community::Community, post::Post},
   traits::Crud,
 };
-use lemmy_db_views::comment_view::CommentView;
+use lemmy_db_views::structs::CommentView;
 use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::{
   send::{send_comment_ws_message, send_local_notifs},
@@ -112,101 +104,3 @@ impl PerformCrud for DeleteComment {
     Ok(res)
   }
 }
-
-#[async_trait::async_trait(?Send)]
-impl PerformCrud for RemoveComment {
-  type Response = CommentResponse;
-
-  #[tracing::instrument(skip(context, websocket_id))]
-  async fn perform(
-    &self,
-    context: &Data<LemmyContext>,
-    websocket_id: Option<ConnectionId>,
-  ) -> Result<CommentResponse, LemmyError> {
-    let data: &RemoveComment = self;
-    let local_user_view =
-      get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
-    let comment_id = data.comment_id;
-    let orig_comment = blocking(context.pool(), move |conn| {
-      CommentView::read(conn, comment_id, None)
-    })
-    .await??;
-
-    check_community_ban(
-      local_user_view.person.id,
-      orig_comment.community.id,
-      context.pool(),
-    )
-    .await?;
-
-    // Verify that only a mod or admin can remove
-    is_mod_or_admin(
-      context.pool(),
-      local_user_view.person.id,
-      orig_comment.community.id,
-    )
-    .await?;
-
-    // Do the remove
-    let removed = data.removed;
-    let updated_comment = blocking(context.pool(), move |conn| {
-      Comment::update_removed(conn, comment_id, removed)
-    })
-    .await?
-    .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?;
-
-    // Mod tables
-    let form = ModRemoveCommentForm {
-      mod_person_id: local_user_view.person.id,
-      comment_id: data.comment_id,
-      removed: Some(removed),
-      reason: data.reason.to_owned(),
-    };
-    blocking(context.pool(), move |conn| {
-      ModRemoveComment::create(conn, &form)
-    })
-    .await??;
-
-    let post_id = updated_comment.post_id;
-    let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
-    let recipient_ids = send_local_notifs(
-      vec![],
-      &updated_comment,
-      &local_user_view.person.clone(),
-      &post,
-      false,
-      context,
-    )
-    .await?;
-
-    let res = send_comment_ws_message(
-      data.comment_id,
-      UserOperationCrud::RemoveComment,
-      websocket_id,
-      None, // TODO maybe this might clear other forms
-      Some(local_user_view.person.id),
-      recipient_ids,
-      context,
-    )
-    .await?;
-
-    // Send the apub message
-    let community = blocking(context.pool(), move |conn| {
-      Community::read(conn, orig_comment.post.community_id)
-    })
-    .await??;
-    let deletable = DeletableObjects::Comment(Box::new(updated_comment.clone().into()));
-    send_apub_delete_in_community(
-      local_user_view.person,
-      community,
-      deletable,
-      data.reason.clone().or_else(|| Some("".to_string())),
-      removed,
-      context,
-    )
-    .await?;
-
-    Ok(res)
-  }
-}
diff --git a/crates/api_crud/src/comment/list.rs b/crates/api_crud/src/comment/list.rs
index cd91a3b0..ab9e25f1 100644
--- a/crates/api_crud/src/comment/list.rs
+++ b/crates/api_crud/src/comment/list.rs
@@ -1,18 +1,14 @@
 use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  check_private_instance,
-  comment::*,
-  get_local_user_view_from_jwt_opt,
+  comment::{GetComments, GetCommentsResponse},
+  utils::{blocking, check_private_instance, get_local_user_view_from_jwt_opt},
 };
 use lemmy_apub::{fetcher::resolve_actor_identifier, objects::community::ApubCommunity};
 use lemmy_db_schema::{
-  from_opt_str_to_opt_enum,
   source::community::Community,
   traits::DeleteableOrRemoveable,
-  ListingType,
-  SortType,
+  utils::{from_opt_str_to_opt_enum, ListingType, SortType},
 };
 use lemmy_db_views::comment_view::CommentQueryBuilder;
 use lemmy_utils::{ConnectionId, LemmyError};
diff --git a/crates/api_crud/src/comment/mod.rs b/crates/api_crud/src/comment/mod.rs
index 7003bdd8..1e832803 100644
--- a/crates/api_crud/src/comment/mod.rs
+++ b/crates/api_crud/src/comment/mod.rs
@@ -2,4 +2,5 @@ mod create;
 mod delete;
 mod list;
 mod read;
+mod remove;
 mod update;
diff --git a/crates/api_crud/src/comment/read.rs b/crates/api_crud/src/comment/read.rs
index 5b2155da..fd248caf 100644
--- a/crates/api_crud/src/comment/read.rs
+++ b/crates/api_crud/src/comment/read.rs
@@ -1,12 +1,10 @@
 use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  check_private_instance,
-  comment::*,
-  get_local_user_view_from_jwt_opt,
+  comment::{CommentResponse, GetComment},
+  utils::{blocking, check_private_instance, get_local_user_view_from_jwt_opt},
 };
-use lemmy_db_views::comment_view::CommentView;
+use lemmy_db_views::structs::CommentView;
 use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::LemmyContext;
 
diff --git a/crates/api_crud/src/comment/remove.rs b/crates/api_crud/src/comment/remove.rs
new file mode 100644
index 00000000..837407c5
--- /dev/null
+++ b/crates/api_crud/src/comment/remove.rs
@@ -0,0 +1,121 @@
+use crate::PerformCrud;
+use actix_web::web::Data;
+use lemmy_api_common::{
+  comment::{CommentResponse, RemoveComment},
+  utils::{blocking, check_community_ban, get_local_user_view_from_jwt, is_mod_or_admin},
+};
+use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects};
+use lemmy_db_schema::{
+  source::{
+    comment::Comment,
+    community::Community,
+    moderator::{ModRemoveComment, ModRemoveCommentForm},
+    post::Post,
+  },
+  traits::Crud,
+};
+use lemmy_db_views::structs::CommentView;
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::{
+  send::{send_comment_ws_message, send_local_notifs},
+  LemmyContext,
+  UserOperationCrud,
+};
+
+#[async_trait::async_trait(?Send)]
+impl PerformCrud for RemoveComment {
+  type Response = CommentResponse;
+
+  #[tracing::instrument(skip(context, websocket_id))]
+  async fn perform(
+    &self,
+    context: &Data<LemmyContext>,
+    websocket_id: Option<ConnectionId>,
+  ) -> Result<CommentResponse, LemmyError> {
+    let data: &RemoveComment = self;
+    let local_user_view =
+      get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+    let comment_id = data.comment_id;
+    let orig_comment = blocking(context.pool(), move |conn| {
+      CommentView::read(conn, comment_id, None)
+    })
+    .await??;
+
+    check_community_ban(
+      local_user_view.person.id,
+      orig_comment.community.id,
+      context.pool(),
+    )
+    .await?;
+
+    // Verify that only a mod or admin can remove
+    is_mod_or_admin(
+      context.pool(),
+      local_user_view.person.id,
+      orig_comment.community.id,
+    )
+    .await?;
+
+    // Do the remove
+    let removed = data.removed;
+    let updated_comment = blocking(context.pool(), move |conn| {
+      Comment::update_removed(conn, comment_id, removed)
+    })
+    .await?
+    .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?;
+
+    // Mod tables
+    let form = ModRemoveCommentForm {
+      mod_person_id: local_user_view.person.id,
+      comment_id: data.comment_id,
+      removed: Some(removed),
+      reason: data.reason.to_owned(),
+    };
+    blocking(context.pool(), move |conn| {
+      ModRemoveComment::create(conn, &form)
+    })
+    .await??;
+
+    let post_id = updated_comment.post_id;
+    let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
+    let recipient_ids = send_local_notifs(
+      vec![],
+      &updated_comment,
+      &local_user_view.person.clone(),
+      &post,
+      false,
+      context,
+    )
+    .await?;
+
+    let res = send_comment_ws_message(
+      data.comment_id,
+      UserOperationCrud::RemoveComment,
+      websocket_id,
+      None, // TODO maybe this might clear other forms
+      Some(local_user_view.person.id),
+      recipient_ids,
+      context,
+    )
+    .await?;
+
+    // Send the apub message
+    let community = blocking(context.pool(), move |conn| {
+      Community::read(conn, orig_comment.post.community_id)
+    })
+    .await??;
+    let deletable = DeletableObjects::Comment(Box::new(updated_comment.clone().into()));
+    send_apub_delete_in_community(
+      local_user_view.person,
+      community,
+      deletable,
+      data.reason.clone().or_else(|| Some("".to_string())),
+      removed,
+      context,
+    )
+    .await?;
+
+    Ok(res)
+  }
+}
diff --git a/crates/api_crud/src/comment/update.rs b/crates/api_crud/src/comment/update.rs
index 4fede145..06065c89 100644
--- a/crates/api_crud/src/comment/update.rs
+++ b/crates/api_crud/src/comment/update.rs
@@ -1,19 +1,20 @@
 use actix_web::web::Data;
-
 use lemmy_api_common::{
-  blocking,
-  check_community_ban,
-  check_community_deleted_or_removed,
-  check_post_deleted_or_removed,
-  comment::*,
-  get_local_user_view_from_jwt,
+  comment::{CommentResponse, EditComment},
+  utils::{
+    blocking,
+    check_community_ban,
+    check_community_deleted_or_removed,
+    check_post_deleted_or_removed,
+    get_local_user_view_from_jwt,
+  },
 };
 use lemmy_apub::protocol::activities::{
   create_or_update::comment::CreateOrUpdateComment,
   CreateOrUpdateType,
 };
 use lemmy_db_schema::source::comment::Comment;
-use lemmy_db_views::comment_view::CommentView;
+use lemmy_db_views::structs::CommentView;
 use lemmy_utils::{
   utils::{remove_slurs, scrape_text_for_mentions},
   ConnectionId,
diff --git a/crates/api_crud/src/community/create.rs b/crates/api_crud/src/community/create.rs
index 8d68d554..6838216f 100644
--- a/crates/api_crud/src/community/create.rs
+++ b/crates/api_crud/src/community/create.rs
@@ -1,11 +1,8 @@
 use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  check_image_has_local_domain,
   community::{CommunityResponse, CreateCommunity},
-  get_local_user_view_from_jwt,
-  is_admin,
+  utils::{blocking, check_image_has_local_domain, get_local_user_view_from_jwt, is_admin},
 };
 use lemmy_apub::{
   generate_followers_url,
@@ -17,7 +14,6 @@ use lemmy_apub::{
 };
 use lemmy_apub_lib::object_id::ObjectId;
 use lemmy_db_schema::{
-  diesel_option_overwrite_to_url,
   source::{
     community::{
       Community,
@@ -30,8 +26,9 @@ use lemmy_db_schema::{
     site::Site,
   },
   traits::{Crud, Followable, Joinable},
+  utils::diesel_option_overwrite_to_url,
 };
-use lemmy_db_views_actor::community_view::CommunityView;
+use lemmy_db_views_actor::structs::CommunityView;
 use lemmy_utils::{
   apub::generate_actor_keypair,
   utils::{check_slurs, check_slurs_opt, is_valid_actor_name},
diff --git a/crates/api_crud/src/community/delete.rs b/crates/api_crud/src/community/delete.rs
index b8a2f230..f42e9b01 100644
--- a/crates/api_crud/src/community/delete.rs
+++ b/crates/api_crud/src/community/delete.rs
@@ -1,16 +1,13 @@
 use crate::PerformCrud;
 use actix_web::web::Data;
-use lemmy_api_common::{blocking, community::*, get_local_user_view_from_jwt, is_admin};
-use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects};
-use lemmy_db_schema::{
-  source::{
-    community::Community,
-    moderator::{ModRemoveCommunity, ModRemoveCommunityForm},
-  },
-  traits::Crud,
+use lemmy_api_common::{
+  community::{CommunityResponse, DeleteCommunity},
+  utils::{blocking, get_local_user_view_from_jwt},
 };
-use lemmy_db_views_actor::community_moderator_view::CommunityModeratorView;
-use lemmy_utils::{utils::naive_from_unix, ConnectionId, LemmyError};
+use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects};
+use lemmy_db_schema::source::community::Community;
+use lemmy_db_views_actor::structs::CommunityModeratorView;
+use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud};
 
 #[async_trait::async_trait(?Send)]
@@ -72,67 +69,3 @@ impl PerformCrud for DeleteCommunity {
     Ok(res)
   }
 }
-
-#[async_trait::async_trait(?Send)]
-impl PerformCrud for RemoveCommunity {
-  type Response = CommunityResponse;
-
-  #[tracing::instrument(skip(context, websocket_id))]
-  async fn perform(
-    &self,
-    context: &Data<LemmyContext>,
-    websocket_id: Option<ConnectionId>,
-  ) -> Result<CommunityResponse, LemmyError> {
-    let data: &RemoveCommunity = self;
-    let local_user_view =
-      get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
-    // Verify its an admin (only an admin can remove a community)
-    is_admin(&local_user_view)?;
-
-    // Do the remove
-    let community_id = data.community_id;
-    let removed = data.removed;
-    let updated_community = blocking(context.pool(), move |conn| {
-      Community::update_removed(conn, community_id, removed)
-    })
-    .await?
-    .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_community"))?;
-
-    // Mod tables
-    let expires = data.expires.map(naive_from_unix);
-    let form = ModRemoveCommunityForm {
-      mod_person_id: local_user_view.person.id,
-      community_id: data.community_id,
-      removed: Some(removed),
-      reason: data.reason.to_owned(),
-      expires,
-    };
-    blocking(context.pool(), move |conn| {
-      ModRemoveCommunity::create(conn, &form)
-    })
-    .await??;
-
-    let res = send_community_ws_message(
-      data.community_id,
-      UserOperationCrud::RemoveCommunity,
-      websocket_id,
-      Some(local_user_view.person.id),
-      context,
-    )
-    .await?;
-
-    // Apub messages
-    let deletable = DeletableObjects::Community(Box::new(updated_community.clone().into()));
-    send_apub_delete_in_community(
-      local_user_view.person,
-      updated_community,
-      deletable,
-      data.reason.clone().or_else(|| Some("".to_string())),
-      removed,
-      context,
-    )
-    .await?;
-    Ok(res)
-  }
-}
diff --git a/crates/api_crud/src/community/list.rs b/crates/api_crud/src/community/list.rs
index 24c154ad..fac41c66 100644
--- a/crates/api_crud/src/community/list.rs
+++ b/crates/api_crud/src/community/list.rs
@@ -1,16 +1,12 @@
 use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  check_private_instance,
-  community::*,
-  get_local_user_view_from_jwt_opt,
+  community::{ListCommunities, ListCommunitiesResponse},
+  utils::{blocking, check_private_instance, get_local_user_view_from_jwt_opt},
 };
 use lemmy_db_schema::{
-  from_opt_str_to_opt_enum,
   traits::DeleteableOrRemoveable,
-  ListingType,
-  SortType,
+  utils::{from_opt_str_to_opt_enum, ListingType, SortType},
 };
 use lemmy_db_views_actor::community_view::CommunityQueryBuilder;
 use lemmy_utils::{ConnectionId, LemmyError};
diff --git a/crates/api_crud/src/community/mod.rs b/crates/api_crud/src/community/mod.rs
index 7003bdd8..1e832803 100644
--- a/crates/api_crud/src/community/mod.rs
+++ b/crates/api_crud/src/community/mod.rs
@@ -2,4 +2,5 @@ mod create;
 mod delete;
 mod list;
 mod read;
+mod remove;
 mod update;
diff --git a/crates/api_crud/src/community/read.rs b/crates/api_crud/src/community/read.rs
index 9c4ba333..16eee469 100644
--- a/crates/api_crud/src/community/read.rs
+++ b/crates/api_crud/src/community/read.rs
@@ -1,10 +1,8 @@
 use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  check_private_instance,
-  community::*,
-  get_local_user_view_from_jwt_opt,
+  community::{GetCommunity, GetCommunityResponse},
+  utils::{blocking, check_private_instance, get_local_user_view_from_jwt_opt},
 };
 use lemmy_apub::{
   fetcher::resolve_actor_identifier,
@@ -14,10 +12,7 @@ use lemmy_db_schema::{
   source::{community::Community, site::Site},
   traits::DeleteableOrRemoveable,
 };
-use lemmy_db_views_actor::{
-  community_moderator_view::CommunityModeratorView,
-  community_view::CommunityView,
-};
+use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView};
 use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::{messages::GetCommunityUsersOnline, LemmyContext};
 
diff --git a/crates/api_crud/src/community/remove.rs b/crates/api_crud/src/community/remove.rs
new file mode 100644
index 00000000..7ca9b73f
--- /dev/null
+++ b/crates/api_crud/src/community/remove.rs
@@ -0,0 +1,80 @@
+use crate::PerformCrud;
+use actix_web::web::Data;
+use lemmy_api_common::{
+  community::{CommunityResponse, RemoveCommunity},
+  utils::{blocking, get_local_user_view_from_jwt, is_admin},
+};
+use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects};
+use lemmy_db_schema::{
+  source::{
+    community::Community,
+    moderator::{ModRemoveCommunity, ModRemoveCommunityForm},
+  },
+  traits::Crud,
+};
+use lemmy_utils::{utils::naive_from_unix, ConnectionId, LemmyError};
+use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud};
+
+#[async_trait::async_trait(?Send)]
+impl PerformCrud for RemoveCommunity {
+  type Response = CommunityResponse;
+
+  #[tracing::instrument(skip(context, websocket_id))]
+  async fn perform(
+    &self,
+    context: &Data<LemmyContext>,
+    websocket_id: Option<ConnectionId>,
+  ) -> Result<CommunityResponse, LemmyError> {
+    let data: &RemoveCommunity = self;
+    let local_user_view =
+      get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+    // Verify its an admin (only an admin can remove a community)
+    is_admin(&local_user_view)?;
+
+    // Do the remove
+    let community_id = data.community_id;
+    let removed = data.removed;
+    let updated_community = blocking(context.pool(), move |conn| {
+      Community::update_removed(conn, community_id, removed)
+    })
+    .await?
+    .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_community"))?;
+
+    // Mod tables
+    let expires = data.expires.map(naive_from_unix);
+    let form = ModRemoveCommunityForm {
+      mod_person_id: local_user_view.person.id,
+      community_id: data.community_id,
+      removed: Some(removed),
+      reason: data.reason.to_owned(),
+      expires,
+    };
+    blocking(context.pool(), move |conn| {
+      ModRemoveCommunity::create(conn, &form)
+    })
+    .await??;
+
+    let res = send_community_ws_message(
+      data.community_id,
+      UserOperationCrud::RemoveCommunity,
+      websocket_id,
+      Some(local_user_view.person.id),
+      context,
+    )
+    .await?;
+
+    // Apub messages
+    let deletable = DeletableObjects::Community(Box::new(updated_community.clone().into()));
+    send_apub_delete_in_community(
+      local_user_view.person,
+      updated_community,
+      deletable,
+      data.reason.clone().or_else(|| Some("".to_string())),
+      removed,
+      context,
+    )
+    .await?;
+    Ok(res)
+  }
+}
diff --git a/crates/api_crud/src/community/update.rs b/crates/api_crud/src/community/update.rs
index 4df708fc..4c7fa8ce 100644
--- a/crates/api_crud/src/community/update.rs
+++ b/crates/api_crud/src/community/update.rs
@@ -1,20 +1,17 @@
 use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  check_image_has_local_domain,
   community::{CommunityResponse, EditCommunity},
-  get_local_user_view_from_jwt,
+  utils::{blocking, check_image_has_local_domain, get_local_user_view_from_jwt},
 };
 use lemmy_apub::protocol::activities::community::update::UpdateCommunity;
 use lemmy_db_schema::{
-  diesel_option_overwrite_to_url,
-  naive_now,
   newtypes::PersonId,
   source::community::{Community, CommunityForm},
   traits::Crud,
+  utils::{diesel_option_overwrite_to_url, naive_now},
 };
-use lemmy_db_views_actor::community_moderator_view::CommunityModeratorView;
+use lemmy_db_views_actor::structs::CommunityModeratorView;
 use lemmy_utils::{utils::check_slurs_opt, ConnectionId, LemmyError};
 use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud};
 
diff --git a/crates/api_crud/src/post/create.rs b/crates/api_crud/src/post/create.rs
index eb162e01..353e186a 100644
--- a/crates/api_crud/src/post/create.rs
+++ b/crates/api_crud/src/post/create.rs
@@ -1,13 +1,16 @@
 use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  check_community_ban,
-  check_community_deleted_or_removed,
-  get_local_user_view_from_jwt,
-  honeypot_check,
-  mark_post_as_read,
-  post::*,
+  post::{CreatePost, PostResponse},
+  request::fetch_site_data,
+  utils::{
+    blocking,
+    check_community_ban,
+    check_community_deleted_or_removed,
+    get_local_user_view_from_jwt,
+    honeypot_check,
+    mark_post_as_read,
+  },
 };
 use lemmy_apub::{
   generate_local_apub_endpoint,
@@ -22,9 +25,8 @@ use lemmy_db_schema::{
   },
   traits::{Crud, Likeable},
 };
-use lemmy_db_views_actor::community_view::CommunityView;
+use lemmy_db_views_actor::structs::CommunityView;
 use lemmy_utils::{
-  request::fetch_site_data,
   utils::{
     check_slurs,
     check_slurs_opt,
diff --git a/crates/api_crud/src/post/delete.rs b/crates/api_crud/src/post/delete.rs
index 05c395b5..f880b4b5 100644
--- a/crates/api_crud/src/post/delete.rs
+++ b/crates/api_crud/src/post/delete.rs
@@ -1,20 +1,17 @@
 use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  check_community_ban,
-  check_community_deleted_or_removed,
-  get_local_user_view_from_jwt,
-  is_mod_or_admin,
-  post::*,
+  post::{DeletePost, PostResponse},
+  utils::{
+    blocking,
+    check_community_ban,
+    check_community_deleted_or_removed,
+    get_local_user_view_from_jwt,
+  },
 };
 use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects};
 use lemmy_db_schema::{
-  source::{
-    community::Community,
-    moderator::{ModRemovePost, ModRemovePostForm},
-    post::Post,
-  },
+  source::{community::Community, post::Post},
   traits::Crud,
 };
 use lemmy_utils::{ConnectionId, LemmyError};
@@ -90,83 +87,3 @@ impl PerformCrud for DeletePost {
     Ok(res)
   }
 }
-
-#[async_trait::async_trait(?Send)]
-impl PerformCrud for RemovePost {
-  type Response = PostResponse;
-
-  #[tracing::instrument(skip(context, websocket_id))]
-  async fn perform(
-    &self,
-    context: &Data<LemmyContext>,
-    websocket_id: Option<ConnectionId>,
-  ) -> Result<PostResponse, LemmyError> {
-    let data: &RemovePost = self;
-    let local_user_view =
-      get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
-    let post_id = data.post_id;
-    let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
-
-    check_community_ban(
-      local_user_view.person.id,
-      orig_post.community_id,
-      context.pool(),
-    )
-    .await?;
-
-    // Verify that only the mods can remove
-    is_mod_or_admin(
-      context.pool(),
-      local_user_view.person.id,
-      orig_post.community_id,
-    )
-    .await?;
-
-    // Update the post
-    let post_id = data.post_id;
-    let removed = data.removed;
-    let updated_post = blocking(context.pool(), move |conn| {
-      Post::update_removed(conn, post_id, removed)
-    })
-    .await??;
-
-    // Mod tables
-    let form = ModRemovePostForm {
-      mod_person_id: local_user_view.person.id,
-      post_id: data.post_id,
-      removed: Some(removed),
-      reason: data.reason.to_owned(),
-    };
-    blocking(context.pool(), move |conn| {
-      ModRemovePost::create(conn, &form)
-    })
-    .await??;
-
-    let res = send_post_ws_message(
-      data.post_id,
-      UserOperationCrud::RemovePost,
-      websocket_id,
-      Some(local_user_view.person.id),
-      context,
-    )
-    .await?;
-
-    // apub updates
-    let community = blocking(context.pool(), move |conn| {
-      Community::read(conn, orig_post.community_id)
-    })
-    .await??;
-    let deletable = DeletableObjects::Post(Box::new(updated_post.into()));
-    send_apub_delete_in_community(
-      local_user_view.person,
-      community,
-      deletable,
-      data.reason.clone().or_else(|| Some("".to_string())),
-      removed,
-      context,
-    )
-    .await?;
-    Ok(res)
-  }
-}
diff --git a/crates/api_crud/src/post/list.rs b/crates/api_crud/src/post/list.rs
index 154faaaa..4b871e54 100644
--- a/crates/api_crud/src/post/list.rs
+++ b/crates/api_crud/src/post/list.rs
@@ -1,18 +1,14 @@
 use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  check_private_instance,
-  get_local_user_view_from_jwt_opt,
   post::{GetPosts, GetPostsResponse},
+  utils::{blocking, check_private_instance, get_local_user_view_from_jwt_opt},
 };
 use lemmy_apub::{fetcher::resolve_actor_identifier, objects::community::ApubCommunity};
 use lemmy_db_schema::{
-  from_opt_str_to_opt_enum,
   source::{community::Community, site::Site},
   traits::DeleteableOrRemoveable,
-  ListingType,
-  SortType,
+  utils::{from_opt_str_to_opt_enum, ListingType, SortType},
 };
 use lemmy_db_views::post_view::PostQueryBuilder;
 use lemmy_utils::{ConnectionId, LemmyError};
diff --git a/crates/api_crud/src/post/mod.rs b/crates/api_crud/src/post/mod.rs
index 7003bdd8..1e832803 100644
--- a/crates/api_crud/src/post/mod.rs
+++ b/crates/api_crud/src/post/mod.rs
@@ -2,4 +2,5 @@ mod create;
 mod delete;
 mod list;
 mod read;
+mod remove;
 mod update;
diff --git a/crates/api_crud/src/post/read.rs b/crates/api_crud/src/post/read.rs
index 03507ba4..a5ca262f 100644
--- a/crates/api_crud/src/post/read.rs
+++ b/crates/api_crud/src/post/read.rs
@@ -1,18 +1,12 @@
 use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  check_private_instance,
-  get_local_user_view_from_jwt_opt,
-  mark_post_as_read,
   post::{GetPost, GetPostResponse},
+  utils::{blocking, check_private_instance, get_local_user_view_from_jwt_opt, mark_post_as_read},
 };
 use lemmy_db_schema::traits::DeleteableOrRemoveable;
-use lemmy_db_views::{comment_view::CommentQueryBuilder, post_view::PostView};
-use lemmy_db_views_actor::{
-  community_moderator_view::CommunityModeratorView,
-  community_view::CommunityView,
-};
+use lemmy_db_views::{comment_view::CommentQueryBuilder, structs::PostView};
+use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView};
 use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::{messages::GetPostUsersOnline, LemmyContext};
 
diff --git a/crates/api_crud/src/post/remove.rs b/crates/api_crud/src/post/remove.rs
new file mode 100644
index 00000000..7e50159c
--- /dev/null
+++ b/crates/api_crud/src/post/remove.rs
@@ -0,0 +1,97 @@
+use crate::PerformCrud;
+use actix_web::web::Data;
+use lemmy_api_common::{
+  post::{PostResponse, RemovePost},
+  utils::{blocking, check_community_ban, get_local_user_view_from_jwt, is_mod_or_admin},
+};
+use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects};
+use lemmy_db_schema::{
+  source::{
+    community::Community,
+    moderator::{ModRemovePost, ModRemovePostForm},
+    post::Post,
+  },
+  traits::Crud,
+};
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCrud};
+
+#[async_trait::async_trait(?Send)]
+impl PerformCrud for RemovePost {
+  type Response = PostResponse;
+
+  #[tracing::instrument(skip(context, websocket_id))]
+  async fn perform(
+    &self,
+    context: &Data<LemmyContext>,
+    websocket_id: Option<ConnectionId>,
+  ) -> Result<PostResponse, LemmyError> {
+    let data: &RemovePost = self;
+    let local_user_view =
+      get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+    let post_id = data.post_id;
+    let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
+
+    check_community_ban(
+      local_user_view.person.id,
+      orig_post.community_id,
+      context.pool(),
+    )
+    .await?;
+
+    // Verify that only the mods can remove
+    is_mod_or_admin(
+      context.pool(),
+      local_user_view.person.id,
+      orig_post.community_id,
+    )
+    .await?;
+
+    // Update the post
+    let post_id = data.post_id;
+    let removed = data.removed;
+    let updated_post = blocking(context.pool(), move |conn| {
+      Post::update_removed(conn, post_id, removed)
+    })
+    .await??;
+
+    // Mod tables
+    let form = ModRemovePostForm {
+      mod_person_id: local_user_view.person.id,
+      post_id: data.post_id,
+      removed: Some(removed),
+      reason: data.reason.to_owned(),
+    };
+    blocking(context.pool(), move |conn| {
+      ModRemovePost::create(conn, &form)
+    })
+    .await??;
+
+    let res = send_post_ws_message(
+      data.post_id,
+      UserOperationCrud::RemovePost,
+      websocket_id,
+      Some(local_user_view.person.id),
+      context,
+    )
+    .await?;
+
+    // apub updates
+    let community = blocking(context.pool(), move |conn| {
+      Community::read(conn, orig_post.community_id)
+    })
+    .await??;
+    let deletable = DeletableObjects::Post(Box::new(updated_post.into()));
+    send_apub_delete_in_community(
+      local_user_view.person,
+      community,
+      deletable,
+      data.reason.clone().or_else(|| Some("".to_string())),
+      removed,
+      context,
+    )
+    .await?;
+    Ok(res)
+  }
+}
diff --git a/crates/api_crud/src/post/update.rs b/crates/api_crud/src/post/update.rs
index fa19c2c9..16f2fea4 100644
--- a/crates/api_crud/src/post/update.rs
+++ b/crates/api_crud/src/post/update.rs
@@ -1,23 +1,24 @@
 use actix_web::web::Data;
-
 use lemmy_api_common::{
-  blocking,
-  check_community_ban,
-  check_community_deleted_or_removed,
-  get_local_user_view_from_jwt,
-  post::*,
+  post::{EditPost, PostResponse},
+  request::fetch_site_data,
+  utils::{
+    blocking,
+    check_community_ban,
+    check_community_deleted_or_removed,
+    get_local_user_view_from_jwt,
+  },
 };
 use lemmy_apub::protocol::activities::{
   create_or_update::post::CreateOrUpdatePost,
   CreateOrUpdateType,
 };
 use lemmy_db_schema::{
-  naive_now,
   source::post::{Post, PostForm},
   traits::Crud,
+  utils::naive_now,
 };
 use lemmy_utils::{
-  request::fetch_site_data,
   utils::{check_slurs_opt, clean_optional_text, clean_url_params, is_valid_post_title},
   ConnectionId,
   LemmyError,
diff --git a/crates/api_crud/src/private_message/create.rs b/crates/api_crud/src/private_message/create.rs
index 44999cf0..de711128 100644
--- a/crates/api_crud/src/private_message/create.rs
+++ b/crates/api_crud/src/private_message/create.rs
@@ -1,12 +1,14 @@
 use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  check_person_block,
-  get_local_user_view_from_jwt,
-  get_user_lang,
   person::{CreatePrivateMessage, PrivateMessageResponse},
-  send_email_to_user,
+  utils::{
+    blocking,
+    check_person_block,
+    get_local_user_view_from_jwt,
+    get_user_lang,
+    send_email_to_user,
+  },
 };
 use lemmy_apub::{
   generate_local_apub_endpoint,
@@ -20,7 +22,7 @@ use lemmy_db_schema::{
   source::private_message::{PrivateMessage, PrivateMessageForm},
   traits::Crud,
 };
-use lemmy_db_views::local_user_view::LocalUserView;
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_utils::{utils::remove_slurs, ConnectionId, LemmyError};
 use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
 
diff --git a/crates/api_crud/src/private_message/delete.rs b/crates/api_crud/src/private_message/delete.rs
index 00b3ff50..af06094e 100644
--- a/crates/api_crud/src/private_message/delete.rs
+++ b/crates/api_crud/src/private_message/delete.rs
@@ -1,9 +1,8 @@
 use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  get_local_user_view_from_jwt,
   person::{DeletePrivateMessage, PrivateMessageResponse},
+  utils::{blocking, get_local_user_view_from_jwt},
 };
 use lemmy_apub::activities::deletion::send_apub_delete_private_message;
 use lemmy_db_schema::{source::private_message::PrivateMessage, traits::Crud};
diff --git a/crates/api_crud/src/private_message/read.rs b/crates/api_crud/src/private_message/read.rs
index 48ff239c..ce03200c 100644
--- a/crates/api_crud/src/private_message/read.rs
+++ b/crates/api_crud/src/private_message/read.rs
@@ -1,9 +1,8 @@
 use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  get_local_user_view_from_jwt,
   person::{GetPrivateMessages, PrivateMessagesResponse},
+  utils::{blocking, get_local_user_view_from_jwt},
 };
 use lemmy_db_schema::traits::DeleteableOrRemoveable;
 use lemmy_db_views::private_message_view::PrivateMessageQueryBuilder;
diff --git a/crates/api_crud/src/private_message/update.rs b/crates/api_crud/src/private_message/update.rs
index 5f23f1bc..aebd5b8b 100644
--- a/crates/api_crud/src/private_message/update.rs
+++ b/crates/api_crud/src/private_message/update.rs
@@ -1,9 +1,8 @@
 use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  get_local_user_view_from_jwt,
   person::{EditPrivateMessage, PrivateMessageResponse},
+  utils::{blocking, get_local_user_view_from_jwt},
 };
 use lemmy_apub::protocol::activities::{
   create_or_update::private_message::CreateOrUpdatePrivateMessage,
diff --git a/crates/api_crud/src/site/create.rs b/crates/api_crud/src/site/create.rs
index 7ec772b9..b9b3b601 100644
--- a/crates/api_crud/src/site/create.rs
+++ b/crates/api_crud/src/site/create.rs
@@ -1,23 +1,23 @@
 use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  check_image_has_local_domain,
-  get_local_user_view_from_jwt,
-  is_admin,
-  site::*,
-  site_description_length_check,
+  site::{CreateSite, SiteResponse},
+  utils::{
+    blocking,
+    check_image_has_local_domain,
+    get_local_user_view_from_jwt,
+    is_admin,
+    site_description_length_check,
+  },
 };
 use lemmy_apub::generate_site_inbox_url;
 use lemmy_db_schema::{
-  diesel_option_overwrite,
-  diesel_option_overwrite_to_url,
-  naive_now,
   newtypes::DbUrl,
   source::site::{Site, SiteForm},
   traits::Crud,
+  utils::{diesel_option_overwrite, diesel_option_overwrite_to_url, naive_now},
 };
-use lemmy_db_views::site_view::SiteView;
+use lemmy_db_views::structs::SiteView;
 use lemmy_utils::{
   apub::generate_actor_keypair,
   settings::structs::Settings,
diff --git a/crates/api_crud/src/site/read.rs b/crates/api_crud/src/site/read.rs
index 17c83896..f9fe75c7 100644
--- a/crates/api_crud/src/site/read.rs
+++ b/crates/api_crud/src/site/read.rs
@@ -1,19 +1,17 @@
 use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  build_federated_instances,
-  get_local_user_settings_view_from_jwt_opt,
   person::Register,
-  site::*,
+  site::{CreateSite, GetSite, GetSiteResponse, MyUserInfo},
+  utils::{blocking, build_federated_instances, get_local_user_settings_view_from_jwt_opt},
 };
-use lemmy_db_views::site_view::SiteView;
-use lemmy_db_views_actor::{
-  community_block_view::CommunityBlockView,
-  community_follower_view::CommunityFollowerView,
-  community_moderator_view::CommunityModeratorView,
-  person_block_view::PersonBlockView,
-  person_view::PersonViewSafe,
+use lemmy_db_views::structs::SiteView;
+use lemmy_db_views_actor::structs::{
+  CommunityBlockView,
+  CommunityFollowerView,
+  CommunityModeratorView,
+  PersonBlockView,
+  PersonViewSafe,
 };
 use lemmy_utils::{version, ConnectionId, LemmyError};
 use lemmy_websocket::{messages::GetUsersOnline, LemmyContext};
diff --git a/crates/api_crud/src/site/update.rs b/crates/api_crud/src/site/update.rs
index a6890d26..ace19fd7 100644
--- a/crates/api_crud/src/site/update.rs
+++ b/crates/api_crud/src/site/update.rs
@@ -1,25 +1,24 @@
 use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  check_image_has_local_domain,
-  get_local_user_view_from_jwt,
-  is_admin,
   site::{EditSite, SiteResponse},
-  site_description_length_check,
+  utils::{
+    blocking,
+    check_image_has_local_domain,
+    get_local_user_view_from_jwt,
+    is_admin,
+    site_description_length_check,
+  },
 };
 use lemmy_db_schema::{
-  diesel_option_overwrite,
-  diesel_option_overwrite_to_url,
-  naive_now,
   source::{
     local_user::LocalUser,
     site::{Site, SiteForm},
   },
   traits::Crud,
-  ListingType,
+  utils::{diesel_option_overwrite, diesel_option_overwrite_to_url, naive_now, ListingType},
 };
-use lemmy_db_views::site_view::SiteView;
+use lemmy_db_views::structs::SiteView;
 use lemmy_utils::{utils::check_slurs_opt, ConnectionId, LemmyError};
 use lemmy_websocket::{messages::SendAllMessage, LemmyContext, UserOperationCrud};
 use std::{default::Default, str::FromStr};
diff --git a/crates/api_crud/src/user/create.rs b/crates/api_crud/src/user/create.rs
index 00ef7db6..45661704 100644
--- a/crates/api_crud/src/user/create.rs
+++ b/crates/api_crud/src/user/create.rs
@@ -1,11 +1,8 @@
 use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  honeypot_check,
-  password_length_check,
-  person::*,
-  send_verification_email,
+  person::{LoginResponse, Register},
+  utils::{blocking, honeypot_check, password_length_check, send_verification_email},
 };
 use lemmy_apub::{
   generate_followers_url,
@@ -15,7 +12,7 @@ use lemmy_apub::{
   EndpointType,
 };
 use lemmy_db_schema::{
-  aggregates::person_aggregates::PersonAggregates,
+  aggregates::structs::PersonAggregates,
   newtypes::CommunityId,
   source::{
     community::{
@@ -33,8 +30,8 @@ use lemmy_db_schema::{
   },
   traits::{Crud, Followable, Joinable},
 };
-use lemmy_db_views::local_user_view::LocalUserView;
-use lemmy_db_views_actor::person_view::PersonViewSafe;
+use lemmy_db_views::structs::LocalUserView;
+use lemmy_db_views_actor::structs::PersonViewSafe;
 use lemmy_utils::{
   apub::generate_actor_keypair,
   claims::Claims,
diff --git a/crates/api_crud/src/user/delete.rs b/crates/api_crud/src/user/delete.rs
index ea1cbcff..c5e0e219 100644
--- a/crates/api_crud/src/user/delete.rs
+++ b/crates/api_crud/src/user/delete.rs
@@ -1,7 +1,10 @@
 use crate::PerformCrud;
 use actix_web::web::Data;
 use bcrypt::verify;
-use lemmy_api_common::{delete_user_account, get_local_user_view_from_jwt, person::*};
+use lemmy_api_common::{
+  person::{DeleteAccount, DeleteAccountResponse},
+  utils::{delete_user_account, get_local_user_view_from_jwt},
+};
 use lemmy_apub::protocol::activities::deletion::delete_user::DeleteUser;
 use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::LemmyContext;
diff --git a/crates/api_crud/src/user/read.rs b/crates/api_crud/src/user/read.rs
index 839f46f2..4b54430d 100644
--- a/crates/api_crud/src/user/read.rs
+++ b/crates/api_crud/src/user/read.rs
@@ -1,18 +1,16 @@
 use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  blocking,
-  check_private_instance,
-  get_local_user_view_from_jwt_opt,
-  person::*,
+  person::{GetPersonDetails, GetPersonDetailsResponse},
+  utils::{blocking, check_private_instance, get_local_user_view_from_jwt_opt},
 };
 use lemmy_apub::{fetcher::resolve_actor_identifier, objects::person::ApubPerson};
-use lemmy_db_schema::{from_opt_str_to_opt_enum, source::person::Person, SortType};
-use lemmy_db_views::{comment_view::CommentQueryBuilder, post_view::PostQueryBuilder};
-use lemmy_db_views_actor::{
-  community_moderator_view::CommunityModeratorView,
-  person_view::PersonViewSafe,
+use lemmy_db_schema::{
+  source::person::Person,
+  utils::{from_opt_str_to_opt_enum, SortType},
 };
+use lemmy_db_views::{comment_view::CommentQueryBuilder, post_view::PostQueryBuilder};
+use lemmy_db_views_actor::structs::{CommunityModeratorView, PersonViewSafe};
 use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::LemmyContext;
 
diff --git a/crates/apub/Cargo.toml b/crates/apub/Cargo.toml
index 7fa3e175..999964e2 100644
--- a/crates/apub/Cargo.toml
+++ b/crates/apub/Cargo.toml
@@ -15,14 +15,14 @@ doctest = false
 [dependencies]
 lemmy_utils = { version = "=0.16.3", path = "../utils" }
 lemmy_apub_lib = { version = "=0.16.3", path = "../apub_lib" }
-lemmy_db_schema = { version = "=0.16.3", path = "../db_schema" }
-lemmy_db_views = { version = "=0.16.3", path = "../db_views" }
-lemmy_db_views_actor = { version = "=0.16.3", path = "../db_views_actor" }
-lemmy_api_common = { version = "=0.16.3", path = "../api_common" }
+lemmy_db_schema = { version = "=0.16.3", path = "../db_schema", features = ["full"] }
+lemmy_db_views = { version = "=0.16.3", path = "../db_views", features = ["full"] }
+lemmy_db_views_actor = { version = "=0.16.3", path = "../db_views_actor", features = ["full"] }
+lemmy_api_common = { version = "=0.16.3", path = "../api_common", features = ["full"] }
 lemmy_websocket = { version = "=0.16.3", path = "../websocket" }
 diesel = "1.4.8"
 activitystreams-kinds = "0.2.1"
-chrono = { version = "0.4.19", features = ["serde"] }
+chrono = { version = "0.4.19", features = ["serde"], default-features = false }
 serde_json = { version = "1.0.79", features = ["preserve_order"] }
 serde = { version = "1.0.136", features = ["derive"] }
 serde_with = "1.12.0"
diff --git a/crates/apub/src/activities/block/block_user.rs b/crates/apub/src/activities/block/block_user.rs
index d21c038b..bcab3148 100644
--- a/crates/apub/src/activities/block/block_user.rs
+++ b/crates/apub/src/activities/block/block_user.rs
@@ -16,7 +16,7 @@ use crate::{
 use activitystreams_kinds::{activity::BlockType, public};
 use anyhow::anyhow;
 use chrono::NaiveDateTime;
-use lemmy_api_common::{blocking, remove_user_data, remove_user_data_in_community};
+use lemmy_api_common::utils::{blocking, remove_user_data, remove_user_data_in_community};
 use lemmy_apub_lib::{
   data::Data,
   object_id::ObjectId,
diff --git a/crates/apub/src/activities/block/mod.rs b/crates/apub/src/activities/block/mod.rs
index f41e352f..bb35d7d2 100644
--- a/crates/apub/src/activities/block/mod.rs
+++ b/crates/apub/src/activities/block/mod.rs
@@ -3,12 +3,12 @@ use crate::{
   protocol::objects::{group::Group, instance::Instance},
 };
 use chrono::NaiveDateTime;
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_apub_lib::{
   object_id::ObjectId,
   traits::{ActorType, ApubObject},
 };
-use lemmy_db_schema::{source::site::Site, DbPool};
+use lemmy_db_schema::{source::site::Site, utils::DbPool};
 use lemmy_utils::LemmyError;
 use lemmy_websocket::LemmyContext;
 use serde::Deserialize;
diff --git a/crates/apub/src/activities/block/undo_block_user.rs b/crates/apub/src/activities/block/undo_block_user.rs
index 29c9cd10..130b1eb5 100644
--- a/crates/apub/src/activities/block/undo_block_user.rs
+++ b/crates/apub/src/activities/block/undo_block_user.rs
@@ -12,7 +12,7 @@ use crate::{
   protocol::activities::block::{block_user::BlockUser, undo_block_user::UndoBlockUser},
 };
 use activitystreams_kinds::{activity::UndoType, public};
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_apub_lib::{
   data::Data,
   object_id::ObjectId,
diff --git a/crates/apub/src/activities/community/add_mod.rs b/crates/apub/src/activities/community/add_mod.rs
index a86c8ac3..2aa7989e 100644
--- a/crates/apub/src/activities/community/add_mod.rs
+++ b/crates/apub/src/activities/community/add_mod.rs
@@ -18,7 +18,7 @@ use crate::{
   protocol::activities::community::add_mod::AddMod,
 };
 use activitystreams_kinds::{activity::AddType, public};
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_apub_lib::{
   data::Data,
   object_id::ObjectId,
diff --git a/crates/apub/src/activities/community/remove_mod.rs b/crates/apub/src/activities/community/remove_mod.rs
index 7e43fcbf..f42d47f6 100644
--- a/crates/apub/src/activities/community/remove_mod.rs
+++ b/crates/apub/src/activities/community/remove_mod.rs
@@ -18,7 +18,7 @@ use crate::{
   protocol::activities::community::remove_mod::RemoveMod,
 };
 use activitystreams_kinds::{activity::RemoveType, public};
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_apub_lib::{
   data::Data,
   object_id::ObjectId,
diff --git a/crates/apub/src/activities/community/report.rs b/crates/apub/src/activities/community/report.rs
index 5ebbd9d7..0069aa6e 100644
--- a/crates/apub/src/activities/community/report.rs
+++ b/crates/apub/src/activities/community/report.rs
@@ -10,7 +10,7 @@ use crate::{
   PostOrComment,
 };
 use activitystreams_kinds::activity::FlagType;
-use lemmy_api_common::{blocking, comment::CommentReportResponse, post::PostReportResponse};
+use lemmy_api_common::{comment::CommentReportResponse, post::PostReportResponse, utils::blocking};
 use lemmy_apub_lib::{
   data::Data,
   object_id::ObjectId,
@@ -23,7 +23,7 @@ use lemmy_db_schema::{
   },
   traits::Reportable,
 };
-use lemmy_db_views::{comment_report_view::CommentReportView, post_report_view::PostReportView};
+use lemmy_db_views::structs::{CommentReportView, PostReportView};
 use lemmy_utils::LemmyError;
 use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
 
diff --git a/crates/apub/src/activities/community/update.rs b/crates/apub/src/activities/community/update.rs
index f260363b..e6676b8b 100644
--- a/crates/apub/src/activities/community/update.rs
+++ b/crates/apub/src/activities/community/update.rs
@@ -12,7 +12,7 @@ use crate::{
   protocol::activities::community::update::UpdateCommunity,
 };
 use activitystreams_kinds::{activity::UpdateType, public};
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_apub_lib::{
   data::Data,
   object_id::ObjectId,
diff --git a/crates/apub/src/activities/create_or_update/comment.rs b/crates/apub/src/activities/create_or_update/comment.rs
index ed95b931..c0e53a60 100644
--- a/crates/apub/src/activities/create_or_update/comment.rs
+++ b/crates/apub/src/activities/create_or_update/comment.rs
@@ -14,7 +14,7 @@ use crate::{
   protocol::activities::{create_or_update::comment::CreateOrUpdateComment, CreateOrUpdateType},
 };
 use activitystreams_kinds::public;
-use lemmy_api_common::{blocking, check_post_deleted_or_removed};
+use lemmy_api_common::utils::{blocking, check_post_deleted_or_removed};
 use lemmy_apub_lib::{
   data::Data,
   object_id::ObjectId,
diff --git a/crates/apub/src/activities/create_or_update/mod.rs b/crates/apub/src/activities/create_or_update/mod.rs
index 3cdac71b..99305f24 100644
--- a/crates/apub/src/activities/create_or_update/mod.rs
+++ b/crates/apub/src/activities/create_or_update/mod.rs
@@ -1,5 +1,5 @@
 use crate::objects::person::ApubPerson;
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_apub_lib::object_id::ObjectId;
 use lemmy_db_schema::{
   newtypes::LocalUserId,
diff --git a/crates/apub/src/activities/create_or_update/post.rs b/crates/apub/src/activities/create_or_update/post.rs
index ddbe0981..70c59c35 100644
--- a/crates/apub/src/activities/create_or_update/post.rs
+++ b/crates/apub/src/activities/create_or_update/post.rs
@@ -13,7 +13,7 @@ use crate::{
   protocol::activities::{create_or_update::post::CreateOrUpdatePost, CreateOrUpdateType},
 };
 use activitystreams_kinds::public;
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_apub_lib::{
   data::Data,
   object_id::ObjectId,
diff --git a/crates/apub/src/activities/create_or_update/private_message.rs b/crates/apub/src/activities/create_or_update/private_message.rs
index bae43047..295e5b4f 100644
--- a/crates/apub/src/activities/create_or_update/private_message.rs
+++ b/crates/apub/src/activities/create_or_update/private_message.rs
@@ -6,7 +6,7 @@ use crate::{
     CreateOrUpdateType,
   },
 };
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_apub_lib::{
   data::Data,
   object_id::ObjectId,
diff --git a/crates/apub/src/activities/deletion/delete.rs b/crates/apub/src/activities/deletion/delete.rs
index 275683a3..3f021587 100644
--- a/crates/apub/src/activities/deletion/delete.rs
+++ b/crates/apub/src/activities/deletion/delete.rs
@@ -10,7 +10,7 @@ use crate::{
 };
 use activitystreams_kinds::activity::DeleteType;
 use anyhow::anyhow;
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_apub_lib::{data::Data, object_id::ObjectId, traits::ActivityHandler};
 use lemmy_db_schema::{
   source::{
diff --git a/crates/apub/src/activities/deletion/delete_user.rs b/crates/apub/src/activities/deletion/delete_user.rs
index 0cd2c501..a96333d5 100644
--- a/crates/apub/src/activities/deletion/delete_user.rs
+++ b/crates/apub/src/activities/deletion/delete_user.rs
@@ -4,7 +4,7 @@ use crate::{
   protocol::activities::deletion::delete_user::DeleteUser,
 };
 use activitystreams_kinds::{activity::DeleteType, public};
-use lemmy_api_common::{blocking, delete_user_account};
+use lemmy_api_common::utils::{blocking, delete_user_account};
 use lemmy_apub_lib::{
   data::Data,
   object_id::ObjectId,
diff --git a/crates/apub/src/activities/deletion/mod.rs b/crates/apub/src/activities/deletion/mod.rs
index 1ff8429a..123e87fb 100644
--- a/crates/apub/src/activities/deletion/mod.rs
+++ b/crates/apub/src/activities/deletion/mod.rs
@@ -18,7 +18,7 @@ use crate::{
   protocol::activities::deletion::{delete::Delete, undo_delete::UndoDelete},
 };
 use activitystreams_kinds::public;
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_apub_lib::{
   object_id::ObjectId,
   traits::{ActorType, ApubObject},
diff --git a/crates/apub/src/activities/deletion/undo_delete.rs b/crates/apub/src/activities/deletion/undo_delete.rs
index 6dced439..34e89747 100644
--- a/crates/apub/src/activities/deletion/undo_delete.rs
+++ b/crates/apub/src/activities/deletion/undo_delete.rs
@@ -9,7 +9,7 @@ use crate::{
   protocol::activities::deletion::{delete::Delete, undo_delete::UndoDelete},
 };
 use activitystreams_kinds::activity::UndoType;
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_apub_lib::{data::Data, object_id::ObjectId, traits::ActivityHandler};
 use lemmy_db_schema::{
   source::{
diff --git a/crates/apub/src/activities/following/accept.rs b/crates/apub/src/activities/following/accept.rs
index 690bec28..dfc008a9 100644
--- a/crates/apub/src/activities/following/accept.rs
+++ b/crates/apub/src/activities/following/accept.rs
@@ -3,7 +3,7 @@ use crate::{
   protocol::activities::following::{accept::AcceptFollowCommunity, follow::FollowCommunity},
 };
 use activitystreams_kinds::activity::AcceptType;
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_apub_lib::{
   data::Data,
   object_id::ObjectId,
diff --git a/crates/apub/src/activities/following/follow.rs b/crates/apub/src/activities/following/follow.rs
index fb35b0d3..d46b3dc2 100644
--- a/crates/apub/src/activities/following/follow.rs
+++ b/crates/apub/src/activities/following/follow.rs
@@ -10,7 +10,7 @@ use crate::{
   protocol::activities::following::{accept::AcceptFollowCommunity, follow::FollowCommunity},
 };
 use activitystreams_kinds::activity::FollowType;
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_apub_lib::{
   data::Data,
   object_id::ObjectId,
diff --git a/crates/apub/src/activities/following/undo_follow.rs b/crates/apub/src/activities/following/undo_follow.rs
index 73b0366b..f70acdf2 100644
--- a/crates/apub/src/activities/following/undo_follow.rs
+++ b/crates/apub/src/activities/following/undo_follow.rs
@@ -4,7 +4,7 @@ use crate::{
   protocol::activities::following::{follow::FollowCommunity, undo_follow::UndoFollowCommunity},
 };
 use activitystreams_kinds::activity::UndoType;
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_apub_lib::{
   data::Data,
   object_id::ObjectId,
diff --git a/crates/apub/src/activities/mod.rs b/crates/apub/src/activities/mod.rs
index 77d4883f..2c8b0921 100644
--- a/crates/apub/src/activities/mod.rs
+++ b/crates/apub/src/activities/mod.rs
@@ -7,7 +7,7 @@ use crate::{
 };
 use activitystreams_kinds::public;
 use anyhow::anyhow;
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_apub_lib::{
   activity_queue::send_activity,
   object_id::ObjectId,
@@ -15,10 +15,7 @@ use lemmy_apub_lib::{
   verify::verify_domains_match,
 };
 use lemmy_db_schema::source::community::Community;
-use lemmy_db_views_actor::{
-  community_person_ban_view::CommunityPersonBanView,
-  community_view::CommunityView,
-};
+use lemmy_db_views_actor::structs::{CommunityPersonBanView, CommunityView};
 use lemmy_utils::{settings::structs::Settings, LemmyError};
 use lemmy_websocket::LemmyContext;
 use serde::Serialize;
diff --git a/crates/apub/src/activities/voting/mod.rs b/crates/apub/src/activities/voting/mod.rs
index c82b4efd..f2e3f24f 100644
--- a/crates/apub/src/activities/voting/mod.rs
+++ b/crates/apub/src/activities/voting/mod.rs
@@ -1,4 +1,4 @@
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_db_schema::{
   source::{
     comment::{CommentLike, CommentLikeForm},
diff --git a/crates/apub/src/activities/voting/undo_vote.rs b/crates/apub/src/activities/voting/undo_vote.rs
index 16ea64e0..eb350770 100644
--- a/crates/apub/src/activities/voting/undo_vote.rs
+++ b/crates/apub/src/activities/voting/undo_vote.rs
@@ -16,7 +16,7 @@ use crate::{
   PostOrComment,
 };
 use activitystreams_kinds::{activity::UndoType, public};
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_apub_lib::{
   data::Data,
   object_id::ObjectId,
diff --git a/crates/apub/src/activities/voting/vote.rs b/crates/apub/src/activities/voting/vote.rs
index ce6dee28..a851e28f 100644
--- a/crates/apub/src/activities/voting/vote.rs
+++ b/crates/apub/src/activities/voting/vote.rs
@@ -14,7 +14,7 @@ use crate::{
 };
 use activitystreams_kinds::public;
 use anyhow::anyhow;
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_apub_lib::{
   data::Data,
   object_id::ObjectId,
diff --git a/crates/apub/src/collections/community_moderators.rs b/crates/apub/src/collections/community_moderators.rs
index b0182bdf..4e084a16 100644
--- a/crates/apub/src/collections/community_moderators.rs
+++ b/crates/apub/src/collections/community_moderators.rs
@@ -6,13 +6,13 @@ use crate::{
 };
 use activitystreams_kinds::collection::OrderedCollectionType;
 use chrono::NaiveDateTime;
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_apub_lib::{object_id::ObjectId, traits::ApubObject, verify::verify_domains_match};
 use lemmy_db_schema::{
   source::community::{CommunityModerator, CommunityModeratorForm},
   traits::Joinable,
 };
-use lemmy_db_views_actor::community_moderator_view::CommunityModeratorView;
+use lemmy_db_views_actor::structs::CommunityModeratorView;
 use lemmy_utils::LemmyError;
 use url::Url;
 
diff --git a/crates/apub/src/collections/community_outbox.rs b/crates/apub/src/collections/community_outbox.rs
index 769e9bb2..098471c2 100644
--- a/crates/apub/src/collections/community_outbox.rs
+++ b/crates/apub/src/collections/community_outbox.rs
@@ -11,7 +11,7 @@ use crate::{
 use activitystreams_kinds::collection::OrderedCollectionType;
 use chrono::NaiveDateTime;
 use futures::future::join_all;
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_apub_lib::{
   data::Data,
   traits::{ActivityHandler, ApubObject},
diff --git a/crates/apub/src/fetcher/deletable_apub_object.rs b/crates/apub/src/fetcher/deletable_apub_object.rs
index ccb409e8..2eb4f884 100644
--- a/crates/apub/src/fetcher/deletable_apub_object.rs
+++ b/crates/apub/src/fetcher/deletable_apub_object.rs
@@ -1,5 +1,5 @@
 use crate::fetcher::post_or_comment::PostOrComment;
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_db_queries::source::{
   comment::Comment_,
   community::Community_,
diff --git a/crates/apub/src/fetcher/mod.rs b/crates/apub/src/fetcher/mod.rs
index 16173568..cd889ccc 100644
--- a/crates/apub/src/fetcher/mod.rs
+++ b/crates/apub/src/fetcher/mod.rs
@@ -1,6 +1,6 @@
 use crate::fetcher::webfinger::webfinger_resolve_actor;
 use itertools::Itertools;
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_apub_lib::traits::{ActorType, ApubObject};
 use lemmy_db_schema::traits::ApubActor;
 use lemmy_utils::{settings::structs::Settings, LemmyError};
diff --git a/crates/apub/src/fetcher/webfinger.rs b/crates/apub/src/fetcher/webfinger.rs
index e2370c32..da516b1d 100644
--- a/crates/apub/src/fetcher/webfinger.rs
+++ b/crates/apub/src/fetcher/webfinger.rs
@@ -5,10 +5,7 @@ use lemmy_apub_lib::{
   traits::{ActorType, ApubObject},
 };
 use lemmy_db_schema::newtypes::DbUrl;
-use lemmy_utils::{
-  request::{retry, RecvError},
-  LemmyError,
-};
+use lemmy_utils::{request::retry, LemmyError};
 use lemmy_websocket::LemmyContext;
 use serde::{Deserialize, Serialize};
 use tracing::debug;
@@ -58,10 +55,7 @@ where
 
   let response = retry(|| context.client().get(&fetch_url).send()).await?;
 
-  let res: WebfingerResponse = response
-    .json()
-    .await
-    .map_err(|e| RecvError(e.to_string()))?;
+  let res: WebfingerResponse = response.json().await.map_err(LemmyError::from)?;
 
   let links: Vec<Url> = res
     .links
diff --git a/crates/apub/src/http/comment.rs b/crates/apub/src/http/comment.rs
index 6a724343..1918a38e 100644
--- a/crates/apub/src/http/comment.rs
+++ b/crates/apub/src/http/comment.rs
@@ -4,7 +4,7 @@ use crate::{
 };
 use actix_web::{web, web::Path, HttpResponse};
 use diesel::result::Error::NotFound;
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_apub_lib::traits::ApubObject;
 use lemmy_db_schema::{newtypes::CommentId, source::comment::Comment, traits::Crud};
 use lemmy_utils::LemmyError;
diff --git a/crates/apub/src/http/community.rs b/crates/apub/src/http/community.rs
index c049c6ae..41f6d1c0 100644
--- a/crates/apub/src/http/community.rs
+++ b/crates/apub/src/http/community.rs
@@ -22,7 +22,7 @@ use crate::{
   },
 };
 use actix_web::{web, web::Payload, HttpRequest, HttpResponse};
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_apub_lib::{object_id::ObjectId, traits::ApubObject};
 use lemmy_db_schema::{source::community::Community, traits::ApubActor};
 use lemmy_utils::LemmyError;
diff --git a/crates/apub/src/http/mod.rs b/crates/apub/src/http/mod.rs
index 477357e0..0112d205 100644
--- a/crates/apub/src/http/mod.rs
+++ b/crates/apub/src/http/mod.rs
@@ -15,7 +15,7 @@ use actix_web::{
 use anyhow::{anyhow, Context};
 use futures::StreamExt;
 use http::StatusCode;
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_apub_lib::{
   data::Data,
   object_id::ObjectId,
diff --git a/crates/apub/src/http/person.rs b/crates/apub/src/http/person.rs
index 9081d5a5..f0540268 100644
--- a/crates/apub/src/http/person.rs
+++ b/crates/apub/src/http/person.rs
@@ -13,7 +13,7 @@ use crate::{
   protocol::collections::empty_outbox::EmptyOutbox,
 };
 use actix_web::{web, web::Payload, HttpRequest, HttpResponse};
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_apub_lib::traits::ApubObject;
 use lemmy_db_schema::{source::person::Person, traits::ApubActor};
 use lemmy_utils::LemmyError;
diff --git a/crates/apub/src/http/post.rs b/crates/apub/src/http/post.rs
index 3f180a0c..845803b9 100644
--- a/crates/apub/src/http/post.rs
+++ b/crates/apub/src/http/post.rs
@@ -4,7 +4,7 @@ use crate::{
 };
 use actix_web::{web, HttpResponse};
 use diesel::result::Error::NotFound;
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_apub_lib::traits::ApubObject;
 use lemmy_db_schema::{newtypes::PostId, source::post::Post, traits::Crud};
 use lemmy_utils::LemmyError;
diff --git a/crates/apub/src/http/site.rs b/crates/apub/src/http/site.rs
index 894622ad..e4811066 100644
--- a/crates/apub/src/http/site.rs
+++ b/crates/apub/src/http/site.rs
@@ -6,7 +6,7 @@ use crate::{
   protocol::collections::empty_outbox::EmptyOutbox,
 };
 use actix_web::{web, web::Payload, HttpRequest, HttpResponse};
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_apub_lib::traits::ApubObject;
 use lemmy_db_schema::source::site::Site;
 use lemmy_utils::{settings::structs::Settings, LemmyError};
diff --git a/crates/apub/src/lib.rs b/crates/apub/src/lib.rs
index 80116a8f..b7f1912d 100644
--- a/crates/apub/src/lib.rs
+++ b/crates/apub/src/lib.rs
@@ -1,7 +1,7 @@
 use crate::fetcher::post_or_comment::PostOrComment;
 use anyhow::{anyhow, Context};
-use lemmy_api_common::blocking;
-use lemmy_db_schema::{newtypes::DbUrl, source::activity::Activity, DbPool};
+use lemmy_api_common::utils::blocking;
+use lemmy_db_schema::{newtypes::DbUrl, source::activity::Activity, utils::DbPool};
 use lemmy_utils::{location_info, settings::structs::Settings, LemmyError};
 use serde::{Deserialize, Deserializer};
 use std::net::IpAddr;
@@ -14,7 +14,6 @@ mod context;
 pub mod fetcher;
 pub mod http;
 pub(crate) mod mentions;
-pub mod migrations;
 pub mod objects;
 pub mod protocol;
 
diff --git a/crates/apub/src/mentions.rs b/crates/apub/src/mentions.rs
index 78d568e5..b68391a9 100644
--- a/crates/apub/src/mentions.rs
+++ b/crates/apub/src/mentions.rs
@@ -3,12 +3,12 @@ use crate::{
   objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson},
 };
 use activitystreams_kinds::link::MentionType;
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_apub_lib::{object_id::ObjectId, traits::ActorType};
 use lemmy_db_schema::{
   source::{comment::Comment, person::Person, post::Post},
   traits::Crud,
-  DbPool,
+  utils::DbPool,
 };
 use lemmy_utils::{
   utils::{scrape_text_for_mentions, MentionData},
diff --git a/crates/apub/src/migrations.rs b/crates/apub/src/migrations.rs
deleted file mode 100644
index 8b137891..00000000
--- a/crates/apub/src/migrations.rs
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/crates/apub/src/objects/comment.rs b/crates/apub/src/objects/comment.rs
index 40aef919..c987ff8b 100644
--- a/crates/apub/src/objects/comment.rs
+++ b/crates/apub/src/objects/comment.rs
@@ -11,7 +11,7 @@ use crate::{
 };
 use activitystreams_kinds::{object::NoteType, public};
 use chrono::NaiveDateTime;
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_apub_lib::{
   object_id::ObjectId,
   traits::ApubObject,
diff --git a/crates/apub/src/objects/community.rs b/crates/apub/src/objects/community.rs
index 59489d3e..a8904ac5 100644
--- a/crates/apub/src/objects/community.rs
+++ b/crates/apub/src/objects/community.rs
@@ -13,13 +13,13 @@ use crate::{
 use activitystreams_kinds::actor::GroupType;
 use chrono::NaiveDateTime;
 use itertools::Itertools;
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_apub_lib::{
   object_id::ObjectId,
   traits::{ActorType, ApubObject},
 };
 use lemmy_db_schema::{source::community::Community, traits::ApubActor};
-use lemmy_db_views_actor::community_follower_view::CommunityFollowerView;
+use lemmy_db_views_actor::structs::CommunityFollowerView;
 use lemmy_utils::{
   utils::{convert_datetime, markdown_to_html},
   LemmyError,
diff --git a/crates/apub/src/objects/instance.rs b/crates/apub/src/objects/instance.rs
index c3641fca..225a1091 100644
--- a/crates/apub/src/objects/instance.rs
+++ b/crates/apub/src/objects/instance.rs
@@ -8,7 +8,7 @@ use crate::{
   },
 };
 use chrono::NaiveDateTime;
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_apub_lib::{
   object_id::ObjectId,
   traits::{ActorType, ApubObject},
@@ -16,8 +16,8 @@ use lemmy_apub_lib::{
   verify::verify_domains_match,
 };
 use lemmy_db_schema::{
-  naive_now,
   source::site::{Site, SiteForm},
+  utils::naive_now,
 };
 use lemmy_utils::{
   utils::{check_slurs, check_slurs_opt, convert_datetime, markdown_to_html},
diff --git a/crates/apub/src/objects/mod.rs b/crates/apub/src/objects/mod.rs
index e7155b4b..0e55c49d 100644
--- a/crates/apub/src/objects/mod.rs
+++ b/crates/apub/src/objects/mod.rs
@@ -62,15 +62,14 @@ pub(crate) mod tests {
     r2d2::{ConnectionManager, Pool},
     PgConnection,
   };
+  use lemmy_api_common::request::build_user_agent;
   use lemmy_apub_lib::activity_queue::create_activity_queue;
   use lemmy_db_schema::{
-    establish_unpooled_connection,
-    get_database_url_from_env,
     source::secret::Secret,
+    utils::{establish_unpooled_connection, get_database_url_from_env},
   };
   use lemmy_utils::{
     rate_limit::{rate_limiter::RateLimiter, RateLimit},
-    request::build_user_agent,
     settings::structs::Settings,
     LemmyError,
   };
diff --git a/crates/apub/src/objects/person.rs b/crates/apub/src/objects/person.rs
index 039dbbb1..e1938ed3 100644
--- a/crates/apub/src/objects/person.rs
+++ b/crates/apub/src/objects/person.rs
@@ -16,16 +16,16 @@ use crate::{
   },
 };
 use chrono::NaiveDateTime;
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_apub_lib::{
   object_id::ObjectId,
   traits::{ActorType, ApubObject},
   verify::verify_domains_match,
 };
 use lemmy_db_schema::{
-  naive_now,
   source::person::{Person as DbPerson, PersonForm},
   traits::ApubActor,
+  utils::naive_now,
 };
 use lemmy_utils::{
   utils::{check_slurs, check_slurs_opt, convert_datetime, markdown_to_html},
diff --git a/crates/apub/src/objects/post.rs b/crates/apub/src/objects/post.rs
index 88ca1ceb..f6938dd6 100644
--- a/crates/apub/src/objects/post.rs
+++ b/crates/apub/src/objects/post.rs
@@ -13,7 +13,7 @@ use crate::{
 };
 use activitystreams_kinds::public;
 use chrono::NaiveDateTime;
-use lemmy_api_common::blocking;
+use lemmy_api_common::{request::fetch_site_data, utils::blocking};
 use lemmy_apub_lib::{
   object_id::ObjectId,
   traits::ApubObject,
@@ -31,7 +31,6 @@ use lemmy_db_schema::{
   traits::Crud,
 };
 use lemmy_utils::{
-  request::fetch_site_data,
   utils::{check_slurs, convert_datetime, markdown_to_html, remove_slurs},
   LemmyError,
 };
diff --git a/crates/apub/src/objects/private_message.rs b/crates/apub/src/objects/private_message.rs
index 56005986..7a638af3 100644
--- a/crates/apub/src/objects/private_message.rs
+++ b/crates/apub/src/objects/private_message.rs
@@ -6,7 +6,7 @@ use crate::{
   },
 };
 use chrono::NaiveDateTime;
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_apub_lib::{
   object_id::ObjectId,
   traits::ApubObject,
diff --git a/crates/apub/src/protocol/collections/group_followers.rs b/crates/apub/src/protocol/collections/group_followers.rs
index 032731b7..777d1980 100644
--- a/crates/apub/src/protocol/collections/group_followers.rs
+++ b/crates/apub/src/protocol/collections/group_followers.rs
@@ -1,8 +1,8 @@
 use crate::generate_followers_url;
 use activitystreams_kinds::collection::CollectionType;
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_db_schema::source::community::Community;
-use lemmy_db_views_actor::community_follower_view::CommunityFollowerView;
+use lemmy_db_views_actor::structs::CommunityFollowerView;
 use lemmy_utils::LemmyError;
 use lemmy_websocket::LemmyContext;
 use serde::{Deserialize, Serialize};
diff --git a/crates/apub/src/protocol/objects/group.rs b/crates/apub/src/protocol/objects/group.rs
index 57d0ae15..c0b544f3 100644
--- a/crates/apub/src/protocol/objects/group.rs
+++ b/crates/apub/src/protocol/objects/group.rs
@@ -14,7 +14,7 @@ use crate::{
 use activitystreams_kinds::actor::GroupType;
 use chrono::{DateTime, FixedOffset};
 use lemmy_apub_lib::{object_id::ObjectId, signatures::PublicKey, verify::verify_domains_match};
-use lemmy_db_schema::{naive_now, source::community::CommunityForm};
+use lemmy_db_schema::{source::community::CommunityForm, utils::naive_now};
 use lemmy_utils::{
   utils::{check_slurs, check_slurs_opt},
   LemmyError,
diff --git a/crates/apub/src/protocol/objects/note.rs b/crates/apub/src/protocol/objects/note.rs
index b1c4b5d0..779675cc 100644
--- a/crates/apub/src/protocol/objects/note.rs
+++ b/crates/apub/src/protocol/objects/note.rs
@@ -6,7 +6,7 @@ use crate::{
 };
 use activitystreams_kinds::object::NoteType;
 use chrono::{DateTime, FixedOffset};
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_apub_lib::{object_id::ObjectId, values::MediaTypeHtml};
 use lemmy_db_schema::{newtypes::CommentId, source::post::Post, traits::Crud};
 use lemmy_utils::LemmyError;
diff --git a/crates/apub_lib/Cargo.toml b/crates/apub_lib/Cargo.toml
index 1b2cabbf..366ee7a0 100644
--- a/crates/apub_lib/Cargo.toml
+++ b/crates/apub_lib/Cargo.toml
@@ -10,7 +10,7 @@ documentation = "https://join-lemmy.org/docs/en/index.html"
 [dependencies]
 lemmy_utils = { version = "=0.16.3", path = "../utils" }
 lemmy_apub_lib_derive = { version = "=0.16.3", path = "../apub_lib_derive" }
-chrono = "0.4.19"
+chrono = { version = "0.4.19", default-features = false }
 serde = { version = "1.0.136", features = ["derive"] }
 async-trait = "0.1.53"
 url = { version = "2.2.2", features = ["serde"] }
diff --git a/crates/db_schema/Cargo.toml b/crates/db_schema/Cargo.toml
index 0ee7dd5a..b2e40ed8 100644
--- a/crates/db_schema/Cargo.toml
+++ b/crates/db_schema/Cargo.toml
@@ -12,22 +12,26 @@ name = "lemmy_db_schema"
 path = "src/lib.rs"
 doctest = false
 
+[features]
+full = ["diesel", "diesel-derive-newtype", "diesel_migrations", "bcrypt", "lemmy_utils",
+    "lemmy_apub_lib", "strum", "strum_macros", "sha2", "regex", "once_cell", "serde_json"]
+
 [dependencies]
-lemmy_utils = { version = "=0.16.3", path = "../utils" }
-lemmy_apub_lib = { version = "=0.16.3", path = "../apub_lib" }
-diesel = { version = "1.4.8", features = ["postgres","chrono","r2d2","serde_json"] }
-diesel_migrations = "1.4.0"
-chrono = { version = "0.4.19", features = ["serde"] }
+chrono = { version = "0.4.19", features = ["serde"], default-features = false }
 serde = { version = "1.0.136", features = ["derive"] }
-serde_json = { version = "1.0.79", features = ["preserve_order"] }
 url = { version = "2.2.2", features = ["serde"] }
-diesel-derive-newtype = "0.1.2"
-regex = "1.5.5"
-once_cell = "1.10.0"
-strum = "0.24.0"
-strum_macros = "0.24.0"
-sha2 = "0.10.2"
-bcrypt = "0.12.1"
+serde_json = { version = "1.0.79", features = ["preserve_order"], optional = true }
+lemmy_apub_lib = { version = "=0.16.3", path = "../apub_lib", optional = true }
+lemmy_utils = { version = "=0.16.3", path = "../utils", optional = true }
+bcrypt = { version = "0.12.1", optional = true }
+diesel = { version = "1.4.8", features = ["postgres","chrono","r2d2","serde_json"], optional = true }
+diesel-derive-newtype = { version = "0.1.2", optional = true }
+diesel_migrations = { version = "1.4.0", optional = true }
+strum = { version = "0.24.0", optional = true }
+strum_macros = { version = "0.24.0", optional = true }
+sha2 = { version = "0.10.2", optional = true }
+regex = { version = "1.5.5", optional = true }
+once_cell = { version = "1.10.0", optional = true }
 
 [dev-dependencies]
 serial_test = "0.6.0"
diff --git a/crates/db_schema/src/aggregates/comment_aggregates.rs b/crates/db_schema/src/aggregates/comment_aggregates.rs
index d47899bb..5e70bcc5 100644
--- a/crates/db_schema/src/aggregates/comment_aggregates.rs
+++ b/crates/db_schema/src/aggregates/comment_aggregates.rs
@@ -1,19 +1,9 @@
-use crate::{newtypes::CommentId, schema::comment_aggregates};
+use crate::{
+  aggregates::structs::CommentAggregates,
+  newtypes::CommentId,
+  schema::comment_aggregates,
+};
 use diesel::{result::Error, *};
-use serde::{Deserialize, Serialize};
-
-#[derive(
-  Queryable, Associations, Identifiable, PartialEq, Debug, Serialize, Deserialize, Clone,
-)]
-#[table_name = "comment_aggregates"]
-pub struct CommentAggregates {
-  pub id: i32,
-  pub comment_id: CommentId,
-  pub score: i64,
-  pub upvotes: i64,
-  pub downvotes: i64,
-  pub published: chrono::NaiveDateTime,
-}
 
 impl CommentAggregates {
   pub fn read(conn: &PgConnection, comment_id: CommentId) -> Result<Self, Error> {
@@ -27,7 +17,6 @@ impl CommentAggregates {
 mod tests {
   use crate::{
     aggregates::comment_aggregates::CommentAggregates,
-    establish_unpooled_connection,
     source::{
       comment::{Comment, CommentForm, CommentLike, CommentLikeForm},
       community::{Community, CommunityForm},
@@ -35,6 +24,7 @@ mod tests {
       post::{Post, PostForm},
     },
     traits::{Crud, Likeable},
+    utils::establish_unpooled_connection,
   };
   use serial_test::serial;
 
diff --git a/crates/db_schema/src/aggregates/community_aggregates.rs b/crates/db_schema/src/aggregates/community_aggregates.rs
index d80425e1..950e68d3 100644
--- a/crates/db_schema/src/aggregates/community_aggregates.rs
+++ b/crates/db_schema/src/aggregates/community_aggregates.rs
@@ -1,23 +1,9 @@
-use crate::{newtypes::CommunityId, schema::community_aggregates};
+use crate::{
+  aggregates::structs::CommunityAggregates,
+  newtypes::CommunityId,
+  schema::community_aggregates,
+};
 use diesel::{result::Error, *};
-use serde::{Deserialize, Serialize};
-
-#[derive(
-  Queryable, Associations, Identifiable, PartialEq, Debug, Serialize, Deserialize, Clone,
-)]
-#[table_name = "community_aggregates"]
-pub struct CommunityAggregates {
-  pub id: i32,
-  pub community_id: CommunityId,
-  pub subscribers: i64,
-  pub posts: i64,
-  pub comments: i64,
-  pub published: chrono::NaiveDateTime,
-  pub users_active_day: i64,
-  pub users_active_week: i64,
-  pub users_active_month: i64,
-  pub users_active_half_year: i64,
-}
 
 impl CommunityAggregates {
   pub fn read(conn: &PgConnection, community_id: CommunityId) -> Result<Self, Error> {
@@ -31,7 +17,6 @@ impl CommunityAggregates {
 mod tests {
   use crate::{
     aggregates::community_aggregates::CommunityAggregates,
-    establish_unpooled_connection,
     source::{
       comment::{Comment, CommentForm},
       community::{Community, CommunityFollower, CommunityFollowerForm, CommunityForm},
@@ -39,6 +24,7 @@ mod tests {
       post::{Post, PostForm},
     },
     traits::{Crud, Followable},
+    utils::establish_unpooled_connection,
   };
   use serial_test::serial;
 
diff --git a/crates/db_schema/src/aggregates/mod.rs b/crates/db_schema/src/aggregates/mod.rs
index ad20f8a6..03ab1b89 100644
--- a/crates/db_schema/src/aggregates/mod.rs
+++ b/crates/db_schema/src/aggregates/mod.rs
@@ -1,5 +1,11 @@
+#[cfg(feature = "full")]
 pub mod comment_aggregates;
+#[cfg(feature = "full")]
 pub mod community_aggregates;
+#[cfg(feature = "full")]
 pub mod person_aggregates;
+#[cfg(feature = "full")]
 pub mod post_aggregates;
+#[cfg(feature = "full")]
 pub mod site_aggregates;
+pub mod structs;
diff --git a/crates/db_schema/src/aggregates/person_aggregates.rs b/crates/db_schema/src/aggregates/person_aggregates.rs
index e0fc0734..0f270f9c 100644
--- a/crates/db_schema/src/aggregates/person_aggregates.rs
+++ b/crates/db_schema/src/aggregates/person_aggregates.rs
@@ -1,19 +1,5 @@
-use crate::{newtypes::PersonId, schema::person_aggregates};
+use crate::{aggregates::structs::PersonAggregates, newtypes::PersonId, schema::person_aggregates};
 use diesel::{result::Error, *};
-use serde::{Deserialize, Serialize};
-
-#[derive(
-  Queryable, Associations, Identifiable, PartialEq, Debug, Serialize, Deserialize, Clone, Default,
-)]
-#[table_name = "person_aggregates"]
-pub struct PersonAggregates {
-  pub id: i32,
-  pub person_id: PersonId,
-  pub post_count: i64,
-  pub post_score: i64,
-  pub comment_count: i64,
-  pub comment_score: i64,
-}
 
 impl PersonAggregates {
   pub fn read(conn: &PgConnection, person_id: PersonId) -> Result<Self, Error> {
@@ -27,7 +13,6 @@ impl PersonAggregates {
 mod tests {
   use crate::{
     aggregates::person_aggregates::PersonAggregates,
-    establish_unpooled_connection,
     source::{
       comment::{Comment, CommentForm, CommentLike, CommentLikeForm},
       community::{Community, CommunityForm},
@@ -35,6 +20,7 @@ mod tests {
       post::{Post, PostForm, PostLike, PostLikeForm},
     },
     traits::{Crud, Likeable},
+    utils::establish_unpooled_connection,
   };
   use serial_test::serial;
 
diff --git a/crates/db_schema/src/aggregates/post_aggregates.rs b/crates/db_schema/src/aggregates/post_aggregates.rs
index cb8aba26..3d1a2729 100644
--- a/crates/db_schema/src/aggregates/post_aggregates.rs
+++ b/crates/db_schema/src/aggregates/post_aggregates.rs
@@ -1,23 +1,5 @@
-use crate::{newtypes::PostId, schema::post_aggregates};
+use crate::{aggregates::structs::PostAggregates, newtypes::PostId, schema::post_aggregates};
 use diesel::{result::Error, *};
-use serde::{Deserialize, Serialize};
-
-#[derive(
-  Queryable, Associations, Identifiable, PartialEq, Debug, Serialize, Deserialize, Clone,
-)]
-#[table_name = "post_aggregates"]
-pub struct PostAggregates {
-  pub id: i32,
-  pub post_id: PostId,
-  pub comments: i64,
-  pub score: i64,
-  pub upvotes: i64,
-  pub downvotes: i64,
-  pub stickied: bool,
-  pub published: chrono::NaiveDateTime,
-  pub newest_comment_time_necro: chrono::NaiveDateTime, // A newest comment time, limited to 2 days, to prevent necrobumping
-  pub newest_comment_time: chrono::NaiveDateTime,
-}
 
 impl PostAggregates {
   pub fn read(conn: &PgConnection, post_id: PostId) -> Result<Self, Error> {
@@ -31,7 +13,6 @@ impl PostAggregates {
 mod tests {
   use crate::{
     aggregates::post_aggregates::PostAggregates,
-    establish_unpooled_connection,
     source::{
       comment::{Comment, CommentForm},
       community::{Community, CommunityForm},
@@ -39,6 +20,7 @@ mod tests {
       post::{Post, PostForm, PostLike, PostLikeForm},
     },
     traits::{Crud, Likeable},
+    utils::establish_unpooled_connection,
   };
   use serial_test::serial;
 
diff --git a/crates/db_schema/src/aggregates/site_aggregates.rs b/crates/db_schema/src/aggregates/site_aggregates.rs
index 745097df..8cf57dfd 100644
--- a/crates/db_schema/src/aggregates/site_aggregates.rs
+++ b/crates/db_schema/src/aggregates/site_aggregates.rs
@@ -1,23 +1,5 @@
-use crate::schema::site_aggregates;
+use crate::{aggregates::structs::SiteAggregates, schema::site_aggregates};
 use diesel::{result::Error, *};
-use serde::{Deserialize, Serialize};
-
-#[derive(
-  Queryable, Associations, Identifiable, PartialEq, Debug, Serialize, Deserialize, Clone,
-)]
-#[table_name = "site_aggregates"]
-pub struct SiteAggregates {
-  pub id: i32,
-  pub site_id: i32,
-  pub users: i64,
-  pub posts: i64,
-  pub comments: i64,
-  pub communities: i64,
-  pub users_active_day: i64,
-  pub users_active_week: i64,
-  pub users_active_month: i64,
-  pub users_active_half_year: i64,
-}
 
 impl SiteAggregates {
   pub fn read(conn: &PgConnection) -> Result<Self, Error> {
@@ -29,7 +11,6 @@ impl SiteAggregates {
 mod tests {
   use crate::{
     aggregates::site_aggregates::SiteAggregates,
-    establish_unpooled_connection,
     source::{
       comment::{Comment, CommentForm},
       community::{Community, CommunityForm},
@@ -38,6 +19,7 @@ mod tests {
       site::{Site, SiteForm},
     },
     traits::Crud,
+    utils::establish_unpooled_connection,
   };
   use serial_test::serial;
 
diff --git a/crates/db_schema/src/aggregates/structs.rs b/crates/db_schema/src/aggregates/structs.rs
new file mode 100644
index 00000000..2f635b9e
--- /dev/null
+++ b/crates/db_schema/src/aggregates/structs.rs
@@ -0,0 +1,83 @@
+use crate::newtypes::{CommentId, CommunityId, PersonId, PostId};
+use serde::{Deserialize, Serialize};
+
+#[cfg(feature = "full")]
+use crate::schema::{
+  comment_aggregates,
+  community_aggregates,
+  person_aggregates,
+  post_aggregates,
+  site_aggregates,
+};
+
+#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
+#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable))]
+#[cfg_attr(feature = "full", table_name = "comment_aggregates")]
+pub struct CommentAggregates {
+  pub id: i32,
+  pub comment_id: CommentId,
+  pub score: i64,
+  pub upvotes: i64,
+  pub downvotes: i64,
+  pub published: chrono::NaiveDateTime,
+}
+
+#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
+#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable))]
+#[cfg_attr(feature = "full", table_name = "community_aggregates")]
+pub struct CommunityAggregates {
+  pub id: i32,
+  pub community_id: CommunityId,
+  pub subscribers: i64,
+  pub posts: i64,
+  pub comments: i64,
+  pub published: chrono::NaiveDateTime,
+  pub users_active_day: i64,
+  pub users_active_week: i64,
+  pub users_active_month: i64,
+  pub users_active_half_year: i64,
+}
+
+#[derive(PartialEq, Debug, Serialize, Deserialize, Clone, Default)]
+#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable))]
+#[cfg_attr(feature = "full", table_name = "person_aggregates")]
+pub struct PersonAggregates {
+  pub id: i32,
+  pub person_id: PersonId,
+  pub post_count: i64,
+  pub post_score: i64,
+  pub comment_count: i64,
+  pub comment_score: i64,
+}
+
+#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
+#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable))]
+#[cfg_attr(feature = "full", table_name = "post_aggregates")]
+pub struct PostAggregates {
+  pub id: i32,
+  pub post_id: PostId,
+  pub comments: i64,
+  pub score: i64,
+  pub upvotes: i64,
+  pub downvotes: i64,
+  pub stickied: bool,
+  pub published: chrono::NaiveDateTime,
+  pub newest_comment_time_necro: chrono::NaiveDateTime, // A newest comment time, limited to 2 days, to prevent necrobumping
+  pub newest_comment_time: chrono::NaiveDateTime,
+}
+
+#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
+#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable))]
+#[cfg_attr(feature = "full", table_name = "site_aggregates")]
+pub struct SiteAggregates {
+  pub id: i32,
+  pub site_id: i32,
+  pub users: i64,
+  pub posts: i64,
+  pub comments: i64,
+  pub communities: i64,
+  pub users_active_day: i64,
+  pub users_active_week: i64,
+  pub users_active_month: i64,
+  pub users_active_half_year: i64,
+}
diff --git a/crates/db_schema/src/impls/activity.rs b/crates/db_schema/src/impls/activity.rs
index 261a89cb..48429315 100644
--- a/crates/db_schema/src/impls/activity.rs
+++ b/crates/db_schema/src/impls/activity.rs
@@ -79,12 +79,12 @@ impl Activity {
 mod tests {
   use super::*;
   use crate::{
-    establish_unpooled_connection,
     newtypes::DbUrl,
     source::{
       activity::{Activity, ActivityForm},
       person::{Person, PersonForm},
     },
+    utils::establish_unpooled_connection,
   };
   use serde_json::Value;
   use serial_test::serial;
diff --git a/crates/db_schema/src/impls/comment.rs b/crates/db_schema/src/impls/comment.rs
index 09389aad..5fb3280c 100644
--- a/crates/db_schema/src/impls/comment.rs
+++ b/crates/db_schema/src/impls/comment.rs
@@ -1,5 +1,4 @@
 use crate::{
-  naive_now,
   newtypes::{CommentId, DbUrl, PersonId},
   source::comment::{
     Comment,
@@ -10,6 +9,7 @@ use crate::{
     CommentSavedForm,
   },
   traits::{Crud, DeleteableOrRemoveable, Likeable, Saveable},
+  utils::naive_now,
 };
 use diesel::{dsl::*, result::Error, *};
 use url::Url;
@@ -209,7 +209,6 @@ impl DeleteableOrRemoveable for Comment {
 #[cfg(test)]
 mod tests {
   use crate::{
-    establish_unpooled_connection,
     source::{
       comment::*,
       community::{Community, CommunityForm},
@@ -217,6 +216,7 @@ mod tests {
       post::*,
     },
     traits::{Crud, Likeable, Saveable},
+    utils::establish_unpooled_connection,
   };
   use serial_test::serial;
 
diff --git a/crates/db_schema/src/impls/comment_report.rs b/crates/db_schema/src/impls/comment_report.rs
index d32f3ef4..56e917e0 100644
--- a/crates/db_schema/src/impls/comment_report.rs
+++ b/crates/db_schema/src/impls/comment_report.rs
@@ -1,8 +1,8 @@
 use crate::{
-  naive_now,
   newtypes::{CommentReportId, PersonId},
   source::comment_report::{CommentReport, CommentReportForm},
   traits::Reportable,
+  utils::naive_now,
 };
 use diesel::{dsl::*, result::Error, *};
 
diff --git a/crates/db_schema/src/impls/community.rs b/crates/db_schema/src/impls/community.rs
index fb8bd6e4..14a670f5 100644
--- a/crates/db_schema/src/impls/community.rs
+++ b/crates/db_schema/src/impls/community.rs
@@ -1,6 +1,4 @@
 use crate::{
-  functions::lower,
-  naive_now,
   newtypes::{CommunityId, DbUrl, PersonId},
   source::community::{
     Community,
@@ -14,6 +12,7 @@ use crate::{
     CommunitySafe,
   },
   traits::{ApubActor, Bannable, Crud, DeleteableOrRemoveable, Followable, Joinable},
+  utils::{functions::lower, naive_now},
 };
 use diesel::{
   dsl::*,
@@ -327,9 +326,9 @@ impl ApubActor for Community {
 #[cfg(test)]
 mod tests {
   use crate::{
-    establish_unpooled_connection,
     source::{community::*, person::*},
     traits::{Bannable, Crud, Followable, Joinable},
+    utils::establish_unpooled_connection,
   };
   use serial_test::serial;
 
diff --git a/crates/db_schema/src/impls/local_user.rs b/crates/db_schema/src/impls/local_user.rs
index 833d6bdb..419ab792 100644
--- a/crates/db_schema/src/impls/local_user.rs
+++ b/crates/db_schema/src/impls/local_user.rs
@@ -1,9 +1,9 @@
 use crate::{
-  naive_now,
   newtypes::LocalUserId,
   schema::local_user::dsl::*,
   source::local_user::{LocalUser, LocalUserForm},
   traits::Crud,
+  utils::naive_now,
 };
 use bcrypt::{hash, DEFAULT_COST};
 use diesel::{dsl::*, result::Error, *};
diff --git a/crates/db_schema/src/impls/moderator.rs b/crates/db_schema/src/impls/moderator.rs
index 696dbe0a..36582d10 100644
--- a/crates/db_schema/src/impls/moderator.rs
+++ b/crates/db_schema/src/impls/moderator.rs
@@ -266,9 +266,9 @@ impl Crud for ModAdd {
 #[cfg(test)]
 mod tests {
   use crate::{
-    establish_unpooled_connection,
     source::{comment::*, community::*, moderator::*, person::*, post::*},
     traits::Crud,
+    utils::establish_unpooled_connection,
   };
   use serial_test::serial;
 
diff --git a/crates/db_schema/src/impls/password_reset_request.rs b/crates/db_schema/src/impls/password_reset_request.rs
index 808f0ac0..576e2673 100644
--- a/crates/db_schema/src/impls/password_reset_request.rs
+++ b/crates/db_schema/src/impls/password_reset_request.rs
@@ -70,13 +70,13 @@ fn bytes_to_hex(bytes: Vec<u8>) -> String {
 #[cfg(test)]
 mod tests {
   use crate::{
-    establish_unpooled_connection,
     source::{
       local_user::{LocalUser, LocalUserForm},
       password_reset_request::PasswordResetRequest,
       person::*,
     },
     traits::Crud,
+    utils::establish_unpooled_connection,
   };
   use serial_test::serial;
 
diff --git a/crates/db_schema/src/impls/person.rs b/crates/db_schema/src/impls/person.rs
index 165d9308..c59126c3 100644
--- a/crates/db_schema/src/impls/person.rs
+++ b/crates/db_schema/src/impls/person.rs
@@ -1,10 +1,9 @@
 use crate::{
-  functions::lower,
-  naive_now,
   newtypes::{DbUrl, PersonId},
   schema::person::dsl::*,
   source::person::{Person, PersonForm, PersonSafe},
   traits::{ApubActor, Crud},
+  utils::{functions::lower, naive_now},
 };
 use diesel::{
   dsl::*,
@@ -318,7 +317,7 @@ impl ApubActor for Person {
 
 #[cfg(test)]
 mod tests {
-  use crate::{establish_unpooled_connection, source::person::*, traits::Crud};
+  use crate::{source::person::*, traits::Crud, utils::establish_unpooled_connection};
 
   #[test]
   fn test_crud() {
diff --git a/crates/db_schema/src/impls/person_mention.rs b/crates/db_schema/src/impls/person_mention.rs
index df0e55ed..c8fb6cc0 100644
--- a/crates/db_schema/src/impls/person_mention.rs
+++ b/crates/db_schema/src/impls/person_mention.rs
@@ -78,7 +78,6 @@ impl PersonMention {
 #[cfg(test)]
 mod tests {
   use crate::{
-    establish_unpooled_connection,
     source::{
       comment::*,
       community::{Community, CommunityForm},
@@ -87,6 +86,7 @@ mod tests {
       post::*,
     },
     traits::Crud,
+    utils::establish_unpooled_connection,
   };
   use serial_test::serial;
 
diff --git a/crates/db_schema/src/impls/post.rs b/crates/db_schema/src/impls/post.rs
index 9a8bbcec..c878931a 100644
--- a/crates/db_schema/src/impls/post.rs
+++ b/crates/db_schema/src/impls/post.rs
@@ -1,5 +1,4 @@
 use crate::{
-  naive_now,
   newtypes::{CommunityId, DbUrl, PersonId, PostId},
   source::post::{
     Post,
@@ -12,6 +11,7 @@ use crate::{
     PostSavedForm,
   },
   traits::{Crud, DeleteableOrRemoveable, Likeable, Readable, Saveable},
+  utils::naive_now,
 };
 use diesel::{dsl::*, result::Error, ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl};
 use url::Url;
@@ -261,13 +261,13 @@ impl DeleteableOrRemoveable for Post {
 #[cfg(test)]
 mod tests {
   use crate::{
-    establish_unpooled_connection,
     source::{
       community::{Community, CommunityForm},
       person::*,
       post::*,
     },
     traits::{Crud, Likeable, Readable, Saveable},
+    utils::establish_unpooled_connection,
   };
   use serial_test::serial;
 
diff --git a/crates/db_schema/src/impls/post_report.rs b/crates/db_schema/src/impls/post_report.rs
index d049fbce..58e2f44b 100644
--- a/crates/db_schema/src/impls/post_report.rs
+++ b/crates/db_schema/src/impls/post_report.rs
@@ -1,8 +1,8 @@
 use crate::{
-  naive_now,
   newtypes::{PersonId, PostReportId},
   source::post_report::*,
   traits::Reportable,
+  utils::naive_now,
 };
 use diesel::{dsl::*, result::Error, *};
 
diff --git a/crates/db_schema/src/impls/private_message.rs b/crates/db_schema/src/impls/private_message.rs
index 6a2e9ee8..e90ae524 100644
--- a/crates/db_schema/src/impls/private_message.rs
+++ b/crates/db_schema/src/impls/private_message.rs
@@ -1,8 +1,8 @@
 use crate::{
-  naive_now,
   newtypes::{DbUrl, PersonId, PrivateMessageId},
   source::private_message::*,
   traits::{Crud, DeleteableOrRemoveable},
+  utils::naive_now,
 };
 use diesel::{dsl::*, result::Error, *};
 use lemmy_utils::LemmyError;
@@ -138,9 +138,9 @@ impl DeleteableOrRemoveable for PrivateMessage {
 #[cfg(test)]
 mod tests {
   use crate::{
-    establish_unpooled_connection,
     source::{person::*, private_message::*},
     traits::Crud,
+    utils::establish_unpooled_connection,
   };
   use serial_test::serial;
 
diff --git a/crates/db_schema/src/impls/site.rs b/crates/db_schema/src/impls/site.rs
index 60d2c013..6c71f076 100644
--- a/crates/db_schema/src/impls/site.rs
+++ b/crates/db_schema/src/impls/site.rs
@@ -1,4 +1,4 @@
-use crate::{source::site::*, traits::Crud, DbUrl};
+use crate::{newtypes::DbUrl, source::site::*, traits::Crud};
 use diesel::{dsl::*, result::Error, *};
 use url::Url;
 
diff --git a/crates/db_schema/src/lib.rs b/crates/db_schema/src/lib.rs
index bd6e0d19..e6dee199 100644
--- a/crates/db_schema/src/lib.rs
+++ b/crates/db_schema/src/lib.rs
@@ -1,197 +1,26 @@
+#[cfg(feature = "full")]
+#[macro_use]
+extern crate strum_macros;
+#[cfg(feature = "full")]
 #[macro_use]
 extern crate diesel;
+#[cfg(feature = "full")]
 #[macro_use]
 extern crate diesel_derive_newtype;
 // this is used in tests
+#[cfg(feature = "full")]
 #[allow(unused_imports)]
 #[macro_use]
 extern crate diesel_migrations;
-#[macro_use]
-extern crate strum_macros;
 
 pub mod aggregates;
+#[cfg(feature = "full")]
 pub mod impls;
 pub mod newtypes;
+#[cfg(feature = "full")]
 pub mod schema;
 pub mod source;
+#[cfg(feature = "full")]
 pub mod traits;
-
-pub type DbPool = diesel::r2d2::Pool<diesel::r2d2::ConnectionManager<diesel::PgConnection>>;
-
-use crate::newtypes::DbUrl;
-use chrono::NaiveDateTime;
-use diesel::{Connection, PgConnection};
-use lemmy_utils::LemmyError;
-use once_cell::sync::Lazy;
-use regex::Regex;
-use serde::{Deserialize, Serialize};
-use std::{env, env::VarError};
-use url::Url;
-
-pub fn get_database_url_from_env() -> Result<String, VarError> {
-  env::var("LEMMY_DATABASE_URL")
-}
-
-#[derive(EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy)]
-pub enum SortType {
-  Active,
-  Hot,
-  New,
-  TopDay,
-  TopWeek,
-  TopMonth,
-  TopYear,
-  TopAll,
-  MostComments,
-  NewComments,
-}
-
-#[derive(EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy, PartialEq)]
-pub enum ListingType {
-  All,
-  Local,
-  Subscribed,
-  Community,
-}
-
-#[derive(EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy)]
-pub enum SearchType {
-  All,
-  Comments,
-  Posts,
-  Communities,
-  Users,
-  Url,
-}
-
-pub fn from_opt_str_to_opt_enum<T: std::str::FromStr>(opt: &Option<String>) -> Option<T> {
-  opt.as_ref().and_then(|t| T::from_str(t).ok())
-}
-
-pub fn fuzzy_search(q: &str) -> String {
-  let replaced = q.replace('%', "\\%").replace('_', "\\_").replace(' ', "%");
-  format!("%{}%", replaced)
-}
-
-pub fn limit_and_offset(page: Option<i64>, limit: Option<i64>) -> (i64, i64) {
-  let page = page.unwrap_or(1);
-  let limit = limit.unwrap_or(10);
-  let offset = limit * (page - 1);
-  (limit, offset)
-}
-
-pub fn is_email_regex(test: &str) -> bool {
-  EMAIL_REGEX.is_match(test)
-}
-
-pub fn diesel_option_overwrite(opt: &Option<String>) -> Option<Option<String>> {
-  match opt {
-    // An empty string is an erase
-    Some(unwrapped) => {
-      if !unwrapped.eq("") {
-        Some(Some(unwrapped.to_owned()))
-      } else {
-        Some(None)
-      }
-    }
-    None => None,
-  }
-}
-
-pub fn diesel_option_overwrite_to_url(
-  opt: &Option<String>,
-) -> Result<Option<Option<DbUrl>>, LemmyError> {
-  match opt.as_ref().map(|s| s.as_str()) {
-    // An empty string is an erase
-    Some("") => Ok(Some(None)),
-    Some(str_url) => match Url::parse(str_url) {
-      Ok(url) => Ok(Some(Some(url.into()))),
-      Err(e) => Err(LemmyError::from_error_message(e, "invalid_url")),
-    },
-    None => Ok(None),
-  }
-}
-
-embed_migrations!();
-
-pub fn establish_unpooled_connection() -> PgConnection {
-  let db_url = match get_database_url_from_env() {
-    Ok(url) => url,
-    Err(e) => panic!(
-      "Failed to read database URL from env var LEMMY_DATABASE_URL: {}",
-      e
-    ),
-  };
-  let conn =
-    PgConnection::establish(&db_url).unwrap_or_else(|_| panic!("Error connecting to {}", db_url));
-  embedded_migrations::run(&conn).expect("load migrations");
-  conn
-}
-
-pub fn naive_now() -> NaiveDateTime {
-  chrono::prelude::Utc::now().naive_utc()
-}
-
-static EMAIL_REGEX: Lazy<Regex> = Lazy::new(|| {
-  Regex::new(r"^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$")
-    .expect("compile email regex")
-});
-
-pub mod functions {
-  use diesel::sql_types::*;
-
-  sql_function! {
-    fn hot_rank(score: BigInt, time: Timestamp) -> Integer;
-  }
-
-  sql_function!(fn lower(x: Text) -> Text);
-}
-
-#[cfg(test)]
-mod tests {
-  use super::{fuzzy_search, *};
-  use crate::is_email_regex;
-
-  #[test]
-  fn test_fuzzy_search() {
-    let test = "This %is% _a_ fuzzy search";
-    assert_eq!(
-      fuzzy_search(test),
-      "%This%\\%is\\%%\\_a\\_%fuzzy%search%".to_string()
-    );
-  }
-
-  #[test]
-  fn test_email() {
-    assert!(is_email_regex("gush@gmail.com"));
-    assert!(!is_email_regex("nada_neutho"));
-  }
-
-  #[test]
-  fn test_diesel_option_overwrite() {
-    assert_eq!(diesel_option_overwrite(&None), None);
-    assert_eq!(diesel_option_overwrite(&Some("".to_string())), Some(None));
-    assert_eq!(
-      diesel_option_overwrite(&Some("test".to_string())),
-      Some(Some("test".to_string()))
-    );
-  }
-
-  #[test]
-  fn test_diesel_option_overwrite_to_url() {
-    assert!(matches!(diesel_option_overwrite_to_url(&None), Ok(None)));
-    assert!(matches!(
-      diesel_option_overwrite_to_url(&Some("".to_string())),
-      Ok(Some(None))
-    ));
-    assert!(matches!(
-      diesel_option_overwrite_to_url(&Some("invalid_url".to_string())),
-      Err(_)
-    ));
-    let example_url = "https://example.com";
-    assert!(matches!(
-      diesel_option_overwrite_to_url(&Some(example_url.to_string())),
-      Ok(Some(Some(url))) if url == Url::parse(example_url).unwrap().into()
-    ));
-  }
-}
+#[cfg(feature = "full")]
+pub mod utils;
diff --git a/crates/db_schema/src/newtypes.rs b/crates/db_schema/src/newtypes.rs
index 3e68768b..946007a3 100644
--- a/crates/db_schema/src/newtypes.rs
+++ b/crates/db_schema/src/newtypes.rs
@@ -1,22 +1,13 @@
-use diesel::{
-  backend::Backend,
-  deserialize::FromSql,
-  serialize::{Output, ToSql},
-  sql_types::Text,
-};
-use lemmy_apub_lib::{object_id::ObjectId, traits::ApubObject};
 use serde::{Deserialize, Serialize};
 use std::{
   fmt,
   fmt::{Display, Formatter},
-  io::Write,
   ops::Deref,
 };
 use url::Url;
 
-#[derive(
-  Debug, Copy, Clone, Hash, Eq, PartialEq, Default, Serialize, Deserialize, DieselNewType,
-)]
+#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Default, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(DieselNewType))]
 pub struct PostId(pub i32);
 
 impl fmt::Display for PostId {
@@ -25,12 +16,12 @@ impl fmt::Display for PostId {
   }
 }
 
-#[derive(
-  Debug, Copy, Clone, Hash, Eq, PartialEq, Default, Serialize, Deserialize, DieselNewType,
-)]
+#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Default, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(DieselNewType))]
 pub struct PersonId(pub i32);
 
-#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, DieselNewType)]
+#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(DieselNewType))]
 pub struct CommentId(pub i32);
 
 impl fmt::Display for CommentId {
@@ -39,17 +30,16 @@ impl fmt::Display for CommentId {
   }
 }
 
-#[derive(
-  Debug, Copy, Clone, Hash, Eq, PartialEq, Default, Serialize, Deserialize, DieselNewType,
-)]
+#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Default, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(DieselNewType))]
 pub struct CommunityId(pub i32);
 
-#[derive(
-  Debug, Copy, Clone, Hash, Eq, PartialEq, Default, Serialize, Deserialize, DieselNewType,
-)]
+#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Default, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(DieselNewType))]
 pub struct LocalUserId(pub i32);
 
-#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, DieselNewType)]
+#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(DieselNewType))]
 pub struct PrivateMessageId(i32);
 
 impl fmt::Display for PrivateMessageId {
@@ -58,44 +48,31 @@ impl fmt::Display for PrivateMessageId {
   }
 }
 
-#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, DieselNewType)]
+#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(DieselNewType))]
 pub struct PersonMentionId(i32);
 
-#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, DieselNewType)]
+#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(DieselNewType))]
 pub struct PersonBlockId(i32);
 
-#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, DieselNewType)]
+#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(DieselNewType))]
 pub struct CommunityBlockId(i32);
 
-#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, DieselNewType)]
+#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(DieselNewType))]
 pub struct CommentReportId(i32);
 
-#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, DieselNewType)]
+#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(DieselNewType))]
 pub struct PostReportId(i32);
 
 #[repr(transparent)]
-#[derive(Clone, PartialEq, Serialize, Deserialize, Debug, AsExpression, FromSqlRow)]
-#[sql_type = "Text"]
-pub struct DbUrl(Url);
-
-impl<DB: Backend> ToSql<Text, DB> for DbUrl
-where
-  String: ToSql<Text, DB>,
-{
-  fn to_sql<W: Write>(&self, out: &mut Output<W, DB>) -> diesel::serialize::Result {
-    self.0.to_string().to_sql(out)
-  }
-}
-
-impl<DB: Backend> FromSql<Text, DB> for DbUrl
-where
-  String: FromSql<Text, DB>,
-{
-  fn from_sql(bytes: Option<&DB::RawValue>) -> diesel::deserialize::Result<Self> {
-    let str = String::from_sql(bytes)?;
-    Ok(DbUrl(Url::parse(&str)?))
-  }
-}
+#[derive(Clone, PartialEq, Serialize, Deserialize, Debug)]
+#[cfg_attr(feature = "full", derive(AsExpression, FromSqlRow))]
+#[cfg_attr(feature = "full", sql_type = "diesel::sql_types::Text")]
+pub struct DbUrl(pub(crate) Url);
 
 impl Display for DbUrl {
   fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
@@ -117,16 +94,6 @@ impl Into<Url> for DbUrl {
   }
 }
 
-impl<Kind> From<ObjectId<Kind>> for DbUrl
-where
-  Kind: ApubObject + Send + 'static,
-  for<'de2> <Kind as ApubObject>::ApubType: serde::Deserialize<'de2>,
-{
-  fn from(id: ObjectId<Kind>) -> Self {
-    DbUrl(id.into())
-  }
-}
-
 impl Deref for DbUrl {
   type Target = Url;
 
diff --git a/crates/db_schema/src/source/activity.rs b/crates/db_schema/src/source/activity.rs
index 5bdeb1db..5d09ebfd 100644
--- a/crates/db_schema/src/source/activity.rs
+++ b/crates/db_schema/src/source/activity.rs
@@ -2,7 +2,7 @@ use crate::{newtypes::DbUrl, schema::activity};
 use serde_json::Value;
 use std::fmt::Debug;
 
-#[derive(Queryable, Identifiable, PartialEq, Debug)]
+#[derive(PartialEq, Debug, Queryable, Identifiable)]
 #[table_name = "activity"]
 pub struct Activity {
   pub id: i32,
diff --git a/crates/db_schema/src/source/comment.rs b/crates/db_schema/src/source/comment.rs
index 46242fc7..33fd72c5 100644
--- a/crates/db_schema/src/source/comment.rs
+++ b/crates/db_schema/src/source/comment.rs
@@ -1,10 +1,9 @@
-use crate::{
-  newtypes::{CommentId, DbUrl, PersonId, PostId},
-  schema::{comment, comment_alias_1, comment_like, comment_saved},
-  source::post::Post,
-};
+use crate::newtypes::{CommentId, DbUrl, PersonId, PostId};
 use serde::{Deserialize, Serialize};
 
+#[cfg(feature = "full")]
+use crate::schema::{comment, comment_alias_1, comment_like, comment_saved};
+
 // WITH RECURSIVE MyTree AS (
 //     SELECT * FROM comment WHERE parent_id IS NULL
 //     UNION ALL
@@ -12,11 +11,10 @@ use serde::{Deserialize, Serialize};
 // )
 // SELECT * FROM MyTree;
 
-#[derive(
-  Clone, Queryable, Associations, Identifiable, PartialEq, Debug, Serialize, Deserialize,
-)]
-#[belongs_to(Post)]
-#[table_name = "comment"]
+#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable))]
+#[cfg_attr(feature = "full", belongs_to(crate::source::post::Post))]
+#[cfg_attr(feature = "full", table_name = "comment")]
 pub struct Comment {
   pub id: CommentId,
   pub creator_id: PersonId,
@@ -32,11 +30,10 @@ pub struct Comment {
   pub local: bool,
 }
 
-#[derive(
-  Clone, Queryable, Associations, Identifiable, PartialEq, Debug, Serialize, Deserialize,
-)]
-#[belongs_to(Post)]
-#[table_name = "comment_alias_1"]
+#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable))]
+#[cfg_attr(feature = "full", belongs_to(crate::source::post::Post))]
+#[cfg_attr(feature = "full", table_name = "comment_alias_1")]
 pub struct CommentAlias1 {
   pub id: CommentId,
   pub creator_id: PersonId,
@@ -52,8 +49,9 @@ pub struct CommentAlias1 {
   pub local: bool,
 }
 
-#[derive(Insertable, AsChangeset, Clone, Default)]
-#[table_name = "comment"]
+#[derive(Clone, Default)]
+#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
+#[cfg_attr(feature = "full", table_name = "comment")]
 pub struct CommentForm {
   pub creator_id: PersonId,
   pub post_id: PostId,
@@ -68,9 +66,10 @@ pub struct CommentForm {
   pub local: Option<bool>,
 }
 
-#[derive(Identifiable, Queryable, Associations, PartialEq, Debug, Clone)]
-#[belongs_to(Comment)]
-#[table_name = "comment_like"]
+#[derive(PartialEq, Debug, Clone)]
+#[cfg_attr(feature = "full", derive(Identifiable, Queryable, Associations))]
+#[cfg_attr(feature = "full", belongs_to(Comment))]
+#[cfg_attr(feature = "full", table_name = "comment_like")]
 pub struct CommentLike {
   pub id: i32,
   pub person_id: PersonId,
@@ -80,8 +79,9 @@ pub struct CommentLike {
   pub published: chrono::NaiveDateTime,
 }
 
-#[derive(Insertable, AsChangeset, Clone)]
-#[table_name = "comment_like"]
+#[derive(Clone)]
+#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
+#[cfg_attr(feature = "full", table_name = "comment_like")]
 pub struct CommentLikeForm {
   pub person_id: PersonId,
   pub comment_id: CommentId,
@@ -89,9 +89,10 @@ pub struct CommentLikeForm {
   pub score: i16,
 }
 
-#[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]
-#[belongs_to(Comment)]
-#[table_name = "comment_saved"]
+#[derive(PartialEq, Debug)]
+#[cfg_attr(feature = "full", derive(Identifiable, Queryable, Associations))]
+#[cfg_attr(feature = "full", belongs_to(Comment))]
+#[cfg_attr(feature = "full", table_name = "comment_saved")]
 pub struct CommentSaved {
   pub id: i32,
   pub comment_id: CommentId,
@@ -99,8 +100,8 @@ pub struct CommentSaved {
   pub published: chrono::NaiveDateTime,
 }
 
-#[derive(Insertable, AsChangeset)]
-#[table_name = "comment_saved"]
+#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
+#[cfg_attr(feature = "full", table_name = "comment_saved")]
 pub struct CommentSavedForm {
   pub comment_id: CommentId,
   pub person_id: PersonId,
diff --git a/crates/db_schema/src/source/comment_report.rs b/crates/db_schema/src/source/comment_report.rs
index 3c265ed0..25689576 100644
--- a/crates/db_schema/src/source/comment_report.rs
+++ b/crates/db_schema/src/source/comment_report.rs
@@ -1,15 +1,13 @@
-use crate::{
-  newtypes::{CommentId, CommentReportId, PersonId},
-  schema::comment_report,
-  source::comment::Comment,
-};
+use crate::newtypes::{CommentId, CommentReportId, PersonId};
 use serde::{Deserialize, Serialize};
 
-#[derive(
-  Identifiable, Queryable, Associations, PartialEq, Serialize, Deserialize, Debug, Clone,
-)]
-#[belongs_to(Comment)]
-#[table_name = "comment_report"]
+#[cfg(feature = "full")]
+use crate::schema::comment_report;
+
+#[derive(PartialEq, Serialize, Deserialize, Debug, Clone)]
+#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable))]
+#[cfg_attr(feature = "full", belongs_to(crate::source::comment::Comment))]
+#[cfg_attr(feature = "full", table_name = "comment_report")]
 pub struct CommentReport {
   pub id: CommentReportId,
   pub creator_id: PersonId,
@@ -22,8 +20,9 @@ pub struct CommentReport {
   pub updated: Option<chrono::NaiveDateTime>,
 }
 
-#[derive(Insertable, AsChangeset, Clone)]
-#[table_name = "comment_report"]
+#[derive(Clone)]
+#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
+#[cfg_attr(feature = "full", table_name = "comment_report")]
 pub struct CommentReportForm {
   pub creator_id: PersonId,
   pub comment_id: CommentId,
diff --git a/crates/db_schema/src/source/community.rs b/crates/db_schema/src/source/community.rs
index 3e8bbf17..7693f2f4 100644
--- a/crates/db_schema/src/source/community.rs
+++ b/crates/db_schema/src/source/community.rs
@@ -1,11 +1,12 @@
-use crate::{
-  newtypes::{CommunityId, DbUrl, PersonId},
-  schema::{community, community_follower, community_moderator, community_person_ban},
-};
+use crate::newtypes::{CommunityId, DbUrl, PersonId};
 use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
-#[table_name = "community"]
+#[cfg(feature = "full")]
+use crate::schema::{community, community_follower, community_moderator, community_person_ban};
+
+#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
+#[cfg_attr(feature = "full", table_name = "community")]
 pub struct Community {
   pub id: CommunityId,
   pub name: String,
@@ -31,8 +32,9 @@ pub struct Community {
 }
 
 /// A safe representation of community, without the sensitive info
-#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
-#[table_name = "community"]
+#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
+#[cfg_attr(feature = "full", table_name = "community")]
 pub struct CommunitySafe {
   pub id: CommunityId,
   pub name: String,
@@ -51,8 +53,9 @@ pub struct CommunitySafe {
   pub posting_restricted_to_mods: bool,
 }
 
-#[derive(Insertable, AsChangeset, Debug, Default)]
-#[table_name = "community"]
+#[derive(Debug, Default)]
+#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
+#[cfg_attr(feature = "full", table_name = "community")]
 pub struct CommunityForm {
   pub name: String,
   pub title: String,
@@ -76,9 +79,10 @@ pub struct CommunityForm {
   pub posting_restricted_to_mods: Option<bool>,
 }
 
-#[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]
-#[belongs_to(Community)]
-#[table_name = "community_moderator"]
+#[derive(PartialEq, Debug)]
+#[cfg_attr(feature = "full", derive(Identifiable, Queryable, Associations))]
+#[cfg_attr(feature = "full", belongs_to(Community))]
+#[cfg_attr(feature = "full", table_name = "community_moderator")]
 pub struct CommunityModerator {
   pub id: i32,
   pub community_id: CommunityId,
@@ -86,16 +90,18 @@ pub struct CommunityModerator {
   pub published: chrono::NaiveDateTime,
 }
 
-#[derive(Insertable, AsChangeset, Clone)]
-#[table_name = "community_moderator"]
+#[derive(Clone)]
+#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
+#[cfg_attr(feature = "full", table_name = "community_moderator")]
 pub struct CommunityModeratorForm {
   pub community_id: CommunityId,
   pub person_id: PersonId,
 }
 
-#[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]
-#[belongs_to(Community)]
-#[table_name = "community_person_ban"]
+#[derive(PartialEq, Debug)]
+#[cfg_attr(feature = "full", derive(Identifiable, Queryable, Associations))]
+#[cfg_attr(feature = "full", belongs_to(Community))]
+#[cfg_attr(feature = "full", table_name = "community_person_ban")]
 pub struct CommunityPersonBan {
   pub id: i32,
   pub community_id: CommunityId,
@@ -104,17 +110,19 @@ pub struct CommunityPersonBan {
   pub expires: Option<chrono::NaiveDateTime>,
 }
 
-#[derive(Insertable, AsChangeset, Clone)]
-#[table_name = "community_person_ban"]
+#[derive(Clone)]
+#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
+#[cfg_attr(feature = "full", table_name = "community_person_ban")]
 pub struct CommunityPersonBanForm {
   pub community_id: CommunityId,
   pub person_id: PersonId,
   pub expires: Option<Option<chrono::NaiveDateTime>>,
 }
 
-#[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]
-#[belongs_to(Community)]
-#[table_name = "community_follower"]
+#[derive(PartialEq, Debug)]
+#[cfg_attr(feature = "full", derive(Identifiable, Queryable, Associations))]
+#[cfg_attr(feature = "full", belongs_to(Community))]
+#[cfg_attr(feature = "full", table_name = "community_follower")]
 pub struct CommunityFollower {
   pub id: i32,
   pub community_id: CommunityId,
@@ -123,8 +131,9 @@ pub struct CommunityFollower {
   pub pending: Option<bool>,
 }
 
-#[derive(Insertable, AsChangeset, Clone)]
-#[table_name = "community_follower"]
+#[derive(Clone)]
+#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
+#[cfg_attr(feature = "full", table_name = "community_follower")]
 pub struct CommunityFollowerForm {
   pub community_id: CommunityId,
   pub person_id: PersonId,
diff --git a/crates/db_schema/src/source/community_block.rs b/crates/db_schema/src/source/community_block.rs
index f5cc545e..d2c40a33 100644
--- a/crates/db_schema/src/source/community_block.rs
+++ b/crates/db_schema/src/source/community_block.rs
@@ -1,15 +1,13 @@
-use crate::{
-  newtypes::{CommunityBlockId, CommunityId, PersonId},
-  schema::community_block,
-  source::community::Community,
-};
+use crate::newtypes::{CommunityBlockId, CommunityId, PersonId};
 use serde::{Deserialize, Serialize};
 
-#[derive(
-  Clone, Queryable, Associations, Identifiable, PartialEq, Debug, Serialize, Deserialize,
-)]
-#[table_name = "community_block"]
-#[belongs_to(Community)]
+#[cfg(feature = "full")]
+use crate::schema::community_block;
+
+#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable))]
+#[cfg_attr(feature = "full", belongs_to(crate::source::community::Community))]
+#[cfg_attr(feature = "full", table_name = "community_block")]
 pub struct CommunityBlock {
   pub id: CommunityBlockId,
   pub person_id: PersonId,
@@ -17,8 +15,8 @@ pub struct CommunityBlock {
   pub published: chrono::NaiveDateTime,
 }
 
-#[derive(Insertable, AsChangeset)]
-#[table_name = "community_block"]
+#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
+#[cfg_attr(feature = "full", table_name = "community_block")]
 pub struct CommunityBlockForm {
   pub person_id: PersonId,
   pub community_id: CommunityId,
diff --git a/crates/db_schema/src/source/email_verification.rs b/crates/db_schema/src/source/email_verification.rs
index e36f2901..8d63670f 100644
--- a/crates/db_schema/src/source/email_verification.rs
+++ b/crates/db_schema/src/source/email_verification.rs
@@ -1,7 +1,11 @@
-use crate::{newtypes::LocalUserId, schema::email_verification};
+use crate::newtypes::LocalUserId;
 
-#[derive(Queryable, Identifiable, Clone)]
-#[table_name = "email_verification"]
+#[cfg(feature = "full")]
+use crate::schema::email_verification;
+
+#[derive(Clone)]
+#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
+#[cfg_attr(feature = "full", table_name = "email_verification")]
 pub struct EmailVerification {
   pub id: i32,
   pub local_user_id: LocalUserId,
@@ -10,8 +14,8 @@ pub struct EmailVerification {
   pub published: chrono::NaiveDateTime,
 }
 
-#[derive(Insertable, AsChangeset)]
-#[table_name = "email_verification"]
+#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
+#[cfg_attr(feature = "full", table_name = "email_verification")]
 pub struct EmailVerificationForm {
   pub local_user_id: LocalUserId,
   pub email: String,
diff --git a/crates/db_schema/src/source/local_user.rs b/crates/db_schema/src/source/local_user.rs
index 88defa6d..57767bb1 100644
--- a/crates/db_schema/src/source/local_user.rs
+++ b/crates/db_schema/src/source/local_user.rs
@@ -1,11 +1,12 @@
-use crate::{
-  newtypes::{LocalUserId, PersonId},
-  schema::local_user,
-};
+use crate::newtypes::{LocalUserId, PersonId};
 use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
-#[table_name = "local_user"]
+#[cfg(feature = "full")]
+use crate::schema::local_user;
+
+#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
+#[cfg_attr(feature = "full", table_name = "local_user")]
 pub struct LocalUser {
   pub id: LocalUserId,
   pub person_id: PersonId,
@@ -28,8 +29,9 @@ pub struct LocalUser {
 }
 
 // TODO redo these, check table defaults
-#[derive(Insertable, AsChangeset, Clone, Default)]
-#[table_name = "local_user"]
+#[derive(Clone, Default)]
+#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
+#[cfg_attr(feature = "full", table_name = "local_user")]
 pub struct LocalUserForm {
   pub person_id: Option<PersonId>,
   pub password_encrypted: Option<String>,
@@ -50,8 +52,9 @@ pub struct LocalUserForm {
 }
 
 /// A local user view that removes password encrypted
-#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
-#[table_name = "local_user"]
+#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
+#[cfg_attr(feature = "full", table_name = "local_user")]
 pub struct LocalUserSettings {
   pub id: LocalUserId,
   pub person_id: PersonId,
diff --git a/crates/db_schema/src/source/mod.rs b/crates/db_schema/src/source/mod.rs
index c96e3a62..f2ff5f78 100644
--- a/crates/db_schema/src/source/mod.rs
+++ b/crates/db_schema/src/source/mod.rs
@@ -1,3 +1,4 @@
+#[cfg(feature = "full")]
 pub mod activity;
 pub mod comment;
 pub mod comment_report;
diff --git a/crates/db_schema/src/source/moderator.rs b/crates/db_schema/src/source/moderator.rs
index 84121f9a..b617564b 100644
--- a/crates/db_schema/src/source/moderator.rs
+++ b/crates/db_schema/src/source/moderator.rs
@@ -1,23 +1,24 @@
-use crate::{
-  newtypes::{CommentId, CommunityId, PersonId, PostId},
-  schema::{
-    mod_add,
-    mod_add_community,
-    mod_ban,
-    mod_ban_from_community,
-    mod_hide_community,
-    mod_lock_post,
-    mod_remove_comment,
-    mod_remove_community,
-    mod_remove_post,
-    mod_sticky_post,
-    mod_transfer_community,
-  },
-};
+use crate::newtypes::{CommentId, CommunityId, PersonId, PostId};
 use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
-#[table_name = "mod_remove_post"]
+#[cfg(feature = "full")]
+use crate::schema::{
+  mod_add,
+  mod_add_community,
+  mod_ban,
+  mod_ban_from_community,
+  mod_hide_community,
+  mod_lock_post,
+  mod_remove_comment,
+  mod_remove_community,
+  mod_remove_post,
+  mod_sticky_post,
+  mod_transfer_community,
+};
+
+#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
+#[cfg_attr(feature = "full", table_name = "mod_remove_post")]
 pub struct ModRemovePost {
   pub id: i32,
   pub mod_person_id: PersonId,
@@ -27,8 +28,8 @@ pub struct ModRemovePost {
   pub when_: chrono::NaiveDateTime,
 }
 
-#[derive(Insertable, AsChangeset)]
-#[table_name = "mod_remove_post"]
+#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
+#[cfg_attr(feature = "full", table_name = "mod_remove_post")]
 pub struct ModRemovePostForm {
   pub mod_person_id: PersonId,
   pub post_id: PostId,
@@ -36,8 +37,9 @@ pub struct ModRemovePostForm {
   pub removed: Option<bool>,
 }
 
-#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
-#[table_name = "mod_lock_post"]
+#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
+#[cfg_attr(feature = "full", table_name = "mod_lock_post")]
 pub struct ModLockPost {
   pub id: i32,
   pub mod_person_id: PersonId,
@@ -46,16 +48,17 @@ pub struct ModLockPost {
   pub when_: chrono::NaiveDateTime,
 }
 
-#[derive(Insertable, AsChangeset)]
-#[table_name = "mod_lock_post"]
+#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
+#[cfg_attr(feature = "full", table_name = "mod_lock_post")]
 pub struct ModLockPostForm {
   pub mod_person_id: PersonId,
   pub post_id: PostId,
   pub locked: Option<bool>,
 }
 
-#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
-#[table_name = "mod_sticky_post"]
+#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
+#[cfg_attr(feature = "full", table_name = "mod_sticky_post")]
 pub struct ModStickyPost {
   pub id: i32,
   pub mod_person_id: PersonId,
@@ -64,16 +67,17 @@ pub struct ModStickyPost {
   pub when_: chrono::NaiveDateTime,
 }
 
-#[derive(Insertable, AsChangeset)]
-#[table_name = "mod_sticky_post"]
+#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
+#[cfg_attr(feature = "full", table_name = "mod_sticky_post")]
 pub struct ModStickyPostForm {
   pub mod_person_id: PersonId,
   pub post_id: PostId,
   pub stickied: Option<bool>,
 }
 
-#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
-#[table_name = "mod_remove_comment"]
+#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
+#[cfg_attr(feature = "full", table_name = "mod_remove_comment")]
 pub struct ModRemoveComment {
   pub id: i32,
   pub mod_person_id: PersonId,
@@ -83,8 +87,8 @@ pub struct ModRemoveComment {
   pub when_: chrono::NaiveDateTime,
 }
 
-#[derive(Insertable, AsChangeset)]
-#[table_name = "mod_remove_comment"]
+#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
+#[cfg_attr(feature = "full", table_name = "mod_remove_comment")]
 pub struct ModRemoveCommentForm {
   pub mod_person_id: PersonId,
   pub comment_id: CommentId,
@@ -92,8 +96,9 @@ pub struct ModRemoveCommentForm {
   pub removed: Option<bool>,
 }
 
-#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
-#[table_name = "mod_remove_community"]
+#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
+#[cfg_attr(feature = "full", table_name = "mod_remove_community")]
 pub struct ModRemoveCommunity {
   pub id: i32,
   pub mod_person_id: PersonId,
@@ -104,8 +109,8 @@ pub struct ModRemoveCommunity {
   pub when_: chrono::NaiveDateTime,
 }
 
-#[derive(Insertable, AsChangeset)]
-#[table_name = "mod_remove_community"]
+#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
+#[cfg_attr(feature = "full", table_name = "mod_remove_community")]
 pub struct ModRemoveCommunityForm {
   pub mod_person_id: PersonId,
   pub community_id: CommunityId,
@@ -114,8 +119,9 @@ pub struct ModRemoveCommunityForm {
   pub expires: Option<chrono::NaiveDateTime>,
 }
 
-#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
-#[table_name = "mod_ban_from_community"]
+#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
+#[cfg_attr(feature = "full", table_name = "mod_ban_from_community")]
 pub struct ModBanFromCommunity {
   pub id: i32,
   pub mod_person_id: PersonId,
@@ -127,8 +133,8 @@ pub struct ModBanFromCommunity {
   pub when_: chrono::NaiveDateTime,
 }
 
-#[derive(Insertable, AsChangeset)]
-#[table_name = "mod_ban_from_community"]
+#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
+#[cfg_attr(feature = "full", table_name = "mod_ban_from_community")]
 pub struct ModBanFromCommunityForm {
   pub mod_person_id: PersonId,
   pub other_person_id: PersonId,
@@ -138,8 +144,9 @@ pub struct ModBanFromCommunityForm {
   pub expires: Option<chrono::NaiveDateTime>,
 }
 
-#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
-#[table_name = "mod_ban"]
+#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
+#[cfg_attr(feature = "full", table_name = "mod_ban")]
 pub struct ModBan {
   pub id: i32,
   pub mod_person_id: PersonId,
@@ -150,16 +157,17 @@ pub struct ModBan {
   pub when_: chrono::NaiveDateTime,
 }
 
-#[derive(Insertable, AsChangeset)]
-#[table_name = "mod_hide_community"]
+#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
+#[cfg_attr(feature = "full", table_name = "mod_hide_community")]
 pub struct ModHideCommunityForm {
   pub community_id: CommunityId,
   pub mod_person_id: PersonId,
   pub hidden: Option<bool>,
   pub reason: Option<String>,
 }
-#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
-#[table_name = "mod_hide_community"]
+#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
+#[cfg_attr(feature = "full", table_name = "mod_hide_community")]
 pub struct ModHideCommunity {
   pub id: i32,
   pub community_id: CommunityId,
@@ -169,8 +177,8 @@ pub struct ModHideCommunity {
   pub when_: chrono::NaiveDateTime,
 }
 
-#[derive(Insertable, AsChangeset)]
-#[table_name = "mod_ban"]
+#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
+#[cfg_attr(feature = "full", table_name = "mod_ban")]
 pub struct ModBanForm {
   pub mod_person_id: PersonId,
   pub other_person_id: PersonId,
@@ -179,8 +187,9 @@ pub struct ModBanForm {
   pub expires: Option<chrono::NaiveDateTime>,
 }
 
-#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
-#[table_name = "mod_add_community"]
+#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
+#[cfg_attr(feature = "full", table_name = "mod_add_community")]
 pub struct ModAddCommunity {
   pub id: i32,
   pub mod_person_id: PersonId,
@@ -190,8 +199,8 @@ pub struct ModAddCommunity {
   pub when_: chrono::NaiveDateTime,
 }
 
-#[derive(Insertable, AsChangeset)]
-#[table_name = "mod_add_community"]
+#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
+#[cfg_attr(feature = "full", table_name = "mod_add_community")]
 pub struct ModAddCommunityForm {
   pub mod_person_id: PersonId,
   pub other_person_id: PersonId,
@@ -199,8 +208,9 @@ pub struct ModAddCommunityForm {
   pub removed: Option<bool>,
 }
 
-#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
-#[table_name = "mod_transfer_community"]
+#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
+#[cfg_attr(feature = "full", table_name = "mod_transfer_community")]
 pub struct ModTransferCommunity {
   pub id: i32,
   pub mod_person_id: PersonId,
@@ -210,8 +220,8 @@ pub struct ModTransferCommunity {
   pub when_: chrono::NaiveDateTime,
 }
 
-#[derive(Insertable, AsChangeset)]
-#[table_name = "mod_transfer_community"]
+#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
+#[cfg_attr(feature = "full", table_name = "mod_transfer_community")]
 pub struct ModTransferCommunityForm {
   pub mod_person_id: PersonId,
   pub other_person_id: PersonId,
@@ -219,8 +229,9 @@ pub struct ModTransferCommunityForm {
   pub removed: Option<bool>,
 }
 
-#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
-#[table_name = "mod_add"]
+#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
+#[cfg_attr(feature = "full", table_name = "mod_add")]
 pub struct ModAdd {
   pub id: i32,
   pub mod_person_id: PersonId,
@@ -229,8 +240,8 @@ pub struct ModAdd {
   pub when_: chrono::NaiveDateTime,
 }
 
-#[derive(Insertable, AsChangeset)]
-#[table_name = "mod_add"]
+#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
+#[cfg_attr(feature = "full", table_name = "mod_add")]
 pub struct ModAddForm {
   pub mod_person_id: PersonId,
   pub other_person_id: PersonId,
diff --git a/crates/db_schema/src/source/password_reset_request.rs b/crates/db_schema/src/source/password_reset_request.rs
index 0d461049..3df7fab0 100644
--- a/crates/db_schema/src/source/password_reset_request.rs
+++ b/crates/db_schema/src/source/password_reset_request.rs
@@ -1,7 +1,11 @@
-use crate::{newtypes::LocalUserId, schema::password_reset_request};
+use crate::newtypes::LocalUserId;
 
-#[derive(Queryable, Identifiable, PartialEq, Debug)]
-#[table_name = "password_reset_request"]
+#[cfg(feature = "full")]
+use crate::schema::password_reset_request;
+
+#[derive(PartialEq, Debug)]
+#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
+#[cfg_attr(feature = "full", table_name = "password_reset_request")]
 pub struct PasswordResetRequest {
   pub id: i32,
   pub token_encrypted: String,
@@ -9,8 +13,8 @@ pub struct PasswordResetRequest {
   pub local_user_id: LocalUserId,
 }
 
-#[derive(Insertable, AsChangeset)]
-#[table_name = "password_reset_request"]
+#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
+#[cfg_attr(feature = "full", table_name = "password_reset_request")]
 pub struct PasswordResetRequestForm {
   pub local_user_id: LocalUserId,
   pub token_encrypted: String,
diff --git a/crates/db_schema/src/source/person.rs b/crates/db_schema/src/source/person.rs
index 97829830..7c301c99 100644
--- a/crates/db_schema/src/source/person.rs
+++ b/crates/db_schema/src/source/person.rs
@@ -1,11 +1,12 @@
-use crate::{
-  newtypes::{DbUrl, PersonId},
-  schema::{person, person_alias_1, person_alias_2},
-};
+use crate::newtypes::{DbUrl, PersonId};
 use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
-#[table_name = "person"]
+#[cfg(feature = "full")]
+use crate::schema::{person, person_alias_1, person_alias_2};
+
+#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
+#[cfg_attr(feature = "full", table_name = "person")]
 pub struct Person {
   pub id: PersonId,
   pub name: String,
@@ -31,8 +32,9 @@ pub struct Person {
 }
 
 /// A safe representation of person, without the sensitive info
-#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
-#[table_name = "person"]
+#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
+#[cfg_attr(feature = "full", table_name = "person")]
 pub struct PersonSafe {
   pub id: PersonId,
   pub name: String,
@@ -54,8 +56,9 @@ pub struct PersonSafe {
   pub ban_expires: Option<chrono::NaiveDateTime>,
 }
 
-#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
-#[table_name = "person_alias_1"]
+#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
+#[cfg_attr(feature = "full", table_name = "person_alias_1")]
 pub struct PersonAlias1 {
   pub id: PersonId,
   pub name: String,
@@ -80,8 +83,9 @@ pub struct PersonAlias1 {
   pub ban_expires: Option<chrono::NaiveDateTime>,
 }
 
-#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
-#[table_name = "person_alias_1"]
+#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
+#[cfg_attr(feature = "full", table_name = "person_alias_1")]
 pub struct PersonSafeAlias1 {
   pub id: PersonId,
   pub name: String,
@@ -103,8 +107,9 @@ pub struct PersonSafeAlias1 {
   pub ban_expires: Option<chrono::NaiveDateTime>,
 }
 
-#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
-#[table_name = "person_alias_2"]
+#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
+#[cfg_attr(feature = "full", table_name = "person_alias_2")]
 pub struct PersonAlias2 {
   pub id: PersonId,
   pub name: String,
@@ -129,8 +134,9 @@ pub struct PersonAlias2 {
   pub ban_expires: Option<chrono::NaiveDateTime>,
 }
 
-#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
-#[table_name = "person_alias_1"]
+#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
+#[cfg_attr(feature = "full", table_name = "person_alias_1")]
 pub struct PersonSafeAlias2 {
   pub id: PersonId,
   pub name: String,
@@ -152,8 +158,9 @@ pub struct PersonSafeAlias2 {
   pub ban_expires: Option<chrono::NaiveDateTime>,
 }
 
-#[derive(Insertable, AsChangeset, Clone, Default)]
-#[table_name = "person"]
+#[derive(Clone, Default)]
+#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
+#[cfg_attr(feature = "full", table_name = "person")]
 pub struct PersonForm {
   pub name: String,
   pub display_name: Option<Option<String>>,
diff --git a/crates/db_schema/src/source/person_block.rs b/crates/db_schema/src/source/person_block.rs
index ff55872f..46920acb 100644
--- a/crates/db_schema/src/source/person_block.rs
+++ b/crates/db_schema/src/source/person_block.rs
@@ -1,13 +1,12 @@
-use crate::{
-  newtypes::{PersonBlockId, PersonId},
-  schema::person_block,
-};
+use crate::newtypes::{PersonBlockId, PersonId};
 use serde::{Deserialize, Serialize};
 
-#[derive(
-  Clone, Queryable, Associations, Identifiable, PartialEq, Debug, Serialize, Deserialize,
-)]
-#[table_name = "person_block"]
+#[cfg(feature = "full")]
+use crate::schema::person_block;
+
+#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable))]
+#[cfg_attr(feature = "full", table_name = "person_block")]
 pub struct PersonBlock {
   pub id: PersonBlockId,
   pub person_id: PersonId,
@@ -15,8 +14,8 @@ pub struct PersonBlock {
   pub published: chrono::NaiveDateTime,
 }
 
-#[derive(Insertable, AsChangeset)]
-#[table_name = "person_block"]
+#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
+#[cfg_attr(feature = "full", table_name = "person_block")]
 pub struct PersonBlockForm {
   pub person_id: PersonId,
   pub target_id: PersonId,
diff --git a/crates/db_schema/src/source/person_mention.rs b/crates/db_schema/src/source/person_mention.rs
index 795799c7..5716cb8c 100644
--- a/crates/db_schema/src/source/person_mention.rs
+++ b/crates/db_schema/src/source/person_mention.rs
@@ -1,15 +1,13 @@
-use crate::{
-  newtypes::{CommentId, PersonId, PersonMentionId},
-  schema::person_mention,
-  source::comment::Comment,
-};
+use crate::newtypes::{CommentId, PersonId, PersonMentionId};
 use serde::{Deserialize, Serialize};
 
-#[derive(
-  Clone, Queryable, Associations, Identifiable, PartialEq, Debug, Serialize, Deserialize,
-)]
-#[belongs_to(Comment)]
-#[table_name = "person_mention"]
+#[cfg(feature = "full")]
+use crate::schema::person_mention;
+
+#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable))]
+#[cfg_attr(feature = "full", belongs_to(crate::source::comment::Comment))]
+#[cfg_attr(feature = "full", table_name = "person_mention")]
 pub struct PersonMention {
   pub id: PersonMentionId,
   pub recipient_id: PersonId,
@@ -18,8 +16,8 @@ pub struct PersonMention {
   pub published: chrono::NaiveDateTime,
 }
 
-#[derive(Insertable, AsChangeset)]
-#[table_name = "person_mention"]
+#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
+#[cfg_attr(feature = "full", table_name = "person_mention")]
 pub struct PersonMentionForm {
   pub recipient_id: PersonId,
   pub comment_id: CommentId,
diff --git a/crates/db_schema/src/source/post.rs b/crates/db_schema/src/source/post.rs
index cc932536..a3054e40 100644
--- a/crates/db_schema/src/source/post.rs
+++ b/crates/db_schema/src/source/post.rs
@@ -1,10 +1,12 @@
-use crate::{
-  newtypes::{CommunityId, DbUrl, PersonId, PostId},
-  schema::{post, post_like, post_read, post_saved},
-};
+use crate::newtypes::{CommunityId, DbUrl, PersonId, PostId};
 use serde::{Deserialize, Serialize};
-#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
-#[table_name = "post"]
+
+#[cfg(feature = "full")]
+use crate::schema::{post, post_like, post_read, post_saved};
+
+#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
+#[cfg_attr(feature = "full", table_name = "post")]
 pub struct Post {
   pub id: PostId,
   pub name: String,
@@ -27,8 +29,9 @@ pub struct Post {
   pub local: bool,
 }
 
-#[derive(Insertable, AsChangeset, Default)]
-#[table_name = "post"]
+#[derive(Default)]
+#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
+#[cfg_attr(feature = "full", table_name = "post")]
 pub struct PostForm {
   pub name: String,
   pub creator_id: PersonId,
@@ -50,9 +53,10 @@ pub struct PostForm {
   pub local: Option<bool>,
 }
 
-#[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]
-#[belongs_to(Post)]
-#[table_name = "post_like"]
+#[derive(PartialEq, Debug)]
+#[cfg_attr(feature = "full", derive(Identifiable, Queryable, Associations))]
+#[cfg_attr(feature = "full", belongs_to(Post))]
+#[cfg_attr(feature = "full", table_name = "post_like")]
 pub struct PostLike {
   pub id: i32,
   pub post_id: PostId,
@@ -61,17 +65,19 @@ pub struct PostLike {
   pub published: chrono::NaiveDateTime,
 }
 
-#[derive(Insertable, AsChangeset, Clone)]
-#[table_name = "post_like"]
+#[derive(Clone)]
+#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
+#[cfg_attr(feature = "full", table_name = "post_like")]
 pub struct PostLikeForm {
   pub post_id: PostId,
   pub person_id: PersonId,
   pub score: i16,
 }
 
-#[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]
-#[belongs_to(Post)]
-#[table_name = "post_saved"]
+#[derive(PartialEq, Debug)]
+#[cfg_attr(feature = "full", derive(Identifiable, Queryable, Associations))]
+#[cfg_attr(feature = "full", belongs_to(Post))]
+#[cfg_attr(feature = "full", table_name = "post_saved")]
 pub struct PostSaved {
   pub id: i32,
   pub post_id: PostId,
@@ -79,16 +85,17 @@ pub struct PostSaved {
   pub published: chrono::NaiveDateTime,
 }
 
-#[derive(Insertable, AsChangeset)]
-#[table_name = "post_saved"]
+#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
+#[cfg_attr(feature = "full", table_name = "post_saved")]
 pub struct PostSavedForm {
   pub post_id: PostId,
   pub person_id: PersonId,
 }
 
-#[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]
-#[belongs_to(Post)]
-#[table_name = "post_read"]
+#[derive(PartialEq, Debug)]
+#[cfg_attr(feature = "full", derive(Identifiable, Queryable, Associations))]
+#[cfg_attr(feature = "full", belongs_to(Post))]
+#[cfg_attr(feature = "full", table_name = "post_read")]
 pub struct PostRead {
   pub id: i32,
   pub post_id: PostId,
@@ -96,8 +103,8 @@ pub struct PostRead {
   pub published: chrono::NaiveDateTime,
 }
 
-#[derive(Insertable, AsChangeset)]
-#[table_name = "post_read"]
+#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
+#[cfg_attr(feature = "full", table_name = "post_read")]
 pub struct PostReadForm {
   pub post_id: PostId,
   pub person_id: PersonId,
diff --git a/crates/db_schema/src/source/post_report.rs b/crates/db_schema/src/source/post_report.rs
index 6e711782..afb33469 100644
--- a/crates/db_schema/src/source/post_report.rs
+++ b/crates/db_schema/src/source/post_report.rs
@@ -1,15 +1,13 @@
-use crate::{
-  newtypes::{DbUrl, PersonId, PostId, PostReportId},
-  schema::post_report,
-  source::post::Post,
-};
+use crate::newtypes::{DbUrl, PersonId, PostId, PostReportId};
 use serde::{Deserialize, Serialize};
 
-#[derive(
-  Identifiable, Queryable, Associations, PartialEq, Serialize, Deserialize, Debug, Clone,
-)]
-#[belongs_to(Post)]
-#[table_name = "post_report"]
+#[cfg(feature = "full")]
+use crate::schema::post_report;
+
+#[derive(PartialEq, Serialize, Deserialize, Debug, Clone)]
+#[cfg_attr(feature = "full", derive(Identifiable, Queryable, Associations))]
+#[cfg_attr(feature = "full", belongs_to(crate::source::post::Post))]
+#[cfg_attr(feature = "full", table_name = "post_report")]
 pub struct PostReport {
   pub id: PostReportId,
   pub creator_id: PersonId,
@@ -24,8 +22,9 @@ pub struct PostReport {
   pub updated: Option<chrono::NaiveDateTime>,
 }
 
-#[derive(Insertable, AsChangeset, Clone)]
-#[table_name = "post_report"]
+#[derive(Clone)]
+#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
+#[cfg_attr(feature = "full", table_name = "post_report")]
 pub struct PostReportForm {
   pub creator_id: PersonId,
   pub post_id: PostId,
diff --git a/crates/db_schema/src/source/private_message.rs b/crates/db_schema/src/source/private_message.rs
index 7facccaa..fa697a51 100644
--- a/crates/db_schema/src/source/private_message.rs
+++ b/crates/db_schema/src/source/private_message.rs
@@ -1,13 +1,12 @@
-use crate::{
-  newtypes::{DbUrl, PersonId, PrivateMessageId},
-  schema::private_message,
-};
+use crate::newtypes::{DbUrl, PersonId, PrivateMessageId};
 use serde::{Deserialize, Serialize};
 
-#[derive(
-  Clone, Queryable, Associations, Identifiable, PartialEq, Debug, Serialize, Deserialize,
-)]
-#[table_name = "private_message"]
+#[cfg(feature = "full")]
+use crate::schema::private_message;
+
+#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable))]
+#[cfg_attr(feature = "full", table_name = "private_message")]
 pub struct PrivateMessage {
   pub id: PrivateMessageId,
   pub creator_id: PersonId,
@@ -21,8 +20,9 @@ pub struct PrivateMessage {
   pub local: bool,
 }
 
-#[derive(Insertable, AsChangeset, Default)]
-#[table_name = "private_message"]
+#[derive(Default)]
+#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
+#[cfg_attr(feature = "full", table_name = "private_message")]
 pub struct PrivateMessageForm {
   pub creator_id: PersonId,
   pub recipient_id: PersonId,
diff --git a/crates/db_schema/src/source/registration_application.rs b/crates/db_schema/src/source/registration_application.rs
index 01f702d8..c874427a 100644
--- a/crates/db_schema/src/source/registration_application.rs
+++ b/crates/db_schema/src/source/registration_application.rs
@@ -1,11 +1,12 @@
-use crate::{
-  newtypes::{LocalUserId, PersonId},
-  schema::registration_application,
-};
+use crate::newtypes::{LocalUserId, PersonId};
 use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
-#[table_name = "registration_application"]
+#[cfg(feature = "full")]
+use crate::schema::registration_application;
+
+#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
+#[cfg_attr(feature = "full", table_name = "registration_application")]
 pub struct RegistrationApplication {
   pub id: i32,
   pub local_user_id: LocalUserId,
@@ -15,8 +16,9 @@ pub struct RegistrationApplication {
   pub published: chrono::NaiveDateTime,
 }
 
-#[derive(Insertable, AsChangeset, Default)]
-#[table_name = "registration_application"]
+#[derive(Default)]
+#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
+#[cfg_attr(feature = "full", table_name = "registration_application")]
 pub struct RegistrationApplicationForm {
   pub local_user_id: Option<LocalUserId>,
   pub answer: Option<String>,
diff --git a/crates/db_schema/src/source/secret.rs b/crates/db_schema/src/source/secret.rs
index 1a8b3018..d32a3ac7 100644
--- a/crates/db_schema/src/source/secret.rs
+++ b/crates/db_schema/src/source/secret.rs
@@ -1,7 +1,9 @@
+#[cfg(feature = "full")]
 use crate::schema::secret;
 
-#[derive(Queryable, Identifiable, Clone)]
-#[table_name = "secret"]
+#[derive(Clone)]
+#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
+#[cfg_attr(feature = "full", table_name = "secret")]
 pub struct Secret {
   pub id: i32,
   pub jwt_secret: String,
diff --git a/crates/db_schema/src/source/site.rs b/crates/db_schema/src/source/site.rs
index 8e73cca4..b4378366 100644
--- a/crates/db_schema/src/source/site.rs
+++ b/crates/db_schema/src/source/site.rs
@@ -1,8 +1,12 @@
-use crate::{newtypes::DbUrl, schema::site};
+use crate::newtypes::DbUrl;
 use serde::{Deserialize, Serialize};
 
-#[derive(Queryable, Identifiable, PartialEq, Debug, Clone, Serialize, Deserialize)]
-#[table_name = "site"]
+#[cfg(feature = "full")]
+use crate::schema::site;
+
+#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
+#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
+#[cfg_attr(feature = "full", table_name = "site")]
 pub struct Site {
   pub id: i32,
   pub name: String,
@@ -29,8 +33,9 @@ pub struct Site {
   pub default_post_listing_type: String,
 }
 
-#[derive(Insertable, AsChangeset, Default)]
-#[table_name = "site"]
+#[derive(Default)]
+#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
+#[cfg_attr(feature = "full", table_name = "site")]
 pub struct SiteForm {
   pub name: String,
   pub sidebar: Option<Option<String>>,
diff --git a/crates/db_schema/src/traits.rs b/crates/db_schema/src/traits.rs
index 98f97327..2b8e3422 100644
--- a/crates/db_schema/src/traits.rs
+++ b/crates/db_schema/src/traits.rs
@@ -1,7 +1,4 @@
-use crate::{
-  newtypes::{CommunityId, PersonId},
-  DbUrl,
-};
+use crate::newtypes::{CommunityId, DbUrl, PersonId};
 use diesel::{result::Error, PgConnection};
 
 pub trait Crud {
diff --git a/crates/db_schema/src/utils.rs b/crates/db_schema/src/utils.rs
new file mode 100644
index 00000000..58543057
--- /dev/null
+++ b/crates/db_schema/src/utils.rs
@@ -0,0 +1,216 @@
+use crate::newtypes::DbUrl;
+use chrono::NaiveDateTime;
+use diesel::{
+  backend::Backend,
+  deserialize::FromSql,
+  serialize::{Output, ToSql},
+  sql_types::Text,
+  Connection,
+  PgConnection,
+};
+use lemmy_apub_lib::{object_id::ObjectId, traits::ApubObject};
+use lemmy_utils::LemmyError;
+use once_cell::sync::Lazy;
+use regex::Regex;
+use serde::{Deserialize, Serialize};
+use std::{env, env::VarError, io::Write};
+use url::Url;
+
+pub type DbPool = diesel::r2d2::Pool<diesel::r2d2::ConnectionManager<diesel::PgConnection>>;
+
+pub fn get_database_url_from_env() -> Result<String, VarError> {
+  env::var("LEMMY_DATABASE_URL")
+}
+
+#[derive(EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy)]
+pub enum SortType {
+  Active,
+  Hot,
+  New,
+  TopDay,
+  TopWeek,
+  TopMonth,
+  TopYear,
+  TopAll,
+  MostComments,
+  NewComments,
+}
+
+#[derive(EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy, PartialEq)]
+pub enum ListingType {
+  All,
+  Local,
+  Subscribed,
+  Community,
+}
+
+#[derive(EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy)]
+pub enum SearchType {
+  All,
+  Comments,
+  Posts,
+  Communities,
+  Users,
+  Url,
+}
+
+pub fn from_opt_str_to_opt_enum<T: std::str::FromStr>(opt: &Option<String>) -> Option<T> {
+  opt.as_ref().and_then(|t| T::from_str(t).ok())
+}
+
+pub fn fuzzy_search(q: &str) -> String {
+  let replaced = q.replace('%', "\\%").replace('_', "\\_").replace(' ', "%");
+  format!("%{}%", replaced)
+}
+
+pub fn limit_and_offset(page: Option<i64>, limit: Option<i64>) -> (i64, i64) {
+  let page = page.unwrap_or(1);
+  let limit = limit.unwrap_or(10);
+  let offset = limit * (page - 1);
+  (limit, offset)
+}
+
+pub fn is_email_regex(test: &str) -> bool {
+  EMAIL_REGEX.is_match(test)
+}
+
+pub fn diesel_option_overwrite(opt: &Option<String>) -> Option<Option<String>> {
+  match opt {
+    // An empty string is an erase
+    Some(unwrapped) => {
+      if !unwrapped.eq("") {
+        Some(Some(unwrapped.to_owned()))
+      } else {
+        Some(None)
+      }
+    }
+    None => None,
+  }
+}
+
+pub fn diesel_option_overwrite_to_url(
+  opt: &Option<String>,
+) -> Result<Option<Option<DbUrl>>, LemmyError> {
+  match opt.as_ref().map(|s| s.as_str()) {
+    // An empty string is an erase
+    Some("") => Ok(Some(None)),
+    Some(str_url) => match Url::parse(str_url) {
+      Ok(url) => Ok(Some(Some(url.into()))),
+      Err(e) => Err(LemmyError::from_error_message(e, "invalid_url")),
+    },
+    None => Ok(None),
+  }
+}
+
+embed_migrations!();
+
+pub fn establish_unpooled_connection() -> PgConnection {
+  let db_url = match get_database_url_from_env() {
+    Ok(url) => url,
+    Err(e) => panic!(
+      "Failed to read database URL from env var LEMMY_DATABASE_URL: {}",
+      e
+    ),
+  };
+  let conn =
+    PgConnection::establish(&db_url).unwrap_or_else(|_| panic!("Error connecting to {}", db_url));
+  embedded_migrations::run(&conn).expect("load migrations");
+  conn
+}
+
+pub fn naive_now() -> NaiveDateTime {
+  chrono::prelude::Utc::now().naive_utc()
+}
+
+static EMAIL_REGEX: Lazy<Regex> = Lazy::new(|| {
+  Regex::new(r"^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$")
+    .expect("compile email regex")
+});
+
+pub mod functions {
+  use diesel::sql_types::*;
+
+  sql_function! {
+    fn hot_rank(score: BigInt, time: Timestamp) -> Integer;
+  }
+
+  sql_function!(fn lower(x: Text) -> Text);
+}
+
+impl<DB: Backend> ToSql<Text, DB> for DbUrl
+where
+  String: ToSql<Text, DB>,
+{
+  fn to_sql<W: Write>(&self, out: &mut Output<W, DB>) -> diesel::serialize::Result {
+    self.0.to_string().to_sql(out)
+  }
+}
+
+impl<DB: Backend> FromSql<Text, DB> for DbUrl
+where
+  String: FromSql<Text, DB>,
+{
+  fn from_sql(bytes: Option<&DB::RawValue>) -> diesel::deserialize::Result<Self> {
+    let str = String::from_sql(bytes)?;
+    Ok(DbUrl(Url::parse(&str)?))
+  }
+}
+
+impl<Kind> From<ObjectId<Kind>> for DbUrl
+where
+  Kind: ApubObject + Send + 'static,
+  for<'de2> <Kind as ApubObject>::ApubType: serde::Deserialize<'de2>,
+{
+  fn from(id: ObjectId<Kind>) -> Self {
+    DbUrl(id.into())
+  }
+}
+
+#[cfg(test)]
+mod tests {
+  use super::{fuzzy_search, *};
+  use crate::utils::is_email_regex;
+
+  #[test]
+  fn test_fuzzy_search() {
+    let test = "This %is% _a_ fuzzy search";
+    assert_eq!(
+      fuzzy_search(test),
+      "%This%\\%is\\%%\\_a\\_%fuzzy%search%".to_string()
+    );
+  }
+
+  #[test]
+  fn test_email() {
+    assert!(is_email_regex("gush@gmail.com"));
+    assert!(!is_email_regex("nada_neutho"));
+  }
+
+  #[test]
+  fn test_diesel_option_overwrite() {
+    assert_eq!(diesel_option_overwrite(&None), None);
+    assert_eq!(diesel_option_overwrite(&Some("".to_string())), Some(None));
+    assert_eq!(
+      diesel_option_overwrite(&Some("test".to_string())),
+      Some(Some("test".to_string()))
+    );
+  }
+
+  #[test]
+  fn test_diesel_option_overwrite_to_url() {
+    assert!(matches!(diesel_option_overwrite_to_url(&None), Ok(None)));
+    assert!(matches!(
+      diesel_option_overwrite_to_url(&Some("".to_string())),
+      Ok(Some(None))
+    ));
+    assert!(matches!(
+      diesel_option_overwrite_to_url(&Some("invalid_url".to_string())),
+      Err(_)
+    ));
+    let example_url = "https://example.com";
+    assert!(matches!(
+      diesel_option_overwrite_to_url(&Some(example_url.to_string())),
+      Ok(Some(Some(url))) if url == Url::parse(example_url).unwrap().into()
+    ));
+  }
+}
diff --git a/crates/db_views/Cargo.toml b/crates/db_views/Cargo.toml
index 4753b38d..e4311371 100644
--- a/crates/db_views/Cargo.toml
+++ b/crates/db_views/Cargo.toml
@@ -10,11 +10,14 @@ documentation = "https://join-lemmy.org/docs/en/index.html"
 [lib]
 doctest = false
 
+[features]
+full = ["lemmy_db_schema/full", "diesel", "tracing"]
+
 [dependencies]
 lemmy_db_schema = { version = "=0.16.3", path = "../db_schema" }
-diesel = { version = "1.4.8", features = ["postgres","chrono","r2d2","serde_json"] }
+diesel = { version = "1.4.8", features = ["postgres","chrono","r2d2","serde_json"], optional = true }
 serde = { version = "1.0.136", features = ["derive"] }
-tracing = "0.1.32"
+tracing = { version = "0.1.32", optional = true }
 
 [dev-dependencies]
 serial_test = "0.6.0"
diff --git a/crates/db_views/src/comment_report_view.rs b/crates/db_views/src/comment_report_view.rs
index 73224be5..bca8afdc 100644
--- a/crates/db_views/src/comment_report_view.rs
+++ b/crates/db_views/src/comment_report_view.rs
@@ -1,7 +1,7 @@
+use crate::structs::CommentReportView;
 use diesel::{dsl::*, result::Error, *};
 use lemmy_db_schema::{
-  aggregates::comment_aggregates::CommentAggregates,
-  limit_and_offset,
+  aggregates::structs::CommentAggregates,
   newtypes::{CommentReportId, CommunityId, PersonId},
   schema::{
     comment,
@@ -24,22 +24,8 @@ use lemmy_db_schema::{
     post::Post,
   },
   traits::{MaybeOptional, ToSafe, ViewToVec},
+  utils::limit_and_offset,
 };
-use serde::{Deserialize, Serialize};
-
-#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
-pub struct CommentReportView {
-  pub comment_report: CommentReport,
-  pub comment: Comment,
-  pub post: Post,
-  pub community: CommunitySafe,
-  pub creator: PersonSafe,
-  pub comment_creator: PersonSafeAlias1,
-  pub counts: CommentAggregates,
-  pub creator_banned_from_community: bool, // Left Join to CommunityPersonBan
-  pub my_vote: Option<i16>,                // Left join to CommentLike
-  pub resolver: Option<PersonSafeAlias2>,
-}
 
 type CommentReportViewTuple = (
   CommentReport,
@@ -321,10 +307,10 @@ impl ViewToVec for CommentReportView {
 mod tests {
   use crate::comment_report_view::{CommentReportQueryBuilder, CommentReportView};
   use lemmy_db_schema::{
-    aggregates::comment_aggregates::CommentAggregates,
-    establish_unpooled_connection,
+    aggregates::structs::CommentAggregates,
     source::{comment::*, comment_report::*, community::*, person::*, post::*},
     traits::{Crud, Joinable, Reportable},
+    utils::establish_unpooled_connection,
   };
   use serial_test::serial;
 
diff --git a/crates/db_views/src/comment_view.rs b/crates/db_views/src/comment_view.rs
index da42bb13..c8b4d387 100644
--- a/crates/db_views/src/comment_view.rs
+++ b/crates/db_views/src/comment_view.rs
@@ -1,9 +1,7 @@
+use crate::structs::CommentView;
 use diesel::{dsl::*, result::Error, *};
 use lemmy_db_schema::{
-  aggregates::comment_aggregates::CommentAggregates,
-  functions::hot_rank,
-  fuzzy_search,
-  limit_and_offset,
+  aggregates::structs::CommentAggregates,
   newtypes::{CommentId, CommunityId, DbUrl, PersonId, PostId},
   schema::{
     comment,
@@ -28,25 +26,8 @@ use lemmy_db_schema::{
     post::Post,
   },
   traits::{MaybeOptional, ToSafe, ViewToVec},
-  ListingType,
-  SortType,
+  utils::{functions::hot_rank, fuzzy_search, limit_and_offset, ListingType, SortType},
 };
-use serde::{Deserialize, Serialize};
-
-#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
-pub struct CommentView {
-  pub comment: Comment,
-  pub creator: PersonSafe,
-  pub recipient: Option<PersonSafeAlias1>, // Left joins to comment and person
-  pub post: Post,
-  pub community: CommunitySafe,
-  pub counts: CommentAggregates,
-  pub creator_banned_from_community: bool, // Left Join to CommunityPersonBan
-  pub subscribed: bool,                    // Left join to CommunityFollower
-  pub saved: bool,                         // Left join to CommentSaved
-  pub creator_blocked: bool,               // Left join to PersonBlock
-  pub my_vote: Option<i16>,                // Left join to CommentLike
-}
 
 type CommentViewTuple = (
   Comment,
@@ -544,10 +525,10 @@ impl ViewToVec for CommentView {
 mod tests {
   use crate::comment_view::*;
   use lemmy_db_schema::{
-    aggregates::comment_aggregates::CommentAggregates,
-    establish_unpooled_connection,
+    aggregates::structs::CommentAggregates,
     source::{comment::*, community::*, person::*, person_block::PersonBlockForm, post::*},
     traits::{Blockable, Crud, Likeable},
+    utils::establish_unpooled_connection,
   };
   use serial_test::serial;
 
diff --git a/crates/db_views/src/lib.rs b/crates/db_views/src/lib.rs
index cb9fefcf..b5567f87 100644
--- a/crates/db_views/src/lib.rs
+++ b/crates/db_views/src/lib.rs
@@ -1,11 +1,20 @@
 #[cfg(test)]
 extern crate serial_test;
 
+#[cfg(feature = "full")]
 pub mod comment_report_view;
+#[cfg(feature = "full")]
 pub mod comment_view;
+#[cfg(feature = "full")]
 pub mod local_user_view;
+#[cfg(feature = "full")]
 pub mod post_report_view;
+#[cfg(feature = "full")]
 pub mod post_view;
+#[cfg(feature = "full")]
 pub mod private_message_view;
+#[cfg(feature = "full")]
 pub mod registration_application_view;
+#[cfg(feature = "full")]
 pub mod site_view;
+pub mod structs;
diff --git a/crates/db_views/src/local_user_view.rs b/crates/db_views/src/local_user_view.rs
index e2453a40..12c26df6 100644
--- a/crates/db_views/src/local_user_view.rs
+++ b/crates/db_views/src/local_user_view.rs
@@ -1,7 +1,7 @@
+use crate::structs::{LocalUserSettingsView, LocalUserView};
 use diesel::{result::Error, *};
 use lemmy_db_schema::{
-  aggregates::person_aggregates::PersonAggregates,
-  functions::lower,
+  aggregates::structs::PersonAggregates,
   newtypes::{LocalUserId, PersonId},
   schema::{local_user, person, person_aggregates},
   source::{
@@ -9,15 +9,8 @@ use lemmy_db_schema::{
     person::{Person, PersonSafe},
   },
   traits::{ToSafe, ToSafeSettings},
+  utils::functions::lower,
 };
-use serde::{Deserialize, Serialize};
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct LocalUserView {
-  pub local_user: LocalUser,
-  pub person: Person,
-  pub counts: PersonAggregates,
-}
 
 type LocalUserViewTuple = (LocalUser, Person, PersonAggregates);
 
@@ -118,13 +111,6 @@ impl LocalUserView {
   }
 }
 
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct LocalUserSettingsView {
-  pub local_user: LocalUserSettings,
-  pub person: PersonSafe,
-  pub counts: PersonAggregates,
-}
-
 type LocalUserSettingsViewTuple = (LocalUserSettings, PersonSafe, PersonAggregates);
 
 impl LocalUserSettingsView {
diff --git a/crates/db_views/src/post_report_view.rs b/crates/db_views/src/post_report_view.rs
index 0f56c262..9769ec02 100644
--- a/crates/db_views/src/post_report_view.rs
+++ b/crates/db_views/src/post_report_view.rs
@@ -1,7 +1,7 @@
+use crate::structs::PostReportView;
 use diesel::{dsl::*, result::Error, *};
 use lemmy_db_schema::{
-  aggregates::post_aggregates::PostAggregates,
-  limit_and_offset,
+  aggregates::structs::PostAggregates,
   newtypes::{CommunityId, PersonId, PostReportId},
   schema::{
     community,
@@ -22,21 +22,8 @@ use lemmy_db_schema::{
     post_report::PostReport,
   },
   traits::{MaybeOptional, ToSafe, ViewToVec},
+  utils::limit_and_offset,
 };
-use serde::{Deserialize, Serialize};
-
-#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
-pub struct PostReportView {
-  pub post_report: PostReport,
-  pub post: Post,
-  pub community: CommunitySafe,
-  pub creator: PersonSafe,
-  pub post_creator: PersonSafeAlias1,
-  pub creator_banned_from_community: bool,
-  pub my_vote: Option<i16>,
-  pub counts: PostAggregates,
-  pub resolver: Option<PersonSafeAlias2>,
-}
 
 type PostReportViewTuple = (
   PostReport,
@@ -304,8 +291,7 @@ impl ViewToVec for PostReportView {
 mod tests {
   use crate::post_report_view::{PostReportQueryBuilder, PostReportView};
   use lemmy_db_schema::{
-    aggregates::post_aggregates::PostAggregates,
-    establish_unpooled_connection,
+    aggregates::structs::PostAggregates,
     source::{
       community::*,
       person::*,
@@ -313,6 +299,7 @@ mod tests {
       post_report::{PostReport, PostReportForm},
     },
     traits::{Crud, Joinable, Reportable},
+    utils::establish_unpooled_connection,
   };
   use serial_test::serial;
 
diff --git a/crates/db_views/src/post_view.rs b/crates/db_views/src/post_view.rs
index dd97cd8d..2fac5ade 100644
--- a/crates/db_views/src/post_view.rs
+++ b/crates/db_views/src/post_view.rs
@@ -1,9 +1,7 @@
+use crate::structs::PostView;
 use diesel::{dsl::*, pg::Pg, result::Error, *};
 use lemmy_db_schema::{
-  aggregates::post_aggregates::PostAggregates,
-  functions::hot_rank,
-  fuzzy_search,
-  limit_and_offset,
+  aggregates::structs::PostAggregates,
   newtypes::{CommunityId, DbUrl, PersonId, PostId},
   schema::{
     community,
@@ -25,26 +23,10 @@ use lemmy_db_schema::{
     post::{Post, PostRead, PostSaved},
   },
   traits::{MaybeOptional, ToSafe, ViewToVec},
-  ListingType,
-  SortType,
+  utils::{functions::hot_rank, fuzzy_search, limit_and_offset, ListingType, SortType},
 };
-use serde::{Deserialize, Serialize};
 use tracing::debug;
 
-#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
-pub struct PostView {
-  pub post: Post,
-  pub creator: PersonSafe,
-  pub community: CommunitySafe,
-  pub creator_banned_from_community: bool, // Left Join to CommunityPersonBan
-  pub counts: PostAggregates,
-  pub subscribed: bool,      // Left join to CommunityFollower
-  pub saved: bool,           // Left join to PostSaved
-  pub read: bool,            // Left join to PostRead
-  pub creator_blocked: bool, // Left join to PersonBlock
-  pub my_vote: Option<i16>,  // Left join to PostLike
-}
-
 type PostViewTuple = (
   Post,
   PersonSafe,
@@ -514,8 +496,7 @@ impl ViewToVec for PostView {
 mod tests {
   use crate::post_view::{PostQueryBuilder, PostView};
   use lemmy_db_schema::{
-    aggregates::post_aggregates::PostAggregates,
-    establish_unpooled_connection,
+    aggregates::structs::PostAggregates,
     source::{
       community::*,
       community_block::{CommunityBlock, CommunityBlockForm},
@@ -524,8 +505,7 @@ mod tests {
       post::*,
     },
     traits::{Blockable, Crud, Likeable},
-    ListingType,
-    SortType,
+    utils::{establish_unpooled_connection, ListingType, SortType},
   };
   use serial_test::serial;
 
diff --git a/crates/db_views/src/private_message_view.rs b/crates/db_views/src/private_message_view.rs
index 8996f229..fdf5b8e1 100644
--- a/crates/db_views/src/private_message_view.rs
+++ b/crates/db_views/src/private_message_view.rs
@@ -1,6 +1,6 @@
+use crate::structs::PrivateMessageView;
 use diesel::{pg::Pg, result::Error, *};
 use lemmy_db_schema::{
-  limit_and_offset,
   newtypes::{PersonId, PrivateMessageId},
   schema::{person, person_alias_1, private_message},
   source::{
@@ -8,17 +8,10 @@ use lemmy_db_schema::{
     private_message::PrivateMessage,
   },
   traits::{MaybeOptional, ToSafe, ViewToVec},
+  utils::limit_and_offset,
 };
-use serde::{Deserialize, Serialize};
 use tracing::debug;
 
-#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
-pub struct PrivateMessageView {
-  pub private_message: PrivateMessage,
-  pub creator: PersonSafe,
-  pub recipient: PersonSafeAlias1,
-}
-
 type PrivateMessageViewTuple = (PrivateMessage, PersonSafe, PersonSafeAlias1);
 
 impl PrivateMessageView {
diff --git a/crates/db_views/src/registration_application_view.rs b/crates/db_views/src/registration_application_view.rs
index 2eab1535..00c79323 100644
--- a/crates/db_views/src/registration_application_view.rs
+++ b/crates/db_views/src/registration_application_view.rs
@@ -1,6 +1,6 @@
+use crate::structs::RegistrationApplicationView;
 use diesel::{dsl::count, result::Error, *};
 use lemmy_db_schema::{
-  limit_and_offset,
   schema::{local_user, person, person_alias_1, registration_application},
   source::{
     local_user::{LocalUser, LocalUserSettings},
@@ -8,16 +8,8 @@ use lemmy_db_schema::{
     registration_application::RegistrationApplication,
   },
   traits::{MaybeOptional, ToSafe, ToSafeSettings, ViewToVec},
+  utils::limit_and_offset,
 };
-use serde::{Deserialize, Serialize};
-
-#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
-pub struct RegistrationApplicationView {
-  pub registration_application: RegistrationApplication,
-  pub creator_local_user: LocalUserSettings,
-  pub creator: PersonSafe,
-  pub admin: Option<PersonSafeAlias1>,
-}
 
 type RegistrationApplicationViewTuple = (
   RegistrationApplication,
@@ -177,13 +169,13 @@ mod tests {
     RegistrationApplicationView,
   };
   use lemmy_db_schema::{
-    establish_unpooled_connection,
     source::{
       local_user::{LocalUser, LocalUserForm, LocalUserSettings},
       person::*,
       registration_application::{RegistrationApplication, RegistrationApplicationForm},
     },
     traits::Crud,
+    utils::establish_unpooled_connection,
   };
   use serial_test::serial;
 
diff --git a/crates/db_views/src/site_view.rs b/crates/db_views/src/site_view.rs
index e3a55c6b..c630216f 100644
--- a/crates/db_views/src/site_view.rs
+++ b/crates/db_views/src/site_view.rs
@@ -1,16 +1,10 @@
+use crate::structs::SiteView;
 use diesel::{result::Error, *};
 use lemmy_db_schema::{
-  aggregates::site_aggregates::SiteAggregates,
+  aggregates::structs::SiteAggregates,
   schema::{site, site_aggregates},
   source::site::Site,
 };
-use serde::{Deserialize, Serialize};
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct SiteView {
-  pub site: Site,
-  pub counts: SiteAggregates,
-}
 
 impl SiteView {
   pub fn read_local(conn: &PgConnection) -> Result<Self, Error> {
diff --git a/crates/db_views/src/structs.rs b/crates/db_views/src/structs.rs
new file mode 100644
index 00000000..59a5f0fe
--- /dev/null
+++ b/crates/db_views/src/structs.rs
@@ -0,0 +1,107 @@
+use lemmy_db_schema::{
+  aggregates::structs::{CommentAggregates, PersonAggregates, PostAggregates, SiteAggregates},
+  source::{
+    comment::Comment,
+    comment_report::CommentReport,
+    community::CommunitySafe,
+    local_user::{LocalUser, LocalUserSettings},
+    person::{Person, PersonSafe, PersonSafeAlias1, PersonSafeAlias2},
+    post::Post,
+    post_report::PostReport,
+    private_message::PrivateMessage,
+    registration_application::RegistrationApplication,
+    site::Site,
+  },
+};
+use serde::{Deserialize, Serialize};
+
+#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
+pub struct CommentReportView {
+  pub comment_report: CommentReport,
+  pub comment: Comment,
+  pub post: Post,
+  pub community: CommunitySafe,
+  pub creator: PersonSafe,
+  pub comment_creator: PersonSafeAlias1,
+  pub counts: CommentAggregates,
+  pub creator_banned_from_community: bool, // Left Join to CommunityPersonBan
+  pub my_vote: Option<i16>,                // Left join to CommentLike
+  pub resolver: Option<PersonSafeAlias2>,
+}
+
+#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
+pub struct CommentView {
+  pub comment: Comment,
+  pub creator: PersonSafe,
+  pub recipient: Option<PersonSafeAlias1>, // Left joins to comment and person
+  pub post: Post,
+  pub community: CommunitySafe,
+  pub counts: CommentAggregates,
+  pub creator_banned_from_community: bool, // Left Join to CommunityPersonBan
+  pub subscribed: bool,                    // Left join to CommunityFollower
+  pub saved: bool,                         // Left join to CommentSaved
+  pub creator_blocked: bool,               // Left join to PersonBlock
+  pub my_vote: Option<i16>,                // Left join to CommentLike
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct LocalUserView {
+  pub local_user: LocalUser,
+  pub person: Person,
+  pub counts: PersonAggregates,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct LocalUserSettingsView {
+  pub local_user: LocalUserSettings,
+  pub person: PersonSafe,
+  pub counts: PersonAggregates,
+}
+
+#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
+pub struct PostReportView {
+  pub post_report: PostReport,
+  pub post: Post,
+  pub community: CommunitySafe,
+  pub creator: PersonSafe,
+  pub post_creator: PersonSafeAlias1,
+  pub creator_banned_from_community: bool,
+  pub my_vote: Option<i16>,
+  pub counts: PostAggregates,
+  pub resolver: Option<PersonSafeAlias2>,
+}
+
+#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
+pub struct PostView {
+  pub post: Post,
+  pub creator: PersonSafe,
+  pub community: CommunitySafe,
+  pub creator_banned_from_community: bool, // Left Join to CommunityPersonBan
+  pub counts: PostAggregates,
+  pub subscribed: bool,      // Left join to CommunityFollower
+  pub saved: bool,           // Left join to PostSaved
+  pub read: bool,            // Left join to PostRead
+  pub creator_blocked: bool, // Left join to PersonBlock
+  pub my_vote: Option<i16>,  // Left join to PostLike
+}
+
+#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
+pub struct PrivateMessageView {
+  pub private_message: PrivateMessage,
+  pub creator: PersonSafe,
+  pub recipient: PersonSafeAlias1,
+}
+
+#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
+pub struct RegistrationApplicationView {
+  pub registration_application: RegistrationApplication,
+  pub creator_local_user: LocalUserSettings,
+  pub creator: PersonSafe,
+  pub admin: Option<PersonSafeAlias1>,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct SiteView {
+  pub site: Site,
+  pub counts: SiteAggregates,
+}
diff --git a/crates/db_views_actor/Cargo.toml b/crates/db_views_actor/Cargo.toml
index 81c83761..b8810064 100644
--- a/crates/db_views_actor/Cargo.toml
+++ b/crates/db_views_actor/Cargo.toml
@@ -10,7 +10,10 @@ documentation = "https://join-lemmy.org/docs/en/index.html"
 [lib]
 doctest = false
 
+[features]
+full = ["lemmy_db_schema/full", "diesel"]
+
 [dependencies]
 lemmy_db_schema = { version = "=0.16.3", path = "../db_schema" }
-diesel = { version = "1.4.8", features = ["postgres","chrono","r2d2","serde_json"] }
+diesel = { version = "1.4.8", features = ["postgres","chrono","r2d2","serde_json"], optional = true }
 serde = { version = "1.0.136", features = ["derive"] }
diff --git a/crates/db_views_actor/src/community_block_view.rs b/crates/db_views_actor/src/community_block_view.rs
index 8f0cfca7..7c753cd2 100644
--- a/crates/db_views_actor/src/community_block_view.rs
+++ b/crates/db_views_actor/src/community_block_view.rs
@@ -1,3 +1,4 @@
+use crate::structs::CommunityBlockView;
 use diesel::{result::Error, *};
 use lemmy_db_schema::{
   newtypes::PersonId,
@@ -8,13 +9,6 @@ use lemmy_db_schema::{
   },
   traits::{ToSafe, ViewToVec},
 };
-use serde::{Deserialize, Serialize};
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct CommunityBlockView {
-  pub person: PersonSafe,
-  pub community: CommunitySafe,
-}
 
 type CommunityBlockViewTuple = (PersonSafe, CommunitySafe);
 
diff --git a/crates/db_views_actor/src/community_follower_view.rs b/crates/db_views_actor/src/community_follower_view.rs
index 581d9b57..6d5d94a5 100644
--- a/crates/db_views_actor/src/community_follower_view.rs
+++ b/crates/db_views_actor/src/community_follower_view.rs
@@ -1,3 +1,4 @@
+use crate::structs::CommunityFollowerView;
 use diesel::{result::Error, *};
 use lemmy_db_schema::{
   newtypes::{CommunityId, PersonId},
@@ -8,13 +9,6 @@ use lemmy_db_schema::{
   },
   traits::{ToSafe, ViewToVec},
 };
-use serde::{Deserialize, Serialize};
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct CommunityFollowerView {
-  pub community: CommunitySafe,
-  pub follower: PersonSafe,
-}
 
 type CommunityFollowerViewTuple = (CommunitySafe, PersonSafe);
 
diff --git a/crates/db_views_actor/src/community_moderator_view.rs b/crates/db_views_actor/src/community_moderator_view.rs
index 1e48ad0a..13e7d8cf 100644
--- a/crates/db_views_actor/src/community_moderator_view.rs
+++ b/crates/db_views_actor/src/community_moderator_view.rs
@@ -1,3 +1,4 @@
+use crate::structs::CommunityModeratorView;
 use diesel::{result::Error, *};
 use lemmy_db_schema::{
   newtypes::{CommunityId, PersonId},
@@ -8,13 +9,6 @@ use lemmy_db_schema::{
   },
   traits::{ToSafe, ViewToVec},
 };
-use serde::{Deserialize, Serialize};
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct CommunityModeratorView {
-  pub community: CommunitySafe,
-  pub moderator: PersonSafe,
-}
 
 type CommunityModeratorViewTuple = (CommunitySafe, PersonSafe);
 
diff --git a/crates/db_views_actor/src/community_person_ban_view.rs b/crates/db_views_actor/src/community_person_ban_view.rs
index 6c67bd82..6d765e07 100644
--- a/crates/db_views_actor/src/community_person_ban_view.rs
+++ b/crates/db_views_actor/src/community_person_ban_view.rs
@@ -1,3 +1,4 @@
+use crate::structs::CommunityPersonBanView;
 use diesel::{dsl::*, result::Error, *};
 use lemmy_db_schema::{
   newtypes::{CommunityId, PersonId},
@@ -8,13 +9,6 @@ use lemmy_db_schema::{
   },
   traits::ToSafe,
 };
-use serde::{Deserialize, Serialize};
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct CommunityPersonBanView {
-  pub community: CommunitySafe,
-  pub person: PersonSafe,
-}
 
 impl CommunityPersonBanView {
   pub fn get(
diff --git a/crates/db_views_actor/src/community_view.rs b/crates/db_views_actor/src/community_view.rs
index 2063fb14..637a2d90 100644
--- a/crates/db_views_actor/src/community_view.rs
+++ b/crates/db_views_actor/src/community_view.rs
@@ -1,10 +1,7 @@
-use crate::{community_moderator_view::CommunityModeratorView, person_view::PersonViewSafe};
+use crate::structs::{CommunityModeratorView, CommunityView, PersonViewSafe};
 use diesel::{result::Error, *};
 use lemmy_db_schema::{
-  aggregates::community_aggregates::CommunityAggregates,
-  functions::hot_rank,
-  fuzzy_search,
-  limit_and_offset,
+  aggregates::structs::CommunityAggregates,
   newtypes::{CommunityId, PersonId},
   schema::{community, community_aggregates, community_block, community_follower, local_user},
   source::{
@@ -12,18 +9,8 @@ use lemmy_db_schema::{
     community_block::CommunityBlock,
   },
   traits::{MaybeOptional, ToSafe, ViewToVec},
-  ListingType,
-  SortType,
+  utils::{functions::hot_rank, fuzzy_search, limit_and_offset, ListingType, SortType},
 };
-use serde::{Deserialize, Serialize};
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct CommunityView {
-  pub community: CommunitySafe,
-  pub subscribed: bool,
-  pub blocked: bool,
-  pub counts: CommunityAggregates,
-}
 
 type CommunityViewTuple = (
   CommunitySafe,
diff --git a/crates/db_views_actor/src/lib.rs b/crates/db_views_actor/src/lib.rs
index 6411feee..878979f7 100644
--- a/crates/db_views_actor/src/lib.rs
+++ b/crates/db_views_actor/src/lib.rs
@@ -1,8 +1,17 @@
+#[cfg(feature = "full")]
 pub mod community_block_view;
+#[cfg(feature = "full")]
 pub mod community_follower_view;
+#[cfg(feature = "full")]
 pub mod community_moderator_view;
+#[cfg(feature = "full")]
 pub mod community_person_ban_view;
+#[cfg(feature = "full")]
 pub mod community_view;
+#[cfg(feature = "full")]
 pub mod person_block_view;
+#[cfg(feature = "full")]
 pub mod person_mention_view;
+#[cfg(feature = "full")]
 pub mod person_view;
+pub mod structs;
diff --git a/crates/db_views_actor/src/person_block_view.rs b/crates/db_views_actor/src/person_block_view.rs
index 2ca2077c..b93f8167 100644
--- a/crates/db_views_actor/src/person_block_view.rs
+++ b/crates/db_views_actor/src/person_block_view.rs
@@ -1,3 +1,4 @@
+use crate::structs::PersonBlockView;
 use diesel::{result::Error, *};
 use lemmy_db_schema::{
   newtypes::PersonId,
@@ -5,13 +6,6 @@ use lemmy_db_schema::{
   source::person::{Person, PersonAlias1, PersonSafe, PersonSafeAlias1},
   traits::{ToSafe, ViewToVec},
 };
-use serde::{Deserialize, Serialize};
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct PersonBlockView {
-  pub person: PersonSafe,
-  pub target: PersonSafeAlias1,
-}
 
 type PersonBlockViewTuple = (PersonSafe, PersonSafeAlias1);
 
diff --git a/crates/db_views_actor/src/person_mention_view.rs b/crates/db_views_actor/src/person_mention_view.rs
index 0be446a9..59e90113 100644
--- a/crates/db_views_actor/src/person_mention_view.rs
+++ b/crates/db_views_actor/src/person_mention_view.rs
@@ -1,8 +1,7 @@
+use crate::structs::PersonMentionView;
 use diesel::{dsl::*, result::Error, *};
 use lemmy_db_schema::{
-  aggregates::comment_aggregates::CommentAggregates,
-  functions::hot_rank,
-  limit_and_offset,
+  aggregates::structs::CommentAggregates,
   newtypes::{PersonId, PersonMentionId},
   schema::{
     comment,
@@ -27,25 +26,8 @@ use lemmy_db_schema::{
     post::Post,
   },
   traits::{MaybeOptional, ToSafe, ViewToVec},
-  SortType,
+  utils::{functions::hot_rank, limit_and_offset, SortType},
 };
-use serde::{Deserialize, Serialize};
-
-#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
-pub struct PersonMentionView {
-  pub person_mention: PersonMention,
-  pub comment: Comment,
-  pub creator: PersonSafe,
-  pub post: Post,
-  pub community: CommunitySafe,
-  pub recipient: PersonSafeAlias1,
-  pub counts: CommentAggregates,
-  pub creator_banned_from_community: bool, // Left Join to CommunityPersonBan
-  pub subscribed: bool,                    // Left join to CommunityFollower
-  pub saved: bool,                         // Left join to CommentSaved
-  pub creator_blocked: bool,               // Left join to PersonBlock
-  pub my_vote: Option<i16>,                // Left join to CommentLike
-}
 
 type PersonMentionViewTuple = (
   PersonMention,
diff --git a/crates/db_views_actor/src/person_view.rs b/crates/db_views_actor/src/person_view.rs
index d4be0585..1849f80b 100644
--- a/crates/db_views_actor/src/person_view.rs
+++ b/crates/db_views_actor/src/person_view.rs
@@ -1,21 +1,13 @@
+use crate::structs::PersonViewSafe;
 use diesel::{dsl::*, result::Error, *};
 use lemmy_db_schema::{
-  aggregates::person_aggregates::PersonAggregates,
-  fuzzy_search,
-  limit_and_offset,
+  aggregates::structs::PersonAggregates,
   newtypes::PersonId,
   schema::{person, person_aggregates},
   source::person::{Person, PersonSafe},
   traits::{MaybeOptional, ToSafe, ViewToVec},
-  SortType,
+  utils::{fuzzy_search, limit_and_offset, SortType},
 };
-use serde::{Deserialize, Serialize};
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct PersonViewSafe {
-  pub person: PersonSafe,
-  pub counts: PersonAggregates,
-}
 
 type PersonViewSafeTuple = (PersonSafe, PersonAggregates);
 
diff --git a/crates/db_views_actor/src/structs.rs b/crates/db_views_actor/src/structs.rs
new file mode 100644
index 00000000..a6ec9710
--- /dev/null
+++ b/crates/db_views_actor/src/structs.rs
@@ -0,0 +1,71 @@
+use lemmy_db_schema::{
+  aggregates::structs::{CommentAggregates, CommunityAggregates, PersonAggregates},
+  source::{
+    comment::Comment,
+    community::CommunitySafe,
+    person::{PersonSafe, PersonSafeAlias1},
+    person_mention::PersonMention,
+    post::Post,
+  },
+};
+use serde::{Deserialize, Serialize};
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct CommunityBlockView {
+  pub person: PersonSafe,
+  pub community: CommunitySafe,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct CommunityFollowerView {
+  pub community: CommunitySafe,
+  pub follower: PersonSafe,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct CommunityModeratorView {
+  pub community: CommunitySafe,
+  pub moderator: PersonSafe,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct CommunityPersonBanView {
+  pub community: CommunitySafe,
+  pub person: PersonSafe,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct CommunityView {
+  pub community: CommunitySafe,
+  pub subscribed: bool,
+  pub blocked: bool,
+  pub counts: CommunityAggregates,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct PersonBlockView {
+  pub person: PersonSafe,
+  pub target: PersonSafeAlias1,
+}
+
+#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
+pub struct PersonMentionView {
+  pub person_mention: PersonMention,
+  pub comment: Comment,
+  pub creator: PersonSafe,
+  pub post: Post,
+  pub community: CommunitySafe,
+  pub recipient: PersonSafeAlias1,
+  pub counts: CommentAggregates,
+  pub creator_banned_from_community: bool, // Left Join to CommunityPersonBan
+  pub subscribed: bool,                    // Left join to CommunityFollower
+  pub saved: bool,                         // Left join to CommentSaved
+  pub creator_blocked: bool,               // Left join to PersonBlock
+  pub my_vote: Option<i16>,                // Left join to CommentLike
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct PersonViewSafe {
+  pub person: PersonSafe,
+  pub counts: PersonAggregates,
+}
diff --git a/crates/db_views_moderator/Cargo.toml b/crates/db_views_moderator/Cargo.toml
index 2e549392..110baaeb 100644
--- a/crates/db_views_moderator/Cargo.toml
+++ b/crates/db_views_moderator/Cargo.toml
@@ -10,7 +10,10 @@ documentation = "https://join-lemmy.org/docs/en/index.html"
 [lib]
 doctest = false
 
+[features]
+full = ["lemmy_db_schema/full", "diesel"]
+
 [dependencies]
 lemmy_db_schema = { version = "=0.16.3", path = "../db_schema" }
-diesel = { version = "1.4.8", features = ["postgres","chrono","r2d2","serde_json"] }
+diesel = { version = "1.4.8", features = ["postgres","chrono","r2d2","serde_json"], optional = true }
 serde = { version = "1.0.136", features = ["derive"] }
diff --git a/crates/db_views_moderator/src/lib.rs b/crates/db_views_moderator/src/lib.rs
index ce51617c..c6f32426 100644
--- a/crates/db_views_moderator/src/lib.rs
+++ b/crates/db_views_moderator/src/lib.rs
@@ -1,11 +1,23 @@
+#[cfg(feature = "full")]
 pub mod mod_add_community_view;
+#[cfg(feature = "full")]
 pub mod mod_add_view;
+#[cfg(feature = "full")]
 pub mod mod_ban_from_community_view;
+#[cfg(feature = "full")]
 pub mod mod_ban_view;
+#[cfg(feature = "full")]
 pub mod mod_hide_community_view;
+#[cfg(feature = "full")]
 pub mod mod_lock_post_view;
+#[cfg(feature = "full")]
 pub mod mod_remove_comment_view;
+#[cfg(feature = "full")]
 pub mod mod_remove_community_view;
+#[cfg(feature = "full")]
 pub mod mod_remove_post_view;
+#[cfg(feature = "full")]
 pub mod mod_sticky_post_view;
+#[cfg(feature = "full")]
 pub mod mod_transfer_community_view;
+pub mod structs;
diff --git a/crates/db_views_moderator/src/mod_add_community_view.rs b/crates/db_views_moderator/src/mod_add_community_view.rs
index b0d72d21..55bb9d99 100644
--- a/crates/db_views_moderator/src/mod_add_community_view.rs
+++ b/crates/db_views_moderator/src/mod_add_community_view.rs
@@ -1,6 +1,6 @@
+use crate::structs::ModAddCommunityView;
 use diesel::{result::Error, *};
 use lemmy_db_schema::{
-  limit_and_offset,
   newtypes::{CommunityId, PersonId},
   schema::{community, mod_add_community, person, person_alias_1},
   source::{
@@ -9,16 +9,8 @@ use lemmy_db_schema::{
     person::{Person, PersonAlias1, PersonSafe, PersonSafeAlias1},
   },
   traits::{ToSafe, ViewToVec},
+  utils::limit_and_offset,
 };
-use serde::{Deserialize, Serialize};
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct ModAddCommunityView {
-  pub mod_add_community: ModAddCommunity,
-  pub moderator: PersonSafe,
-  pub community: CommunitySafe,
-  pub modded_person: PersonSafeAlias1,
-}
 
 type ModAddCommunityViewTuple = (ModAddCommunity, PersonSafe, CommunitySafe, PersonSafeAlias1);
 
diff --git a/crates/db_views_moderator/src/mod_add_view.rs b/crates/db_views_moderator/src/mod_add_view.rs
index bd4e388b..5b48d006 100644
--- a/crates/db_views_moderator/src/mod_add_view.rs
+++ b/crates/db_views_moderator/src/mod_add_view.rs
@@ -1,6 +1,6 @@
+use crate::structs::ModAddView;
 use diesel::{result::Error, *};
 use lemmy_db_schema::{
-  limit_and_offset,
   newtypes::PersonId,
   schema::{mod_add, person, person_alias_1},
   source::{
@@ -8,15 +8,8 @@ use lemmy_db_schema::{
     person::{Person, PersonAlias1, PersonSafe, PersonSafeAlias1},
   },
   traits::{ToSafe, ViewToVec},
+  utils::limit_and_offset,
 };
-use serde::{Deserialize, Serialize};
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct ModAddView {
-  pub mod_add: ModAdd,
-  pub moderator: PersonSafe,
-  pub modded_person: PersonSafeAlias1,
-}
 
 type ModAddViewTuple = (ModAdd, PersonSafe, PersonSafeAlias1);
 
diff --git a/crates/db_views_moderator/src/mod_ban_from_community_view.rs b/crates/db_views_moderator/src/mod_ban_from_community_view.rs
index a5a37cce..9e44e479 100644
--- a/crates/db_views_moderator/src/mod_ban_from_community_view.rs
+++ b/crates/db_views_moderator/src/mod_ban_from_community_view.rs
@@ -1,6 +1,6 @@
+use crate::structs::ModBanFromCommunityView;
 use diesel::{result::Error, *};
 use lemmy_db_schema::{
-  limit_and_offset,
   newtypes::{CommunityId, PersonId},
   schema::{community, mod_ban_from_community, person, person_alias_1},
   source::{
@@ -9,16 +9,8 @@ use lemmy_db_schema::{
     person::{Person, PersonAlias1, PersonSafe, PersonSafeAlias1},
   },
   traits::{ToSafe, ViewToVec},
+  utils::limit_and_offset,
 };
-use serde::{Deserialize, Serialize};
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct ModBanFromCommunityView {
-  pub mod_ban_from_community: ModBanFromCommunity,
-  pub moderator: PersonSafe,
-  pub community: CommunitySafe,
-  pub banned_person: PersonSafeAlias1,
-}
 
 type ModBanFromCommunityViewTuple = (
   ModBanFromCommunity,
diff --git a/crates/db_views_moderator/src/mod_ban_view.rs b/crates/db_views_moderator/src/mod_ban_view.rs
index fabb56ca..a36adaad 100644
--- a/crates/db_views_moderator/src/mod_ban_view.rs
+++ b/crates/db_views_moderator/src/mod_ban_view.rs
@@ -1,6 +1,6 @@
+use crate::structs::ModBanView;
 use diesel::{result::Error, *};
 use lemmy_db_schema::{
-  limit_and_offset,
   newtypes::PersonId,
   schema::{mod_ban, person, person_alias_1},
   source::{
@@ -8,15 +8,8 @@ use lemmy_db_schema::{
     person::{Person, PersonAlias1, PersonSafe, PersonSafeAlias1},
   },
   traits::{ToSafe, ViewToVec},
+  utils::limit_and_offset,
 };
-use serde::{Deserialize, Serialize};
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct ModBanView {
-  pub mod_ban: ModBan,
-  pub moderator: PersonSafe,
-  pub banned_person: PersonSafeAlias1,
-}
 
 type ModBanViewTuple = (ModBan, PersonSafe, PersonSafeAlias1);
 
diff --git a/crates/db_views_moderator/src/mod_hide_community_view.rs b/crates/db_views_moderator/src/mod_hide_community_view.rs
index c7aba772..a8380562 100644
--- a/crates/db_views_moderator/src/mod_hide_community_view.rs
+++ b/crates/db_views_moderator/src/mod_hide_community_view.rs
@@ -1,6 +1,6 @@
+use crate::structs::ModHideCommunityView;
 use diesel::{result::Error, *};
 use lemmy_db_schema::{
-  limit_and_offset,
   newtypes::{CommunityId, PersonId},
   schema::{community, mod_hide_community, person},
   source::{
@@ -9,15 +9,8 @@ use lemmy_db_schema::{
     person::{Person, PersonSafe},
   },
   traits::{ToSafe, ViewToVec},
+  utils::limit_and_offset,
 };
-use serde::{Deserialize, Serialize};
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct ModHideCommunityView {
-  pub mod_hide_community: ModHideCommunity,
-  pub admin: PersonSafe,
-  pub community: CommunitySafe,
-}
 
 type ModHideCommunityViewTuple = (ModHideCommunity, PersonSafe, CommunitySafe);
 
diff --git a/crates/db_views_moderator/src/mod_lock_post_view.rs b/crates/db_views_moderator/src/mod_lock_post_view.rs
index 5ec35579..e9ec5f66 100644
--- a/crates/db_views_moderator/src/mod_lock_post_view.rs
+++ b/crates/db_views_moderator/src/mod_lock_post_view.rs
@@ -1,6 +1,6 @@
+use crate::structs::ModLockPostView;
 use diesel::{result::Error, *};
 use lemmy_db_schema::{
-  limit_and_offset,
   newtypes::{CommunityId, PersonId},
   schema::{community, mod_lock_post, person, post},
   source::{
@@ -10,16 +10,8 @@ use lemmy_db_schema::{
     post::Post,
   },
   traits::{ToSafe, ViewToVec},
+  utils::limit_and_offset,
 };
-use serde::{Deserialize, Serialize};
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct ModLockPostView {
-  pub mod_lock_post: ModLockPost,
-  pub moderator: PersonSafe,
-  pub post: Post,
-  pub community: CommunitySafe,
-}
 
 type ModLockPostViewTuple = (ModLockPost, PersonSafe, Post, CommunitySafe);
 
diff --git a/crates/db_views_moderator/src/mod_remove_comment_view.rs b/crates/db_views_moderator/src/mod_remove_comment_view.rs
index abb88020..b2b14f22 100644
--- a/crates/db_views_moderator/src/mod_remove_comment_view.rs
+++ b/crates/db_views_moderator/src/mod_remove_comment_view.rs
@@ -1,6 +1,6 @@
+use crate::structs::ModRemoveCommentView;
 use diesel::{result::Error, *};
 use lemmy_db_schema::{
-  limit_and_offset,
   newtypes::{CommunityId, PersonId},
   schema::{comment, community, mod_remove_comment, person, person_alias_1, post},
   source::{
@@ -11,18 +11,8 @@ use lemmy_db_schema::{
     post::Post,
   },
   traits::{ToSafe, ViewToVec},
+  utils::limit_and_offset,
 };
-use serde::{Deserialize, Serialize};
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct ModRemoveCommentView {
-  pub mod_remove_comment: ModRemoveComment,
-  pub moderator: PersonSafe,
-  pub comment: Comment,
-  pub commenter: PersonSafeAlias1,
-  pub post: Post,
-  pub community: CommunitySafe,
-}
 
 type ModRemoveCommentViewTuple = (
   ModRemoveComment,
diff --git a/crates/db_views_moderator/src/mod_remove_community_view.rs b/crates/db_views_moderator/src/mod_remove_community_view.rs
index 6634b2ff..29aad7f9 100644
--- a/crates/db_views_moderator/src/mod_remove_community_view.rs
+++ b/crates/db_views_moderator/src/mod_remove_community_view.rs
@@ -1,6 +1,6 @@
+use crate::structs::ModRemoveCommunityView;
 use diesel::{result::Error, *};
 use lemmy_db_schema::{
-  limit_and_offset,
   newtypes::PersonId,
   schema::{community, mod_remove_community, person},
   source::{
@@ -9,15 +9,8 @@ use lemmy_db_schema::{
     person::{Person, PersonSafe},
   },
   traits::{ToSafe, ViewToVec},
+  utils::limit_and_offset,
 };
-use serde::{Deserialize, Serialize};
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct ModRemoveCommunityView {
-  pub mod_remove_community: ModRemoveCommunity,
-  pub moderator: PersonSafe,
-  pub community: CommunitySafe,
-}
 
 type ModRemoveCommunityTuple = (ModRemoveCommunity, PersonSafe, CommunitySafe);
 
diff --git a/crates/db_views_moderator/src/mod_remove_post_view.rs b/crates/db_views_moderator/src/mod_remove_post_view.rs
index c9219161..849b5639 100644
--- a/crates/db_views_moderator/src/mod_remove_post_view.rs
+++ b/crates/db_views_moderator/src/mod_remove_post_view.rs
@@ -1,6 +1,6 @@
+use crate::structs::ModRemovePostView;
 use diesel::{result::Error, *};
 use lemmy_db_schema::{
-  limit_and_offset,
   newtypes::{CommunityId, PersonId},
   schema::{community, mod_remove_post, person, post},
   source::{
@@ -10,16 +10,8 @@ use lemmy_db_schema::{
     post::Post,
   },
   traits::{ToSafe, ViewToVec},
+  utils::limit_and_offset,
 };
-use serde::{Deserialize, Serialize};
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct ModRemovePostView {
-  pub mod_remove_post: ModRemovePost,
-  pub moderator: PersonSafe,
-  pub post: Post,
-  pub community: CommunitySafe,
-}
 
 type ModRemovePostViewTuple = (ModRemovePost, PersonSafe, Post, CommunitySafe);
 
diff --git a/crates/db_views_moderator/src/mod_sticky_post_view.rs b/crates/db_views_moderator/src/mod_sticky_post_view.rs
index 55593e4f..8b2383ee 100644
--- a/crates/db_views_moderator/src/mod_sticky_post_view.rs
+++ b/crates/db_views_moderator/src/mod_sticky_post_view.rs
@@ -1,6 +1,6 @@
+use crate::structs::ModStickyPostView;
 use diesel::{result::Error, *};
 use lemmy_db_schema::{
-  limit_and_offset,
   newtypes::{CommunityId, PersonId},
   schema::{community, mod_sticky_post, person, post},
   source::{
@@ -10,16 +10,8 @@ use lemmy_db_schema::{
     post::Post,
   },
   traits::{ToSafe, ViewToVec},
+  utils::limit_and_offset,
 };
-use serde::{Deserialize, Serialize};
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct ModStickyPostView {
-  pub mod_sticky_post: ModStickyPost,
-  pub moderator: PersonSafe,
-  pub post: Post,
-  pub community: CommunitySafe,
-}
 
 type ModStickyPostViewTuple = (ModStickyPost, PersonSafe, Post, CommunitySafe);
 
diff --git a/crates/db_views_moderator/src/mod_transfer_community_view.rs b/crates/db_views_moderator/src/mod_transfer_community_view.rs
index 2fbacbb8..c944a9a1 100644
--- a/crates/db_views_moderator/src/mod_transfer_community_view.rs
+++ b/crates/db_views_moderator/src/mod_transfer_community_view.rs
@@ -1,6 +1,6 @@
+use crate::structs::ModTransferCommunityView;
 use diesel::{result::Error, *};
 use lemmy_db_schema::{
-  limit_and_offset,
   newtypes::{CommunityId, PersonId},
   schema::{community, mod_transfer_community, person, person_alias_1},
   source::{
@@ -9,16 +9,8 @@ use lemmy_db_schema::{
     person::{Person, PersonAlias1, PersonSafe, PersonSafeAlias1},
   },
   traits::{ToSafe, ViewToVec},
+  utils::limit_and_offset,
 };
-use serde::{Deserialize, Serialize};
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct ModTransferCommunityView {
-  pub mod_transfer_community: ModTransferCommunity,
-  pub moderator: PersonSafe,
-  pub community: CommunitySafe,
-  pub modded_person: PersonSafeAlias1,
-}
 
 type ModTransferCommunityViewTuple = (
   ModTransferCommunity,
diff --git a/crates/db_views_moderator/src/structs.rs b/crates/db_views_moderator/src/structs.rs
new file mode 100644
index 00000000..9d525b57
--- /dev/null
+++ b/crates/db_views_moderator/src/structs.rs
@@ -0,0 +1,106 @@
+use lemmy_db_schema::source::{
+  comment::Comment,
+  community::CommunitySafe,
+  moderator::{
+    ModAdd,
+    ModAddCommunity,
+    ModBan,
+    ModBanFromCommunity,
+    ModHideCommunity,
+    ModLockPost,
+    ModRemoveComment,
+    ModRemoveCommunity,
+    ModRemovePost,
+    ModStickyPost,
+    ModTransferCommunity,
+  },
+  person::{PersonSafe, PersonSafeAlias1},
+  post::Post,
+};
+use serde::{Deserialize, Serialize};
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct ModAddCommunityView {
+  pub mod_add_community: ModAddCommunity,
+  pub moderator: PersonSafe,
+  pub community: CommunitySafe,
+  pub modded_person: PersonSafeAlias1,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct ModAddView {
+  pub mod_add: ModAdd,
+  pub moderator: PersonSafe,
+  pub modded_person: PersonSafeAlias1,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct ModBanFromCommunityView {
+  pub mod_ban_from_community: ModBanFromCommunity,
+  pub moderator: PersonSafe,
+  pub community: CommunitySafe,
+  pub banned_person: PersonSafeAlias1,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct ModBanView {
+  pub mod_ban: ModBan,
+  pub moderator: PersonSafe,
+  pub banned_person: PersonSafeAlias1,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct ModHideCommunityView {
+  pub mod_hide_community: ModHideCommunity,
+  pub admin: PersonSafe,
+  pub community: CommunitySafe,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct ModLockPostView {
+  pub mod_lock_post: ModLockPost,
+  pub moderator: PersonSafe,
+  pub post: Post,
+  pub community: CommunitySafe,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct ModRemoveCommentView {
+  pub mod_remove_comment: ModRemoveComment,
+  pub moderator: PersonSafe,
+  pub comment: Comment,
+  pub commenter: PersonSafeAlias1,
+  pub post: Post,
+  pub community: CommunitySafe,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct ModRemoveCommunityView {
+  pub mod_remove_community: ModRemoveCommunity,
+  pub moderator: PersonSafe,
+  pub community: CommunitySafe,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct ModRemovePostView {
+  pub mod_remove_post: ModRemovePost,
+  pub moderator: PersonSafe,
+  pub post: Post,
+  pub community: CommunitySafe,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct ModStickyPostView {
+  pub mod_sticky_post: ModStickyPost,
+  pub moderator: PersonSafe,
+  pub post: Post,
+  pub community: CommunitySafe,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct ModTransferCommunityView {
+  pub mod_transfer_community: ModTransferCommunity,
+  pub moderator: PersonSafe,
+  pub community: CommunitySafe,
+  pub modded_person: PersonSafeAlias1,
+}
diff --git a/crates/routes/Cargo.toml b/crates/routes/Cargo.toml
index f1bf6edc..2ade2385 100644
--- a/crates/routes/Cargo.toml
+++ b/crates/routes/Cargo.toml
@@ -21,7 +21,7 @@ lemmy_apub = { version = "=0.16.3", path = "../apub" }
 diesel = "1.4.8"
 actix-web = { version = "4.0.1", default-features = false, features = ["rustls"] }
 anyhow = "1.0.56"
-chrono = { version = "0.4.19", features = ["serde"] }
+chrono = { version = "0.4.19", features = ["serde"], default-features = false }
 futures = "0.3.21"
 reqwest = { version = "0.11.10", features = ["stream"] }
 reqwest-middleware = "0.1.5"
diff --git a/crates/routes/src/feeds.rs b/crates/routes/src/feeds.rs
index bfe49fbd..1bcfc6c3 100644
--- a/crates/routes/src/feeds.rs
+++ b/crates/routes/src/feeds.rs
@@ -2,20 +2,22 @@ use actix_web::{error::ErrorBadRequest, *};
 use anyhow::anyhow;
 use chrono::{DateTime, NaiveDateTime, Utc};
 use diesel::PgConnection;
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_db_schema::{
   newtypes::LocalUserId,
   source::{community::Community, local_user::LocalUser, person::Person},
   traits::{ApubActor, Crud},
-  ListingType,
-  SortType,
+  utils::{ListingType, SortType},
 };
 use lemmy_db_views::{
-  comment_view::{CommentQueryBuilder, CommentView},
-  post_view::{PostQueryBuilder, PostView},
-  site_view::SiteView,
+  comment_view::CommentQueryBuilder,
+  post_view::PostQueryBuilder,
+  structs::{CommentView, PostView, SiteView},
+};
+use lemmy_db_views_actor::{
+  person_mention_view::PersonMentionQueryBuilder,
+  structs::PersonMentionView,
 };
-use lemmy_db_views_actor::person_mention_view::{PersonMentionQueryBuilder, PersonMentionView};
 use lemmy_utils::{claims::Claims, utils::markdown_to_html, LemmyError};
 use lemmy_websocket::LemmyContext;
 use once_cell::sync::Lazy;
diff --git a/crates/routes/src/nodeinfo.rs b/crates/routes/src/nodeinfo.rs
index 6e216f44..6ca2114e 100644
--- a/crates/routes/src/nodeinfo.rs
+++ b/crates/routes/src/nodeinfo.rs
@@ -1,7 +1,7 @@
 use actix_web::{error::ErrorBadRequest, *};
 use anyhow::anyhow;
-use lemmy_api_common::blocking;
-use lemmy_db_views::site_view::SiteView;
+use lemmy_api_common::utils::blocking;
+use lemmy_db_views::structs::SiteView;
 use lemmy_utils::{version, LemmyError};
 use lemmy_websocket::LemmyContext;
 use serde::{Deserialize, Serialize};
diff --git a/crates/routes/src/webfinger.rs b/crates/routes/src/webfinger.rs
index a95902b2..01ed2bc0 100644
--- a/crates/routes/src/webfinger.rs
+++ b/crates/routes/src/webfinger.rs
@@ -1,6 +1,6 @@
 use actix_web::{web, web::Query, HttpResponse};
 use anyhow::Context;
-use lemmy_api_common::blocking;
+use lemmy_api_common::utils::blocking;
 use lemmy_apub::fetcher::webfinger::{WebfingerLink, WebfingerResponse};
 use lemmy_db_schema::{
   source::{community::Community, person::Person},
diff --git a/crates/utils/Cargo.toml b/crates/utils/Cargo.toml
index 3984b897..18968897 100644
--- a/crates/utils/Cargo.toml
+++ b/crates/utils/Cargo.toml
@@ -14,24 +14,20 @@ doctest = false
 
 [dependencies]
 regex = "1.5.5"
-chrono = { version = "0.4.19", features = ["serde"] }
+chrono = { version = "0.4.19", features = ["serde"], default-features = false }
 lettre = "0.10.0-rc.4"
 tracing = "0.1.32"
 tracing-error = "0.2.0"
 itertools = "0.10.3"
 rand = "0.8.5"
-percent-encoding = "2.1.0"
 serde = { version = "1.0.136", features = ["derive"] }
 serde_json = { version = "1.0.79", features = ["preserve_order"] }
-thiserror = "1.0.30"
 comrak = { version = "0.12.1", default-features = false }
 once_cell = "1.10.0"
 openssl = "0.10.38"
 url = { version = "2.2.2", features = ["serde"] }
 actix-web = { version = "4.0.1", default-features = false, features = ["rustls"] }
-actix-rt = { version = "2.7.0", default-features = false }
 anyhow = "1.0.56"
-reqwest = { version = "0.11.10", features = ["json"] }
 reqwest-middleware = "0.1.5"
 strum = "0.24.0"
 strum_macros = "0.24.0"
@@ -40,11 +36,9 @@ diesel = "1.4.8"
 http = "0.2.6"
 deser-hjson = "1.0.2"
 smart-default = "0.6.0"
-webpage = { version = "1.4.0", default-features = false, features = ["serde"] }
 jsonwebtoken = "8.0.1"
 doku = "0.11.0"
 uuid = { version = "0.8.2", features = ["serde", "v4"] }
-encoding = "0.2.33"
 html2text = "0.3.1"
 rosetta-i18n = "0.1.2"
 parking_lot = "0.12.0"
diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs
index 1dbfb25b..b8e301bd 100644
--- a/crates/utils/src/lib.rs
+++ b/crates/utils/src/lib.rs
@@ -6,19 +6,15 @@ extern crate smart_default;
 pub mod apub;
 pub mod email;
 pub mod rate_limit;
-pub mod request;
 pub mod settings;
 
 pub mod claims;
+pub mod request;
 #[cfg(test)]
 mod test;
 pub mod utils;
 pub mod version;
 
-mod sensitive;
-
-pub use sensitive::Sensitive;
-
 use actix_web::HttpResponse;
 use http::StatusCode;
 use std::{fmt, fmt::Display, time::Duration};
diff --git a/crates/utils/src/request.rs b/crates/utils/src/request.rs
index fb4949bd..e98e0e8a 100644
--- a/crates/utils/src/request.rs
+++ b/crates/utils/src/request.rs
@@ -1,22 +1,4 @@
-use crate::{settings::structs::Settings, version::VERSION, LemmyError, REQWEST_TIMEOUT};
-use anyhow::anyhow;
-use encoding::{all::encodings, DecoderTrap};
-use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
-use reqwest_middleware::ClientWithMiddleware;
-use serde::{Deserialize, Serialize};
 use std::future::Future;
-use thiserror::Error;
-use tracing::{error, info};
-use url::Url;
-use webpage::HTML;
-
-#[derive(Clone, Debug, Error)]
-#[error("Error sending request, {0}")]
-struct SendError(pub String);
-
-#[derive(Clone, Debug, Error)]
-#[error("Error receiving response, {0}")]
-pub struct RecvError(pub String);
 
 #[tracing::instrument(skip_all)]
 pub async fn retry<F, Fut, T>(f: F) -> Result<T, reqwest_middleware::Error>
@@ -53,285 +35,3 @@ where
 
   response.expect("retry http request")
 }
-
-#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
-pub struct SiteMetadata {
-  pub title: Option<String>,
-  pub description: Option<String>,
-  image: Option<Url>,
-  pub html: Option<String>,
-}
-
-/// Fetches the post link html tags (like title, description, image, etc)
-#[tracing::instrument(skip_all)]
-pub async fn fetch_site_metadata(
-  client: &ClientWithMiddleware,
-  url: &Url,
-) -> Result<SiteMetadata, LemmyError> {
-  info!("Fetching site metadata for url: {}", url);
-  let response = client
-    .get(url.as_str())
-    .timeout(REQWEST_TIMEOUT)
-    .send()
-    .await?;
-
-  // Can't use .text() here, because it only checks the content header, not the actual bytes
-  // https://github.com/LemmyNet/lemmy/issues/1964
-  let html_bytes = response
-    .bytes()
-    .await
-    .map_err(|e| RecvError(e.to_string()))?
-    .to_vec();
-
-  let tags = html_to_site_metadata(&html_bytes)?;
-
-  Ok(tags)
-}
-
-fn html_to_site_metadata(html_bytes: &[u8]) -> Result<SiteMetadata, LemmyError> {
-  let html = String::from_utf8_lossy(html_bytes);
-
-  // Make sure the first line is doctype html
-  let first_line = html
-    .trim_start()
-    .lines()
-    .into_iter()
-    .next()
-    .ok_or_else(|| LemmyError::from_message("No lines in html"))?
-    .to_lowercase();
-
-  if !first_line.starts_with("<!doctype html>") {
-    return Err(LemmyError::from_message(
-      "Site metadata page fetch is not DOCTYPE html",
-    ));
-  }
-
-  let mut page = HTML::from_string(html.to_string(), None)?;
-
-  // If the web page specifies that it isn't actually UTF-8, re-decode the received bytes with the
-  // proper encoding. If the specified encoding cannot be found, fall back to the original UTF-8
-  // version.
-  if let Some(charset) = page.meta.get("charset") {
-    if charset.to_lowercase() != "utf-8" {
-      if let Some(encoding_ref) = encodings().iter().find(|e| e.name() == charset) {
-        if let Ok(html_with_encoding) = encoding_ref.decode(html_bytes, DecoderTrap::Replace) {
-          page = HTML::from_string(html_with_encoding, None)?;
-        }
-      }
-    }
-  }
-
-  let page_title = page.title;
-  let page_description = page.description;
-
-  let og_description = page
-    .opengraph
-    .properties
-    .get("description")
-    .map(|t| t.to_string());
-  let og_title = page
-    .opengraph
-    .properties
-    .get("title")
-    .map(|t| t.to_string());
-  let og_image = page
-    .opengraph
-    .images
-    .get(0)
-    .and_then(|ogo| Url::parse(&ogo.url).ok());
-
-  let title = og_title.or(page_title);
-  let description = og_description.or(page_description);
-  let image = og_image;
-
-  Ok(SiteMetadata {
-    title,
-    description,
-    image,
-    html: None,
-  })
-}
-
-#[derive(Deserialize, Debug, Clone)]
-pub(crate) struct PictrsResponse {
-  files: Vec<PictrsFile>,
-  msg: String,
-}
-
-#[derive(Deserialize, Debug, Clone)]
-pub(crate) struct PictrsFile {
-  file: String,
-  #[allow(dead_code)]
-  delete_token: String,
-}
-
-#[tracing::instrument(skip_all)]
-pub(crate) async fn fetch_pictrs(
-  client: &ClientWithMiddleware,
-  settings: &Settings,
-  image_url: &Url,
-) -> Result<PictrsResponse, LemmyError> {
-  if let Some(pictrs_url) = settings.pictrs_url.to_owned() {
-    is_image_content_type(client, image_url).await?;
-
-    let fetch_url = format!(
-      "{}/image/download?url={}",
-      pictrs_url,
-      utf8_percent_encode(image_url.as_str(), NON_ALPHANUMERIC) // TODO this might not be needed
-    );
-
-    let response = client
-      .get(&fetch_url)
-      .timeout(REQWEST_TIMEOUT)
-      .send()
-      .await?;
-
-    let response: PictrsResponse = response
-      .json()
-      .await
-      .map_err(|e| RecvError(e.to_string()))?;
-
-    if response.msg == "ok" {
-      Ok(response)
-    } else {
-      Err(anyhow!("{}", &response.msg).into())
-    }
-  } else {
-    Err(anyhow!("pictrs_url not set up in config").into())
-  }
-}
-
-/// Both are options, since the URL might be either an html page, or an image
-/// Returns the SiteMetadata, and a Pictrs URL, if there is a picture associated
-#[tracing::instrument(skip_all)]
-pub async fn fetch_site_data(
-  client: &ClientWithMiddleware,
-  settings: &Settings,
-  url: Option<&Url>,
-) -> (Option<SiteMetadata>, Option<Url>) {
-  match &url {
-    Some(url) => {
-      // Fetch metadata
-      // Ignore errors, since it may be an image, or not have the data.
-      // Warning, this may ignore SSL errors
-      let metadata_option = fetch_site_metadata(client, url).await.ok();
-
-      // Fetch pictrs thumbnail
-      let pictrs_hash = match &metadata_option {
-        Some(metadata_res) => match &metadata_res.image {
-          // Metadata, with image
-          // Try to generate a small thumbnail if there's a full sized one from post-links
-          Some(metadata_image) => fetch_pictrs(client, settings, metadata_image)
-            .await
-            .map(|r| r.files[0].file.to_owned()),
-          // Metadata, but no image
-          None => fetch_pictrs(client, settings, url)
-            .await
-            .map(|r| r.files[0].file.to_owned()),
-        },
-        // No metadata, try to fetch the URL as an image
-        None => fetch_pictrs(client, settings, url)
-          .await
-          .map(|r| r.files[0].file.to_owned()),
-      };
-
-      // The full urls are necessary for federation
-      let pictrs_thumbnail = pictrs_hash
-        .map(|p| {
-          Url::parse(&format!(
-            "{}/pictrs/image/{}",
-            settings.get_protocol_and_hostname(),
-            p
-          ))
-          .ok()
-        })
-        .ok()
-        .flatten();
-
-      (metadata_option, pictrs_thumbnail)
-    }
-    None => (None, None),
-  }
-}
-
-#[tracing::instrument(skip_all)]
-async fn is_image_content_type(client: &ClientWithMiddleware, url: &Url) -> Result<(), LemmyError> {
-  let response = client
-    .get(url.as_str())
-    .timeout(REQWEST_TIMEOUT)
-    .send()
-    .await?;
-  if response
-    .headers()
-    .get("Content-Type")
-    .ok_or_else(|| anyhow!("No Content-Type header"))?
-    .to_str()?
-    .starts_with("image/")
-  {
-    Ok(())
-  } else {
-    Err(anyhow!("Not an image type.").into())
-  }
-}
-
-pub fn build_user_agent(settings: &Settings) -> String {
-  format!(
-    "Lemmy/{}; +{}",
-    VERSION,
-    settings.get_protocol_and_hostname()
-  )
-}
-
-#[cfg(test)]
-mod tests {
-  use crate::request::{build_user_agent, fetch_site_metadata};
-  use url::Url;
-
-  use super::SiteMetadata;
-  use crate::settings::structs::Settings;
-
-  // These helped with testing
-  #[actix_rt::test]
-  async fn test_site_metadata() {
-    let settings = Settings::init().unwrap();
-    let client = reqwest::Client::builder()
-      .user_agent(build_user_agent(&settings))
-      .build()
-      .unwrap()
-      .into();
-    let sample_url = Url::parse("https://gitlab.com/IzzyOnDroid/repo/-/wikis/FAQ").unwrap();
-    let sample_res = fetch_site_metadata(&client, &sample_url).await.unwrap();
-    assert_eq!(
-      SiteMetadata {
-        title: Some("FAQ · Wiki · IzzyOnDroid / repo · GitLab".to_string()),
-        description: Some(
-          "The F-Droid compatible repo at https://apt.izzysoft.de/fdroid/".to_string()
-        ),
-        image: Some(
-          Url::parse("https://gitlab.com/uploads/-/system/project/avatar/4877469/iod_logo.png")
-            .unwrap()
-        ),
-        html: None,
-      },
-      sample_res
-    );
-
-    let youtube_url = Url::parse("https://www.youtube.com/watch?v=IquO_TcMZIQ").unwrap();
-    let youtube_res = fetch_site_metadata(&client, &youtube_url).await.unwrap();
-    assert_eq!(
-      SiteMetadata {
-        title: Some("A Hard Look at Rent and Rent Seeking with Michael Hudson & Pepe Escobar".to_string()),
-        description: Some("An interactive discussion on wealth inequality and the “Great Game” on the control of natural resources.In this webinar organized jointly by the Henry George...".to_string()),
-        image: Some(Url::parse("https://i.ytimg.com/vi/IquO_TcMZIQ/maxresdefault.jpg").unwrap()),
-        html: None,
-      }, youtube_res);
-  }
-
-  // #[test]
-  // fn test_pictshare() {
-  //   let res = fetch_pictshare("https://upload.wikimedia.org/wikipedia/en/2/27/The_Mandalorian_logo.jpg");
-  //   assert!(res.is_ok());
-  //   let res_other = fetch_pictshare("https://upload.wikimedia.org/wikipedia/en/2/27/The_Mandalorian_logo.jpgaoeu");
-  //   assert!(res_other.is_err());
-  // }
-}
diff --git a/crates/websocket/Cargo.toml b/crates/websocket/Cargo.toml
index 01c985bc..79f51eb3 100644
--- a/crates/websocket/Cargo.toml
+++ b/crates/websocket/Cargo.toml
@@ -15,9 +15,9 @@ doctest = false
 [dependencies]
 lemmy_utils = { version = "=0.16.3", path = "../utils" }
 lemmy_api_common = { version = "=0.16.3", path = "../api_common" }
-lemmy_db_schema = { version = "=0.16.3", path = "../db_schema" }
-lemmy_db_views = { version = "=0.16.3", path = "../db_views" }
-lemmy_db_views_actor = { version = "=0.16.3", path = "../db_views_actor" }
+lemmy_db_schema = { version = "=0.16.3", path = "../db_schema", features = ["full"] }
+lemmy_db_views = { version = "=0.16.3", path = "../db_views", features = ["full"] }
+lemmy_db_views_actor = { version = "=0.16.3", path = "../db_views_actor", features = ["full"] }
 reqwest-middleware = "0.1.5"
 tracing = "0.1.32"
 rand = "0.8.5"
@@ -30,7 +30,7 @@ background-jobs = "0.12.0"
 tokio = "1.17.0"
 strum = "0.24.0"
 strum_macros = "0.24.0"
-chrono = { version = "0.4.19", features = ["serde"] }
+chrono = { version = "0.4.19", features = ["serde"], default-features = false }
 actix-web = { version = "4.0.1", default-features = false, features = ["rustls"] }
 actix-web-actors = { version = "4.1.0", default-features = false }
 opentelemetry = "0.17.0"
diff --git a/crates/websocket/src/handlers.rs b/crates/websocket/src/handlers.rs
index 6085e245..7fc7b4f6 100644
--- a/crates/websocket/src/handlers.rs
+++ b/crates/websocket/src/handlers.rs
@@ -4,7 +4,7 @@ use crate::{
   OperationType,
 };
 use actix::{Actor, Context, Handler, ResponseFuture};
-use lemmy_db_schema::naive_now;
+use lemmy_db_schema::utils::naive_now;
 use lemmy_utils::ConnectionId;
 use opentelemetry::trace::TraceContextExt;
 use rand::Rng;
diff --git a/crates/websocket/src/lib.rs b/crates/websocket/src/lib.rs
index 32456218..12f9783a 100644
--- a/crates/websocket/src/lib.rs
+++ b/crates/websocket/src/lib.rs
@@ -4,7 +4,7 @@ extern crate strum_macros;
 use crate::chat_server::ChatServer;
 use actix::Addr;
 use background_jobs::QueueHandle;
-use lemmy_db_schema::{source::secret::Secret, DbPool};
+use lemmy_db_schema::{source::secret::Secret, utils::DbPool};
 use lemmy_utils::{settings::structs::Settings, LemmyError};
 use reqwest_middleware::ClientWithMiddleware;
 use serde::Serialize;
diff --git a/crates/websocket/src/send.rs b/crates/websocket/src/send.rs
index 1f0677d7..d3a1ed25 100644
--- a/crates/websocket/src/send.rs
+++ b/crates/websocket/src/send.rs
@@ -4,14 +4,11 @@ use crate::{
   OperationType,
 };
 use lemmy_api_common::{
-  blocking,
-  check_person_block,
   comment::CommentResponse,
   community::CommunityResponse,
-  get_user_lang,
   person::PrivateMessageResponse,
   post::PostResponse,
-  send_email_to_user,
+  utils::{blocking, check_person_block, get_user_lang, send_email_to_user},
 };
 use lemmy_db_schema::{
   newtypes::{CommentId, CommunityId, LocalUserId, PersonId, PostId, PrivateMessageId},
@@ -23,13 +20,8 @@ use lemmy_db_schema::{
   },
   traits::{Crud, DeleteableOrRemoveable},
 };
-use lemmy_db_views::{
-  comment_view::CommentView,
-  local_user_view::LocalUserView,
-  post_view::PostView,
-  private_message_view::PrivateMessageView,
-};
-use lemmy_db_views_actor::community_view::CommunityView;
+use lemmy_db_views::structs::{CommentView, LocalUserView, PostView, PrivateMessageView};
+use lemmy_db_views_actor::structs::CommunityView;
 use lemmy_utils::{utils::MentionData, ConnectionId, LemmyError};
 
 #[tracing::instrument(skip_all)]
diff --git a/src/code_migrations.rs b/src/code_migrations.rs
index 69e41916..5a0b9eb6 100644
--- a/src/code_migrations.rs
+++ b/src/code_migrations.rs
@@ -12,7 +12,6 @@ use lemmy_apub::{
   EndpointType,
 };
 use lemmy_db_schema::{
-  naive_now,
   source::{
     comment::Comment,
     community::{Community, CommunityForm},
@@ -22,6 +21,7 @@ use lemmy_db_schema::{
     site::{Site, SiteForm},
   },
   traits::Crud,
+  utils::naive_now,
 };
 use lemmy_utils::{apub::generate_actor_keypair, LemmyError};
 use tracing::info;
diff --git a/src/main.rs b/src/main.rs
index 9297334e..f304a5fc 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -9,10 +9,13 @@ use diesel::{
 };
 use doku::json::{AutoComments, Formatting};
 use lemmy_api::match_websocket_operation;
-use lemmy_api_common::{blocking, check_private_instance_and_federation_enabled};
+use lemmy_api_common::{
+  request::build_user_agent,
+  utils::{blocking, check_private_instance_and_federation_enabled},
+};
 use lemmy_api_crud::match_websocket_operation_crud;
 use lemmy_apub_lib::activity_queue::create_activity_queue;
-use lemmy_db_schema::{get_database_url_from_env, source::secret::Secret};
+use lemmy_db_schema::{source::secret::Secret, utils::get_database_url_from_env};
 use lemmy_routes::{feeds, images, nodeinfo, webfinger};
 use lemmy_server::{
   api_routes,
@@ -23,7 +26,6 @@ use lemmy_server::{
 };
 use lemmy_utils::{
   rate_limit::{rate_limiter::RateLimiter, RateLimit},
-  request::build_user_agent,
   settings::structs::Settings,
   LemmyError,
   REQWEST_TIMEOUT,
diff --git a/src/scheduled_tasks.rs b/src/scheduled_tasks.rs
index 30e553b5..32bfd5e8 100644
--- a/src/scheduled_tasks.rs
+++ b/src/scheduled_tasks.rs
@@ -2,7 +2,7 @@
 use clokwerk::{Scheduler, TimeUnits};
 // Import week days and WeekDay
 use diesel::{sql_query, PgConnection, RunQueryDsl};
-use lemmy_db_schema::{source::activity::Activity, DbPool};
+use lemmy_db_schema::{source::activity::Activity, utils::DbPool};
 use lemmy_utils::LemmyError;
 use std::{thread, time::Duration};
 use tracing::info;
-- 
2.44.1