diff --git a/server/app/__init__.py b/server/app/__init__.py
index 5365f636732398fcc1aced7badb39057e1eb8d89..d1bbd3af80ccef9d5692f55d3a48ce2e34d920f6 100644
--- a/server/app/__init__.py
+++ b/server/app/__init__.py
@@ -1,7 +1,7 @@
 from flask import Flask, redirect, request
 
 import app.core.models as models
-from app.core import bcrypt, db, jwt
+from app.core import bcrypt, db, jwt, ma
 
 
 def create_app(config_name="configmodule.DevelopmentConfig"):
@@ -13,6 +13,7 @@ def create_app(config_name="configmodule.DevelopmentConfig"):
         bcrypt.init_app(app)
         jwt.init_app(app)
         db.init_app(app)
+        ma.init_app(app)
 
         from app.apis import flask_api
 
diff --git a/server/app/apis/__init__.py b/server/app/apis/__init__.py
index c9ff2f51143eb9b085f3b779dbc3fdca86ee5982..493a1a947d372e175785663896d4573a1f78714c 100644
--- a/server/app/apis/__init__.py
+++ b/server/app/apis/__init__.py
@@ -1,7 +1,9 @@
 from functools import wraps
 
+import app.core.http_codes as codes
 from flask_jwt_extended import verify_jwt_in_request
 from flask_jwt_extended.utils import get_jwt_claims
+from flask_restx.errors import abort
 
 
 def admin_required():
@@ -13,27 +15,29 @@ def admin_required():
             if claims["role"] == "Admin":
                 return fn(*args, **kwargs)
             else:
-                return {"message:": "Admins only"}, 403
+                return {"message:": "Admins only"}, codes.FORBIDDEN
 
         return decorator
 
     return wrapper
 
 
-def text_response(text, code=200):
-    return {"message": text}, code
+def text_response(message, code=200):
+    return {"message": message}, 200
 
 
-def query_response(db_items, code=200):
-    if type(db_items) is not list:
-        db_items = [db_items]
-    return {"result": [i.get_dict() for i in db_items]}, code
+def list_response(items, total=None, code=200):
+    if type(items) is not list:
+        abort(codes.INTERNAL_SERVER_ERROR)
+    if not total:
+        total = len(items)
+    return {"items": items, "count": len(items), "total_count": total}, code
 
 
-def object_response(items, code=200):
-    if type(items) is not list:
-        items = [items]
-    return {"result": items}, code
+def item_response(item, code=200):
+    if isinstance(item, list):
+        abort(codes.INTERNAL_SERVER_ERROR)
+    return item, code
 
 
 from flask_restx import Api
diff --git a/server/app/apis/auth.py b/server/app/apis/auth.py
index 8fe25b6b7e52b0b4f28a4aa0b88e5ac9d9c830b3..920d2d65ea482e70a51c4547419656f97f157a31 100644
--- a/server/app/apis/auth.py
+++ b/server/app/apis/auth.py
@@ -1,16 +1,22 @@
 import app.core.controller as dbc
 import app.core.http_codes as codes
-from app.apis import admin_required
+from app.apis import admin_required, item_response, text_response
 from app.core.dto import AuthDTO
 from app.core.models import User
 from app.core.parsers import create_user_parser, login_parser
-from flask_jwt_extended import (create_access_token, create_refresh_token,
-                                get_jwt_identity, get_raw_jwt,
-                                jwt_refresh_token_required, jwt_required)
+from flask_jwt_extended import (
+    create_access_token,
+    create_refresh_token,
+    get_jwt_identity,
+    get_raw_jwt,
+    jwt_refresh_token_required,
+    jwt_required,
+)
 from flask_restx import Namespace, Resource, cors
 
 api = AuthDTO.api
-user_model = AuthDTO.model
+schema = AuthDTO.schema
+list_schema = AuthDTO.list_schema
 
 
 def get_user_claims(item_user):
@@ -20,7 +26,6 @@ def get_user_claims(item_user):
 @api.route("/signup")
 class AuthSignup(Resource):
     @jwt_required
-    @cors.crossdomain(origin="*")
     def post(self):
         args = create_user_parser.parse_args(strict=True)
         email = args.get("email")
@@ -35,26 +40,24 @@ class AuthSignup(Resource):
         if not item_user:
             api.abort(codes.BAD_REQUEST, "User could not be created")
 
-        return {"id": item_user.id}
+        return item_response(schema.dump(item_user))
 
 
 @api.route("/delete/<ID>")
 @api.param("ID")
 class AuthDelete(Resource):
     @jwt_required
-    @cors.crossdomain(origin="*")
     def delete(self, ID):
         item_user = User.query.filter(User.id == ID).first()
         dbc.delete.default(item_user)
         if ID == get_jwt_identity():
             jti = get_raw_jwt()["jti"]
             dbc.add.blacklist(jti)
-        return "deleted"
+        return text_response(f"User {ID} deleted")
 
 
 @api.route("/login")
 class AuthLogin(Resource):
-    @cors.crossdomain(origin="*")
     def post(self):
         args = login_parser.parse_args(strict=True)
         email = args.get("email")
@@ -74,18 +77,16 @@ class AuthLogin(Resource):
 @api.route("/logout")
 class AuthLogout(Resource):
     @jwt_required
-    @cors.crossdomain(origin="*")
     def post(self):
         jti = get_raw_jwt()["jti"]
         dbc.add.blacklist(jti)
-        return "logout"
+        return text_response("User logout")
 
 
 @api.route("/refresh")
 class AuthRefresh(Resource):
     @jwt_required
     @jwt_refresh_token_required
-    @cors.crossdomain(origin="*")
     def post(self):
         old_jti = get_raw_jwt()["jti"]
 
diff --git a/server/app/apis/competitions.py b/server/app/apis/competitions.py
index ac7384dfdea97a738af22aa91f89aa0eea56904d..b2c0799928e577805f3d5797982d850a8ace3be9 100644
--- a/server/app/apis/competitions.py
+++ b/server/app/apis/competitions.py
@@ -1,15 +1,14 @@
 import app.core.controller as dbc
-import app.core.http_codes as codes
-from app.apis import admin_required
+from app.apis import admin_required, item_response, list_response
 from app.core.dto import CompetitionDTO
-from app.core.models import Competition, Slide, Team
+from app.core.models import Competition
 from app.core.parsers import competition_parser, competition_search_parser
-from flask_jwt_extended import get_jwt_identity, jwt_required
-from flask_restx import Resource, reqparse
+from flask_jwt_extended import jwt_required
+from flask_restx import Resource
 
 api = CompetitionDTO.api
-competition_model = CompetitionDTO.model
-competition_list_model = CompetitionDTO.user_list_model
+schema = CompetitionDTO.schema
+list_schema = CompetitionDTO.list_schema
 
 
 def get_comp(CID):
@@ -19,7 +18,6 @@ def get_comp(CID):
 @api.route("/")
 class CompetitionsList(Resource):
     @jwt_required
-    @api.marshal_with(competition_model)
     def post(self):
         args = competition_parser.parse_args(strict=True)
 
@@ -29,25 +27,24 @@ class CompetitionsList(Resource):
         style_id = args.get("style_id")
 
         # Add competition
-        item_competition = dbc.add.default(Competition(name, year, style_id, city_id))
+        item = dbc.add.default(Competition(name, year, style_id, city_id))
 
         # Add default slide
-        item_slide = dbc.add.slide(item_competition.id)
+        item_slide = dbc.add.slide(item)
 
-        return item_competition
+        dbc.refresh(item)
+        return item_response(schema.dump(item))
 
 
 @api.route("/<ID>")
 @api.param("ID")
 class Competitions(Resource):
     @jwt_required
-    @api.marshal_with(competition_model)
     def get(self, ID):
         item = get_comp(ID)
-        return item
+        return item_response(schema.dump(item))
 
     @jwt_required
-    @api.marshal_with(competition_model)
     def put(self, ID):
         args = competition_parser.parse_args(strict=True)
 
@@ -56,7 +53,8 @@ class Competitions(Resource):
         year = args.get("year")
         city_id = args.get("city_id")
         style_id = args.get("style_id")
-        return dbc.edit.competition(item, name, year, city_id, style_id)
+        item = dbc.edit.competition(item, name, year, city_id, style_id)
+        return item_response(schema.dump(item))
 
     @jwt_required
     def delete(self, ID):
@@ -68,7 +66,6 @@ class Competitions(Resource):
 @api.route("/search")
 class CompetitionSearch(Resource):
     @jwt_required
-    @api.marshal_with(competition_list_model)
     def get(self):
         args = competition_search_parser.parse_args(strict=True)
         name = args.get("name")
@@ -77,6 +74,8 @@ class CompetitionSearch(Resource):
         style_id = args.get("style_id")
         page = args.get("page", 0)
         page_size = args.get("page_size", 15)
-        result, total = dbc.get.search_competitions(name, year, city_id, style_id, page, page_size)
+        order = args.get("order", 1)
+        order_by = args.get("order_by")
 
-        return {"competitions": result, "count": len(result), "total": total}
+        items, total = dbc.get.search_competitions(name, year, city_id, style_id, page, page_size, order, order_by)
+        return list_response(list_schema.dump(items), total)
diff --git a/server/app/apis/misc.py b/server/app/apis/misc.py
index d44c26ee9cebd836bba90ea65bdae72917124c9d..13055f211e480a2f16b42d74d9517514e04c67f7 100644
--- a/server/app/apis/misc.py
+++ b/server/app/apis/misc.py
@@ -1,4 +1,5 @@
 import app.core.controller as dbc
+from app.apis import admin_required, item_response, list_response
 from app.core.dto import MiscDTO
 from app.core.models import City, MediaType, QuestionType, Role
 from flask_jwt_extended import jwt_required
@@ -6,10 +7,10 @@ from flask_restx import Resource, reqparse
 
 api = MiscDTO.api
 
-question_type_model = MiscDTO.question_type_model
-media_type_model = MiscDTO.media_type_model
-role_model = MiscDTO.role_model
-city_model = MiscDTO.city_model
+question_type_schema = MiscDTO.question_type_schema
+media_type_schema = MiscDTO.media_type_schema
+role_schema = MiscDTO.role_schema
+city_schema = MiscDTO.city_schema
 
 
 name_parser = reqparse.RequestParser()
@@ -19,57 +20,57 @@ name_parser.add_argument("name", type=str, required=True, location="json")
 @api.route("/media_types")
 class MediaTypeList(Resource):
     @jwt_required
-    @api.marshal_with(media_type_model)
     def get(self):
-        return MediaType.query.all()
+        items = MediaType.query.all()
+        return list_response(media_type_schema.dump(items))
 
 
 @api.route("/question_types")
 class QuestionTypeList(Resource):
     @jwt_required
-    @api.marshal_with(question_type_model)
     def get(self):
-        return QuestionType.query.all()
+        items = QuestionType.query.all()
+        return list_response(question_type_schema.dump(items))
 
 
 @api.route("/roles")
 class RoleList(Resource):
     @jwt_required
-    @api.marshal_with(role_model)
     def get(self):
-        return Role.query.all()
+        items = Role.query.all()
+        return list_response(role_schema.dump(items))
 
 
 @api.route("/cities")
 class CitiesList(Resource):
     @jwt_required
-    @api.marshal_with(city_model)
     def get(self):
-        return City.query.all()
+        items = City.query.all()
+        return list_response(city_schema.dump(items))
 
     @jwt_required
-    @api.marshal_with(role_model)
     def post(self):
         args = name_parser.parse_args(strict=True)
         dbc.add.default(City(args["name"]))
-        return City.query.all()
+        items = City.query.all()
+        return list_response(city_schema.dump(items))
 
 
 @api.route("/cities/<ID>")
 @api.param("ID")
 class Cities(Resource):
     @jwt_required
-    @api.marshal_with(city_model)
     def put(self, ID):
         item = City.query.filter(City.id == ID).first()
         args = name_parser.parse_args(strict=True)
         item.name = args["name"]
-        dbc.edit.default(item)
-        return City.query.all()
+        dbc.commit_and_refresh(item)
+        items = City.query.all()
+        return list_response(city_schema.dump(items))
 
     @jwt_required
-    @api.marshal_with(city_model)
     def delete(self, ID):
         item = City.query.filter(City.id == ID).first()
         dbc.delete.default(item)
-        return City.query.all()
+        items = City.query.all()
+        return list_response(city_schema.dump(items))
diff --git a/server/app/apis/slides.py b/server/app/apis/slides.py
index 6399ebc77c999563522db32a14db55bf126bf63b..6a2b1714a101cde574f188d3876243e9a5d97f86 100644
--- a/server/app/apis/slides.py
+++ b/server/app/apis/slides.py
@@ -1,6 +1,7 @@
 import app.core.controller as dbc
 import app.core.http_codes as codes
-from app.apis import admin_required
+from app.apis import admin_required, item_response, list_response
+from app.core import schemas
 from app.core.dto import SlideDTO
 from app.core.models import Competition, Slide
 from app.core.parsers import slide_parser
@@ -8,7 +9,8 @@ from flask_jwt_extended import get_jwt_identity, jwt_required
 from flask_restx import Namespace, Resource, reqparse
 
 api = SlideDTO.api
-model = SlideDTO.model
+schema = SlideDTO.schema
+list_schema = SlideDTO.list_schema
 
 
 def get_comp(CID):
@@ -19,38 +21,36 @@ def get_comp(CID):
 @api.param("CID")
 class SlidesList(Resource):
     @jwt_required
-    @api.marshal_with(model)
     def get(self, CID):
         item_comp = get_comp(CID)
-        return item_comp.slides
+        return list_response(list_schema.dump(item_comp.slides))
 
     @jwt_required
-    @api.marshal_with(model)
     def post(self, CID):
-        dbc.add.slide(CID)
         item_comp = get_comp(CID)
-        return item_comp.slides
+        dbc.add.slide(item_comp)
+        dbc.refresh(item_comp)
+        return list_response(list_schema.dump(item_comp.slides))
 
 
 @api.route("/<SID>")
 @api.param("CID,SID")
 class Slides(Resource):
     @jwt_required
-    @api.marshal_with(model)
     def get(self, CID, SID):
         item_slide = dbc.get.slide(CID, SID)
-        return item_slide
+        return item_response(schema.dump(item_slide))
 
     @jwt_required
-    @api.marshal_with(model)
     def put(self, CID, SID):
         args = slide_parser.parse_args(strict=True)
         title = args.get("title")
         timer = args.get("timer")
 
         item_slide = dbc.get.slide(CID, SID)
+        item_slide = dbc.edit.slide(item_slide, title, timer)
 
-        return dbc.edit.slide(item_slide, title, timer)
+        return item_response(schema.dump(item_slide))
 
     @jwt_required
     def delete(self, CID, SID):
@@ -63,7 +63,6 @@ class Slides(Resource):
 @api.param("CID,SID")
 class SlidesOrder(Resource):
     @jwt_required
-    @api.marshal_with(model)
     def put(self, CID, SID):
         args = slide_parser.parse_args(strict=True)
         order = args.get("order")
@@ -86,4 +85,4 @@ class SlidesOrder(Resource):
         # switch place between them
         item_slide = dbc.edit.switch_order(item_slide, item_slide_order)
 
-        return item_slide
+        return item_response(schema.dump(item_slide))
diff --git a/server/app/apis/teams.py b/server/app/apis/teams.py
index 02c730bb64200a26fa160210b6b42a526b334070..3f183bf5ea0965c3282b9a0407c9f95476486b7d 100644
--- a/server/app/apis/teams.py
+++ b/server/app/apis/teams.py
@@ -1,13 +1,14 @@
 import app.core.controller as dbc
 import app.core.http_codes as codes
-from app.apis import admin_required
+from app.apis import admin_required, item_response, list_response
 from app.core.dto import TeamDTO
 from app.core.models import Competition, Team
 from flask_jwt_extended import get_jwt_identity, jwt_required
 from flask_restx import Namespace, Resource, reqparse
 
 api = TeamDTO.api
-model = TeamDTO.model
+schema = TeamDTO.schema
+list_schema = TeamDTO.list_schema
 
 
 def get_comp(CID):
@@ -18,13 +19,11 @@ def get_comp(CID):
 @api.param("CID")
 class TeamsList(Resource):
     @jwt_required
-    @api.marshal_with(model)
     def get(self, CID):
         item_comp = get_comp(CID)
-        return item_comp.teams
+        return list_response(list_schema.dump(item_comp.teams))
 
     @jwt_required
-    @api.marshal_with(model)
     def post(self, CID):
         parser = reqparse.RequestParser()
         parser.add_argument("name", type=str, location="json")
@@ -32,17 +31,16 @@ class TeamsList(Resource):
 
         dbc.add.default(Team(args["name"], CID))
         item_comp = get_comp(CID)
-        return item_comp.teams
+        return list_response(list_schema.dump(item_comp.teams))
 
 
 @api.route("/<TID>")
 @api.param("CID,TID")
 class Teams(Resource):
     @jwt_required
-    @api.marshal_with(model)
     def get(self, CID, TID):
-        item_team = dbc.get.team(CID, TID)
-        return item_team
+        item = dbc.get.team(CID, TID)
+        return item_response(schema.dump(item))
 
     @jwt_required
     def delete(self, CID, TID):
diff --git a/server/app/apis/users.py b/server/app/apis/users.py
index 43e304c11a5c86bee2e9482a30d22a9c163e9861..4aaf372b831a1c58c78cc6f78592ae5513c4d213 100644
--- a/server/app/apis/users.py
+++ b/server/app/apis/users.py
@@ -1,6 +1,6 @@
 import app.core.controller as dbc
 import app.core.http_codes as codes
-from app.apis import admin_required
+from app.apis import admin_required, item_response, list_response
 from app.core.dto import UserDTO
 from app.core.models import User
 from app.core.parsers import user_parser, user_search_parser
@@ -8,8 +8,8 @@ from flask_jwt_extended import get_jwt_identity, jwt_required
 from flask_restx import Namespace, Resource
 
 api = UserDTO.api
-user_model = UserDTO.model
-user_list_model = UserDTO.user_list_model
+schema = UserDTO.schema
+list_schema = UserDTO.list_schema
 
 
 def edit_user(item_user, args):
@@ -28,38 +28,37 @@ def edit_user(item_user, args):
 @api.route("/")
 class UsersList(Resource):
     @jwt_required
-    @api.marshal_list_with(user_model)
     def get(self):
-        return User.query.filter(User.id == get_jwt_identity()).first()
+        item = User.query.filter(User.id == get_jwt_identity()).first()
+        return item_response(schema.dump(item))
 
     @jwt_required
-    @api.marshal_with(user_model)
     def put(self):
         args = user_parser.parse_args(strict=True)
-        item_user = User.query.filter(User.id == get_jwt_identity()).first()
-        return edit_user(item_user, args)
+        item = User.query.filter(User.id == get_jwt_identity()).first()
+        item = edit_user(item, args)
+        return item_response(schema.dump(item))
 
 
 @api.route("/<ID>")
 @api.param("ID")
 class Users(Resource):
     @jwt_required
-    @api.marshal_with(user_model)
     def get(self, ID):
-        return User.query.filter(User.id == ID).first()
+        item = User.query.filter(User.id == ID).first()
+        return item_response(schema.dump(item))
 
     @jwt_required
-    @api.marshal_with(user_model)
     def put(self, ID):
         args = user_parser.parse_args(strict=True)
-        item_user = User.query.filter(User.id == ID).first()
-        return edit_user(item_user, args)
+        item = User.query.filter(User.id == ID).first()
+        item = edit_user(item, args)
+        return item_response(schema.dump(item))
 
 
 @api.route("/search")
 class UserSearch(Resource):
     @jwt_required
-    @api.marshal_list_with(user_list_model)
     def get(self):
         args = user_search_parser.parse_args(strict=True)
         name = args.get("name")
@@ -68,6 +67,8 @@ class UserSearch(Resource):
         city_id = args.get("city_id")
         page = args.get("page", 0)
         page_size = args.get("page_size", 15)
+        order = args.get("order", 1)
+        order_by = args.get("order_by")
 
-        result, total = dbc.get.search_user(email, name, city_id, role_id, page, page_size)
-        return {"users": result, "count": len(result), "total": total}
+        items, total = dbc.get.search_user(email, name, city_id, role_id, page, page_size, order, order_by)
+        return list_response(list_schema.dump(items), total)
diff --git a/server/app/core/__init__.py b/server/app/core/__init__.py
index 1f6cad4846d15460bd6c6e113297c4aa319dbb63..e9d132cb332637eb5f064c37427d228c6e57d87f 100644
--- a/server/app/core/__init__.py
+++ b/server/app/core/__init__.py
@@ -1,6 +1,7 @@
 import sqlalchemy as sa
 from flask_bcrypt import Bcrypt
 from flask_jwt_extended.jwt_manager import JWTManager
+from flask_marshmallow import Marshmallow
 from flask_sqlalchemy import SQLAlchemy
 from flask_sqlalchemy.model import Model
 from sqlalchemy.sql import func
@@ -15,3 +16,4 @@ class Base(Model):
 db = SQLAlchemy(model_class=Base)
 bcrypt = Bcrypt()
 jwt = JWTManager()
+ma = Marshmallow()
diff --git a/server/app/core/controller/__init__.py b/server/app/core/controller/__init__.py
index ec746b748474a23bcfeff64ecda5161b2c2425b6..57f9b429697d46cab59aa8e8028f27990edf5b5e 100644
--- a/server/app/core/controller/__init__.py
+++ b/server/app/core/controller/__init__.py
@@ -1,3 +1,16 @@
 # import add, get
 from app.core import db
 from app.core.controller import add, delete, edit, get
+
+
+def commit_and_refresh(item):
+    db.session.commit()
+    db.session.refresh(item)
+
+
+def refresh(item):
+    db.session.refresh(item)
+
+
+def commit(item):
+    db.session.commit()
diff --git a/server/app/core/controller/add.py b/server/app/core/controller/add.py
index 958cf766d06cb2b30cab7c0c8fa9fd347df501c9..14c5eecf04940b2daf1c6e563b09f617e68b3cf1 100644
--- a/server/app/core/controller/add.py
+++ b/server/app/core/controller/add.py
@@ -14,9 +14,9 @@ def blacklist(jti):
     db.session.commit()
 
 
-def slide(competition_id):
-    order = Slide.query.filter(Slide.competition_id == competition_id).count()
-    item = Slide(order, competition_id)
+def slide(item_competition):
+    order = Slide.query.filter(Slide.competition_id == item_competition.id).count()
+    item = Slide(order, item_competition.id)
     db.session.add(item)
     db.session.commit()
     db.session.refresh(item)
diff --git a/server/app/core/controller/edit.py b/server/app/core/controller/edit.py
index 77c726a7daf1d5cd8280e2a3919e17af233f96b0..9373ebc794f0427982010e27b4f8e9931816f8c9 100644
--- a/server/app/core/controller/edit.py
+++ b/server/app/core/controller/edit.py
@@ -1,12 +1,6 @@
 from app.core import db
 
 
-def default(item):
-    db.session.commit()
-    db.session.refresh(item)
-    return item
-
-
 def switch_order(item1, item2):
     old_order = item1.order
     new_order = item2.order
diff --git a/server/app/core/controller/get.py b/server/app/core/controller/get.py
index ef86ef6bd23222a9157ecdc8d6481f91d6a6e4c7..d5b5978a2d4bb56a4f4d3054fcba0a1f8c4ee1c6 100644
--- a/server/app/core/controller/get.py
+++ b/server/app/core/controller/get.py
@@ -13,7 +13,19 @@ def team(CID, TID):
     return Team.query.filter((Team.competition_id == CID) & (Team.id == TID)).first()
 
 
-def search_user(email=None, name=None, city_id=None, role_id=None, page=0, page_size=15):
+def _search(query, order_column, page=0, page_size=15, order=1):
+    if order == 1:
+        query = query.order_by(order_column)
+    else:
+        query = query.order_by(order_column.desc())
+
+    total = query.count()
+    query = query.limit(page_size).offset(page * page_size)
+    items = query.all()
+    return items, total
+
+
+def search_user(email=None, name=None, city_id=None, role_id=None, page=0, page_size=15, order=1, order_by=None):
     query = User.query
     if name:
         query = query.filter(User.name.like(f"%{name}%"))
@@ -24,14 +36,16 @@ def search_user(email=None, name=None, city_id=None, role_id=None, page=0, page_
     if role_id:
         query = query.filter(User.role_id == role_id)
 
-    total = query.count()
-    query = query.limit(page_size).offset(page * page_size)
-    result = query.all()
+    order_column = User.id  # Default order_by
+    if order_by:
+        order_column = getattr(User.__table__.c, order_by)
 
-    return result, total
+    return _search(query, order_column, page, page_size, order)
 
 
-def search_competitions(name=None, year=None, city_id=None, style_id=None, page=0, page_size=15):
+def search_competitions(
+    name=None, year=None, city_id=None, style_id=None, page=0, page_size=15, order=1, order_by=None
+):
     query = Competition.query
     if name:
         query = query.filter(Competition.name.like(f"%{name}%"))
@@ -42,8 +56,8 @@ def search_competitions(name=None, year=None, city_id=None, style_id=None, page=
     if style_id:
         query = query.filter(Competition.style_id == style_id)
 
-    total = query.count()
-    query = query.limit(page_size).offset(page * page_size)
-    result = query.all()
+    order_column = Competition.year  # Default order_by
+    if order_by:
+        order_column = getattr(Competition.columns, order_by)
 
-    return result, total
+    return _search(query, order_column, page, page_size, order)
diff --git a/server/app/core/dto.py b/server/app/core/dto.py
index 6c47877c1de2202348d3ed6c91dcae50aeeaf701..3378a17bcad43e410331fadc31812982fd95563f 100644
--- a/server/app/core/dto.py
+++ b/server/app/core/dto.py
@@ -1,69 +1,42 @@
+import app.core.rich_schemas as rich_schemas
+import app.core.schemas as schemas
+import marshmallow as ma
 from flask_restx import Namespace, fields
 
 
 class AuthDTO:
     api = Namespace("auth")
-    model = api.model("Auth", {"id": fields.Integer()})
+    schema = rich_schemas.UserSchemaRich(many=False)
+    list_schema = rich_schemas.UserSchemaRich(many=True)
 
 
 class UserDTO:
     api = Namespace("users")
-    model = api.model(
-        "User",
-        {
-            "id": fields.Integer(),
-            "name": fields.String(),
-            "email": fields.String(),
-            "role_id": fields.Integer(),
-            "city_id": fields.Integer(),
-        },
-    )
-    user_list_model = api.model(
-        "UserList",
-        {"users": fields.List(fields.Nested(model)), "count": fields.Integer(), "total": fields.Integer()},
-    )
+    schema = rich_schemas.UserSchemaRich(many=False)
+    list_schema = rich_schemas.UserSchemaRich(many=True)
 
 
 class CompetitionDTO:
     api = Namespace("competitions")
-    model = api.model(
-        "Competition",
-        {
-            "id": fields.Integer(),
-            "name": fields.String(),
-            "year": fields.Integer(),
-            "style_id": fields.Integer(),
-            "city_id": fields.Integer(),
-        },
-    )
-    user_list_model = api.model(
-        "CompetitionList",
-        {"competitions": fields.List(fields.Nested(model)), "count": fields.Integer(), "total": fields.Integer()},
-    )
+    schema = rich_schemas.CompetitionSchemaRich(many=False)
+    list_schema = rich_schemas.CompetitionSchemaRich(many=True)
 
 
 class SlideDTO:
     api = Namespace("slides")
-    model = api.model(
-        "Slide",
-        {
-            "id": fields.Integer(),
-            "order": fields.Integer(),
-            "title": fields.String(),
-            "timer": fields.Integer(),
-            "competition_id": fields.Integer(),
-        },
-    )
+    schema = schemas.SlideSchema(many=False)
+    list_schema = schemas.SlideSchema(many=True)
 
 
 class TeamDTO:
     api = Namespace("teams")
-    model = api.model("Team", {"id": fields.Integer(), "name": fields.String(), "competition_id": fields.Integer()})
+    schema = schemas.TeamSchema(many=False)
+    list_schema = schemas.TeamSchema(many=True)
 
 
 class MiscDTO:
     api = Namespace("misc")
-    role_model = api.model("Role", {"id": fields.Integer(), "name": fields.String()})
-    question_type_model = api.model("QuestionType", {"id": fields.Integer(), "name": fields.String()})
-    media_type_model = api.model("MediaType", {"id": fields.Integer(), "name": fields.String()})
-    city_model = api.model("City", {"id": fields.Integer(), "name": fields.String()})
+    role_schema = schemas.RoleSchema(many=True)
+    question_type_schema = schemas.QuestionTypeSchema(many=True)
+    media_type_schema = schemas.MediaTypeSchema(many=True)
+    city_schema = schemas.CitySchema(many=True)
diff --git a/server/app/core/parsers.py b/server/app/core/parsers.py
index 3fc63f1a14680920767c7f0a3d1bcfe7eeec2142..6f287cb271a5984c3c5ba5f0e8b49a38ff7196c5 100644
--- a/server/app/core/parsers.py
+++ b/server/app/core/parsers.py
@@ -2,8 +2,10 @@ from flask_restx import inputs, reqparse
 
 ###SEARCH####
 search_parser = reqparse.RequestParser()
-search_parser.add_argument("page", type=int, default=0)
-search_parser.add_argument("page_size", type=int, default=15)
+search_parser.add_argument("page", type=int, default=0, location="args")
+search_parser.add_argument("page_size", type=int, default=15, location="args")
+search_parser.add_argument("order", type=int, default=1, location="args")
+search_parser.add_argument("order_by", type=str, location="args")
 
 ###LOGIN####
 login_parser = reqparse.RequestParser()
diff --git a/server/app/core/rich_schemas.py b/server/app/core/rich_schemas.py
new file mode 100644
index 0000000000000000000000000000000000000000..56aec85e5f8f93eaf663d8a374738b0fee478fd8
--- /dev/null
+++ b/server/app/core/rich_schemas.py
@@ -0,0 +1,39 @@
+import app.core.models as models
+import app.core.schemas as schemas
+from app.core import ma
+from marshmallow import fields as fields2
+from marshmallow_sqlalchemy import fields
+
+
+class RichSchema(ma.SQLAlchemySchema):
+    class Meta:
+        strict = True
+        load_instance = True
+        include_relationships = True
+
+
+class UserSchemaRich(RichSchema):
+    class Meta(RichSchema.Meta):
+        model = models.User
+
+    id = ma.auto_field()
+    name = ma.auto_field()
+    email = ma.auto_field()
+    role = fields.Nested(schemas.RoleSchema, many=False)
+    city = fields.Nested(schemas.CitySchema, many=False)
+
+
+class CompetitionSchemaRich(RichSchema):
+    class Meta(RichSchema.Meta):
+        model = models.Competition
+
+    id = ma.auto_field()
+    name = ma.auto_field()
+    year = ma.auto_field()
+    slides = fields.Nested(schemas.SlideSchema, many=True)
+    style = fields.Nested(schemas.RoleSchema, many=False)
+    city = fields.Nested(schemas.CitySchema, many=False)
+
+
+class UserListSchema(ma.Schema):
+    users = fields2.Nested(UserSchemaRich, many=False)
diff --git a/server/app/core/schemas.py b/server/app/core/schemas.py
new file mode 100644
index 0000000000000000000000000000000000000000..9962479fe3bcd241bcf77a5ed8b389b4e55ec19e
--- /dev/null
+++ b/server/app/core/schemas.py
@@ -0,0 +1,82 @@
+import app.core.models as models
+from app.core import ma
+from marshmallow_sqlalchemy import fields
+
+
+class BaseSchema(ma.SQLAlchemySchema):
+    class Meta:
+        strict = True
+        load_instance = False
+        include_relationships = False
+
+
+class QuestionTypeSchema(BaseSchema):
+    class Meta(BaseSchema.Meta):
+        model = models.QuestionType
+
+    id = ma.auto_field()
+    name = ma.auto_field()
+
+
+class MediaTypeSchema(BaseSchema):
+    class Meta(BaseSchema.Meta):
+        model = models.MediaType
+
+    id = ma.auto_field()
+    name = ma.auto_field()
+
+
+class RoleSchema(BaseSchema):
+    class Meta(BaseSchema.Meta):
+        model = models.Role
+
+    id = ma.auto_field()
+    name = ma.auto_field()
+
+
+class CitySchema(BaseSchema):
+    class Meta(BaseSchema.Meta):
+        model = models.City
+
+    id = ma.auto_field()
+    name = ma.auto_field()
+
+
+class MediaSchema(BaseSchema):
+    class Meta(BaseSchema.Meta):
+        model = models.Media
+
+    id = ma.auto_field()
+    filename = ma.auto_field()
+    type_id = ma.auto_field()
+    upload_by_id = ma.auto_field()
+
+
+class StyleSchema(BaseSchema):
+    class Meta(BaseSchema.Meta):
+        model = models.Style
+
+    id = ma.auto_field()
+    name = ma.auto_field()
+    css = ma.auto_field()
+    bg_image = fields.Nested(MediaSchema, many=False)
+
+
+class SlideSchema(BaseSchema):
+    class Meta(BaseSchema.Meta):
+        model = models.Slide
+
+    id = ma.auto_field()
+    order = ma.auto_field()
+    title = ma.auto_field()
+    timer = ma.auto_field()
+    competition_id = ma.auto_field()
+
+
+class TeamSchema(BaseSchema):
+    class Meta(BaseSchema.Meta):
+        model = models.Team
+
+    id = ma.auto_field()
+    name = ma.auto_field()
+    competition_id = ma.auto_field()
diff --git a/server/requirements.txt b/server/requirements.txt
index 463ef4fe07f14b03b7771a25842170c8903f73f7..172f152510c3ccd6245f19e5cb53ac702a6b4370 100644
Binary files a/server/requirements.txt and b/server/requirements.txt differ
diff --git a/server/tests/test_app.py b/server/tests/test_app.py
index ce897883c3dcf15d793ccc9d7a6d6bda0ac03498..383cb574b5b3ad56def838bb169cdf761bd6bf59 100644
--- a/server/tests/test_app.py
+++ b/server/tests/test_app.py
@@ -2,6 +2,38 @@ from tests import app, client, db
 from tests.test_helpers import add_default_values, delete, get, post, put
 
 
+def test_misc(client):
+    add_default_values()
+
+    # Login in with default user
+    response, body = post(client, "/api/auth/login", {"email": "test@test.se", "password": "password"})
+    assert response.status_code == 200
+    headers = {"Authorization": "Bearer " + body["access_token"]}
+
+    ## Get misc
+    response, body = get(client, "/api/misc/roles", headers=headers)
+    assert body["count"] >= 2
+
+    response, body = get(client, "/api/misc/cities", headers=headers)
+    assert body["count"] >= 1
+    assert body["items"][0]["name"] == "Linköping"
+
+    response, body = get(client, "/api/misc/media_types", headers=headers)
+    assert body["count"] >= 2
+
+    response, body = get(client, "/api/misc/question_types", headers=headers)
+    assert body["count"] >= 3
+
+    ## Cities
+    response, body = post(client, "/api/misc/cities", {"name": "Göteborg"}, headers=headers)
+    assert body["count"] >= 2
+    assert body["items"][1]["name"] == "Göteborg"
+
+    response, body = put(client, "/api/misc/cities/2", {"name": "Gbg"}, headers=headers)
+    assert body["count"] >= 2
+    assert body["items"][1]["name"] == "Gbg"
+
+
 def test_competition(client):
     add_default_values()
 
@@ -26,7 +58,7 @@ def test_competition(client):
 
     response, body = get(client, "/api/competitions/1/slides", headers=headers)
     assert response.status_code == 200
-    assert len(body) == 2
+    assert len(body["items"]) == 2
 
     response, body = put(client, "/api/competitions/1/slides/1/order", {"order": 1}, headers=headers)
     assert response.status_code == 200
@@ -36,8 +68,8 @@ def test_competition(client):
 
     response, body = get(client, "/api/competitions/1/teams", headers=headers)
     assert response.status_code == 200
-    assert len(body) == 1
-    assert body[0]["name"] == "t1"
+    assert len(body["items"]) == 1
+    assert body["items"][0]["name"] == "t1"
 
     response, body = delete(client, "/api/competitions/1", {}, headers=headers)
     assert response.status_code == 200
@@ -57,6 +89,7 @@ def test_app(client):
     assert response.status_code == 200
     assert body["id"] == 2
     assert "password" not in body
+    assert "_password" not in body
 
     # Try loggin with wrong PASSWORD
     response, body = post(client, "/api/auth/login", {"email": "test1@test.se", "password": "abc1234"})