diff --git a/doc/api/projects.md b/doc/api/projects.md
index de57f91bb8e9b2c74a30d1031cd6fa751c4e1155..132be644b5981c4dfc24f6b9ad1f89b4ec4d06b7 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -626,6 +626,7 @@ Parameters:
 | `path` | string | no | Custom repository name for new project. By default generated based on name |
 | `default_branch` | string | no | `master` by default |
 | `namespace_id` | integer | no | Namespace for the new project (defaults to the current user's namespace) |
+| `default_branch` | string | no | `master` by default |
 | `description` | string | no | Short project description |
 | `issues_enabled` | boolean | no | Enable issues for this project |
 | `merge_requests_enabled` | boolean | no | Enable merge requests for this project |
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 0d3ddb89dc3c985af0d35cb709a8da19a86759e4..2fd50143e91aaad5a2c6e362701a0138d357f922 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -324,11 +324,6 @@ module API
     # Projects helpers
 
     def filter_projects(projects)
-      # If the archived parameter is passed, limit results accordingly
-      if params[:archived].present?
-        projects = projects.where(archived: to_boolean(params[:archived]))
-      end
-
       if params[:search].present?
         projects = projects.search(params[:search])
       end
@@ -337,25 +332,8 @@ module API
         projects = projects.search_by_visibility(params[:visibility])
       end
 
-      projects.reorder(project_order_by => project_sort)
-    end
-
-    def project_order_by
-      order_fields = %w(id name path created_at updated_at last_activity_at)
-
-      if order_fields.include?(params['order_by'])
-        params['order_by']
-      else
-        'created_at'
-      end
-    end
-
-    def project_sort
-      if params["sort"] == 'asc'
-        :asc
-      else
-        :desc
-      end
+      projects = projects.where(archived: params[:archived])
+      projects.reorder(params[:order_by] => params[:sort])
     end
 
     # file helpers
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 2ea3c433ae25c8494ea68758929711b170731b52..8975b1a751ce50201ec3e50d33ac5c0db484e255 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -1,293 +1,287 @@
 module API
   # Projects API
   class Projects < Grape::API
+    include PaginationParams
+
     before { authenticate! }
 
-    resource :projects, requirements: { id: /[^\/]+/ } do
+    helpers do
+      params :optional_params do
+        optional :description, type: String, desc: 'The description of the project'
+        optional :issues_enabled, type: Boolean, desc: 'Flag indication if the issue tracker is enabled'
+        optional :merge_requests_enabled, type: Boolean, desc: 'Flag indication if merge requests are enabled'
+        optional :wiki_enabled, type: Boolean, desc: 'Flag indication if the wiki is enabled'
+        optional :builds_enabled, type: Boolean, desc: 'Flag indication if builds are enabled'
+        optional :snippets_enabled, type: Boolean, desc: 'Flag indication if snippets are enabled'
+        optional :shared_runners_enabled, type: Boolean, desc: 'Flag indication if shared runners are enabled for that project'
+        optional :container_registry_enabled, type: Boolean, desc: 'Flag indication if the container registry is enabled for that project'
+        optional :lfs_enabled, type: Boolean, desc: 'Flag indication if Git LFS is enabled for that project'
+        optional :public, type: Boolean, desc: 'Create a public project. The same as visibility_level = 20.'
+        optional :visibility_level, type: Integer, values: [
+          Gitlab::VisibilityLevel::PRIVATE,
+          Gitlab::VisibilityLevel::INTERNAL,
+          Gitlab::VisibilityLevel::PUBLIC ], desc: 'Create a public project. The same as visibility_level = 20.'
+        optional :public_builds, type: Boolean, desc: 'Perform public builds'
+        optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access'
+        optional :only_allow_merge_if_build_succeeds, type: Boolean, desc: 'Only allow to merge if builds succeed'
+        optional :only_allow_merge_if_all_discussions_are_resolved, type: Boolean, desc: 'Only allow to merge if all discussions are resolved'
+      end
+
+      def map_public_to_visibility_level(attrs)
+        publik = attrs.delete(:public)
+        if !publik.nil? && !attrs[:visibility_level].present?
+          # Since setting the public attribute to private could mean either
+          # private or internal, use the more conservative option, private.
+          attrs[:visibility_level] = (publik == true) ? Gitlab::VisibilityLevel::PUBLIC : Gitlab::VisibilityLevel::PRIVATE
+        end
+        attrs
+      end
+    end
+
+    resource :projects do
       helpers do
-        def map_public_to_visibility_level(attrs)
-          publik = attrs.delete(:public)
-          if publik.present? && !attrs[:visibility_level].present?
-            publik = to_boolean(publik)
-            # Since setting the public attribute to private could mean either
-            # private or internal, use the more conservative option, private.
-            attrs[:visibility_level] = (publik == true) ? Gitlab::VisibilityLevel::PUBLIC : Gitlab::VisibilityLevel::PRIVATE
-          end
-          attrs
+        params :sort_params do
+          optional :order_by, type: String, values: %w[id name path created_at updated_at last_activity_at],
+                              default: 'created_at', desc: 'Return projects ordered by field'
+          optional :sort, type: String, values: %w[asc desc], default: 'desc',
+                          desc: 'Return projects sorted in ascending and descending order'
+        end
+
+        params :filter_params do
+          optional :archived, type: Boolean, default: false, desc: 'Limit by archived status'
+          optional :visibility, type: String, values: %w[public internal private],
+                                desc: 'Limit by visibility'
+          optional :search, type: String, desc: 'Return list of authorized projects matching the search criteria'
+          use :sort_params
+        end
+
+        params :create_params do
+          optional :namespace_id, type: Integer, desc: 'Namespace ID for the new project. Default to the user namespace.'
+          optional :import_url, type: String, desc: 'URL from which the project is imported'
         end
       end
 
-      # Get a projects list for authenticated user
-      #
-      # Example Request:
-      #   GET /projects
+      desc 'Get a projects list for authenticated user' do
+        success Entities::BasicProjectDetails
+      end
+      params do
+        optional :simple, type: Boolean, default: false,
+                          desc: 'Return only the ID, URL, name, and path of each project'
+        use :filter_params
+        use :pagination
+      end
       get do
         projects = current_user.authorized_projects
         projects = filter_projects(projects)
-        projects = paginate projects
         entity = params[:simple] ? Entities::BasicProjectDetails : Entities::ProjectWithAccess
 
-        present projects, with: entity, user: current_user
+        present paginate(projects), with: entity, user: current_user
       end
 
-      # Get a list of visible projects for authenticated user
-      #
-      # Example Request:
-      #   GET /projects/visible
+      desc 'Get a list of visible projects for authenticated user' do
+        success Entities::BasicProjectDetails
+      end
+      params do
+        optional :simple, type: Boolean, default: false,
+                          desc: 'Return only the ID, URL, name, and path of each project'
+        use :filter_params
+        use :pagination
+      end
       get '/visible' do
         projects = ProjectsFinder.new.execute(current_user)
         projects = filter_projects(projects)
-        projects = paginate projects
         entity = params[:simple] ? Entities::BasicProjectDetails : Entities::ProjectWithAccess
 
-        present projects, with: entity, user: current_user
+        present paginate(projects), with: entity, user: current_user
       end
 
-      # Get an owned projects list for authenticated user
-      #
-      # Example Request:
-      #   GET /projects/owned
+      desc 'Get an owned projects list for authenticated user' do
+        success Entities::BasicProjectDetails
+      end
+      params do
+        use :filter_params
+        use :pagination
+      end
       get '/owned' do
         projects = current_user.owned_projects
         projects = filter_projects(projects)
-        projects = paginate projects
-        present projects, with: Entities::ProjectWithAccess, user: current_user
+
+        present paginate(projects), with: Entities::ProjectWithAccess, user: current_user
       end
 
-      # Gets starred project for the authenticated user
-      #
-      # Example Request:
-      #   GET /projects/starred
+      desc 'Gets starred project for the authenticated user' do
+        success Entities::BasicProjectDetails
+      end
+      params do
+        use :filter_params
+        use :pagination
+      end
       get '/starred' do
         projects = current_user.viewable_starred_projects
         projects = filter_projects(projects)
-        projects = paginate projects
-        present projects, with: Entities::Project, user: current_user
+
+        present paginate(projects), with: Entities::Project, user: current_user
       end
 
-      # Get all projects for admin user
-      #
-      # Example Request:
-      #   GET /projects/all
+      desc 'Get all projects for admin user' do
+        success Entities::BasicProjectDetails
+      end
+      params do
+        use :filter_params
+        use :pagination
+      end
       get '/all' do
         authenticated_as_admin!
         projects = Project.all
         projects = filter_projects(projects)
-        projects = paginate projects
-        present projects, with: Entities::ProjectWithAccess, user: current_user
+
+        present paginate(projects), with: Entities::ProjectWithAccess, user: current_user
       end
 
-      # Get a single project
-      #
-      # Parameters:
-      #   id (required) - The ID of a project
-      # Example Request:
-      #   GET /projects/:id
-      get ":id" do
-        present user_project, with: Entities::ProjectWithAccess, user: current_user,
-                              user_can_admin_project: can?(current_user, :admin_project, user_project)
+      desc 'Search for projects the current user has access to' do
+        success Entities::Project
+      end
+      params do
+        requires :query, type: String, desc: 'The project name to be searched'
+        use :sort_params
+        use :pagination
       end
+      get "/search/:query" do
+        search_service = Search::GlobalService.new(current_user, search: params[:query]).execute
+        projects = search_service.objects('projects', params[:page])
+        projects = projects.reorder(params[:order_by] => params[:sort])
 
-      # Get events for a single project
-      #
-      # Parameters:
-      #   id (required) - The ID of a project
-      # Example Request:
-      #   GET /projects/:id/events
-      get ":id/events" do
-        events = paginate user_project.events.recent
-        present events, with: Entities::Event
-      end
-
-      # Create new project
-      #
-      # Parameters:
-      #   name (required)                   - name for new project
-      #   description (optional)            - short project description
-      #   issues_enabled (optional)
-      #   merge_requests_enabled (optional)
-      #   builds_enabled (optional)
-      #   wiki_enabled (optional)
-      #   snippets_enabled (optional)
-      #   container_registry_enabled (optional)
-      #   shared_runners_enabled (optional)
-      #   namespace_id (optional)           - defaults to user namespace
-      #   public (optional)                 - if true same as setting visibility_level = 20
-      #   visibility_level (optional)       - 0 by default
-      #   import_url (optional)
-      #   public_builds (optional)
-      #   lfs_enabled (optional)
-      #   request_access_enabled (optional) - Allow users to request member access
-      # Example Request
-      #   POST /projects
+        present paginate(projects), with: Entities::Project
+      end
+
+      desc 'Create new project' do
+        success Entities::Project
+      end
+      params do
+        requires :name, type: String, desc: 'The name of the project'
+        optional :path, type: String, desc: 'The path of the repository'
+        use :optional_params
+        use :create_params
+      end
       post do
-        required_attributes! [:name]
-        attrs = attributes_for_keys [:builds_enabled,
-                                     :container_registry_enabled,
-                                     :description,
-                                     :import_url,
-                                     :issues_enabled,
-                                     :lfs_enabled,
-                                     :merge_requests_enabled,
-                                     :name,
-                                     :namespace_id,
-                                     :only_allow_merge_if_build_succeeds,
-                                     :path,
-                                     :public,
-                                     :public_builds,
-                                     :request_access_enabled,
-                                     :shared_runners_enabled,
-                                     :snippets_enabled,
-                                     :visibility_level,
-                                     :wiki_enabled,
-                                     :only_allow_merge_if_all_discussions_are_resolved]
-        attrs = map_public_to_visibility_level(attrs)
-        @project = ::Projects::CreateService.new(current_user, attrs).execute
-        if @project.saved?
-          present @project, with: Entities::Project,
-                            user_can_admin_project: can?(current_user, :admin_project, @project)
+        attrs = map_public_to_visibility_level(declared_params(include_missing: false))
+        project = ::Projects::CreateService.new(current_user, attrs).execute
+
+        if project.saved?
+          present project, with: Entities::Project,
+                           user_can_admin_project: can?(current_user, :admin_project, project)
         else
-          if @project.errors[:limit_reached].present?
-            error!(@project.errors[:limit_reached], 403)
+          if project.errors[:limit_reached].present?
+            error!(project.errors[:limit_reached], 403)
           end
-          render_validation_error!(@project)
+          render_validation_error!(project)
         end
       end
 
-      # Create new project for a specified user.  Only available to admin users.
-      #
-      # Parameters:
-      #   user_id (required)                - The ID of a user
-      #   name (required)                   - name for new project
-      #   description (optional)            - short project description
-      #   default_branch (optional)         - 'master' by default
-      #   issues_enabled (optional)
-      #   merge_requests_enabled (optional)
-      #   builds_enabled (optional)
-      #   wiki_enabled (optional)
-      #   snippets_enabled (optional)
-      #   container_registry_enabled (optional)
-      #   shared_runners_enabled (optional)
-      #   public (optional)                 - if true same as setting visibility_level = 20
-      #   visibility_level (optional)
-      #   import_url (optional)
-      #   public_builds (optional)
-      #   lfs_enabled (optional)
-      #   request_access_enabled (optional) - Allow users to request member access
-      # Example Request
-      #   POST /projects/user/:user_id
+      desc 'Create new project for a specified user. Only available to admin users.' do
+        success Entities::Project
+      end
+      params do
+        requires :name, type: String, desc: 'The name of the project'
+        requires :user_id, type: Integer, desc: 'The ID of a user'
+        optional :default_branch, type: String, desc: 'The default branch of the project'
+        use :optional_params
+        use :create_params
+      end
       post "user/:user_id" do
         authenticated_as_admin!
-        user = User.find(params[:user_id])
-        attrs = attributes_for_keys [:builds_enabled,
-                                     :default_branch,
-                                     :description,
-                                     :import_url,
-                                     :issues_enabled,
-                                     :lfs_enabled,
-                                     :merge_requests_enabled,
-                                     :name,
-                                     :only_allow_merge_if_build_succeeds,
-                                     :public,
-                                     :public_builds,
-                                     :request_access_enabled,
-                                     :shared_runners_enabled,
-                                     :snippets_enabled,
-                                     :visibility_level,
-                                     :wiki_enabled,
-                                     :only_allow_merge_if_all_discussions_are_resolved]
-        attrs = map_public_to_visibility_level(attrs)
-        @project = ::Projects::CreateService.new(user, attrs).execute
-        if @project.saved?
-          present @project, with: Entities::Project,
-                            user_can_admin_project: can?(current_user, :admin_project, @project)
+        user = User.find_by(id: params.delete(:user_id))
+        not_found!('User') unless user
+
+        attrs = map_public_to_visibility_level(declared_params(include_missing: false))
+        project = ::Projects::CreateService.new(user, attrs).execute
+
+        if project.saved?
+          present project, with: Entities::Project,
+                           user_can_admin_project: can?(current_user, :admin_project, project)
         else
-          render_validation_error!(@project)
+          render_validation_error!(project)
         end
       end
+    end
+
+    params do
+      requires :id, type: String, desc: 'The ID of a project'
+    end
+    resource :projects, requirements: { id: /[^\/]+/ } do
+      desc 'Get a single project' do
+        success Entities::ProjectWithAccess
+      end
+      get ":id" do
+        present user_project, with: Entities::ProjectWithAccess, user: current_user,
+                              user_can_admin_project: can?(current_user, :admin_project, user_project)
+      end
+
+      desc 'Get events for a single project' do
+        success Entities::Event
+      end
+      params do
+        use :pagination
+      end
+      get ":id/events" do
+        present paginate(user_project.events.recent), with: Entities::Event
+      end
 
-      # Fork new project for the current user or provided namespace.
-      #
-      # Parameters:
-      #   id (required) - The ID of a project
-      #   namespace (optional) - The ID or name of the namespace that the project will be forked into.
-      # Example Request
-      #   POST /projects/fork/:id
+      desc 'Fork new project for the current user or provided namespace.' do
+        success Entities::Project
+      end
+      params do
+        optional :namespace, type: String, desc: 'The ID or name of the namespace that the project will be forked into'
+      end
       post 'fork/:id' do
-        attrs = {}
-        namespace_id = params[:namespace]
+        fork_params = declared_params(include_missing: false)
+        namespace_id = fork_params[:namespace]
 
         if namespace_id.present?
-          namespace = Namespace.find_by(id: namespace_id) || Namespace.find_by_path_or_name(namespace_id)
+          fork_params[:namespace] = if namespace_id =~ /^\d+$/
+                                      Namespace.find_by(id: namespace_id)
+                                    else
+                                      Namespace.find_by_path_or_name(namespace_id)
+                                    end
 
-          unless namespace && can?(current_user, :create_projects, namespace)
+          unless fork_params[:namespace] && can?(current_user, :create_projects, fork_params[:namespace])
             not_found!('Target Namespace')
           end
-
-          attrs[:namespace] = namespace
         end
 
-        @forked_project =
-          ::Projects::ForkService.new(user_project,
-                                      current_user,
-                                      attrs).execute
+        forked_project = ::Projects::ForkService.new(user_project, current_user, fork_params).execute
 
-        if @forked_project.errors.any?
-          conflict!(@forked_project.errors.messages)
+        if forked_project.errors.any?
+          conflict!(forked_project.errors.messages)
         else
-          present @forked_project, with: Entities::Project,
-                                   user_can_admin_project: can?(current_user, :admin_project, @forked_project)
+          present forked_project, with: Entities::Project,
+                                  user_can_admin_project: can?(current_user, :admin_project, forked_project)
         end
       end
 
-      # Update an existing project
-      #
-      # Parameters:
-      #   id (required) - the id of a project
-      #   name (optional) - name of a project
-      #   path (optional) - path of a project
-      #   description (optional) - short project description
-      #   issues_enabled (optional)
-      #   merge_requests_enabled (optional)
-      #   builds_enabled (optional)
-      #   wiki_enabled (optional)
-      #   snippets_enabled (optional)
-      #   container_registry_enabled (optional)
-      #   shared_runners_enabled (optional)
-      #   public (optional) - if true same as setting visibility_level = 20
-      #   visibility_level (optional) - visibility level of a project
-      #   public_builds (optional)
-      #   lfs_enabled (optional)
-      # Example Request
-      #   PUT /projects/:id
+      desc 'Update an existing project' do
+        success Entities::Project
+      end
+      params do
+        optional :name, type: String, desc: 'The name of the project'
+        optional :default_branch, type: String, desc: 'The default branch of the project'
+        optional :path, type: String, desc: 'The path of the repository'
+        use :optional_params
+        at_least_one_of :name, :description, :issues_enabled, :merge_requests_enabled,
+                        :wiki_enabled, :builds_enabled, :snippets_enabled,
+                        :shared_runners_enabled, :container_registry_enabled,
+                        :lfs_enabled, :public, :visibility_level, :public_builds,
+                        :request_access_enabled, :only_allow_merge_if_build_succeeds,
+                        :only_allow_merge_if_all_discussions_are_resolved, :path,
+                        :default_branch
+      end
       put ':id' do
-        attrs = attributes_for_keys [:builds_enabled,
-                                     :container_registry_enabled,
-                                     :default_branch,
-                                     :description,
-                                     :issues_enabled,
-                                     :lfs_enabled,
-                                     :merge_requests_enabled,
-                                     :name,
-                                     :only_allow_merge_if_build_succeeds,
-                                     :path,
-                                     :public,
-                                     :public_builds,
-                                     :request_access_enabled,
-                                     :shared_runners_enabled,
-                                     :snippets_enabled,
-                                     :visibility_level,
-                                     :wiki_enabled,
-                                     :only_allow_merge_if_all_discussions_are_resolved]
-        attrs = map_public_to_visibility_level(attrs)
         authorize_admin_project
+        attrs = map_public_to_visibility_level(declared_params(include_missing: false))
         authorize! :rename_project, user_project if attrs[:name].present?
-        if attrs[:visibility_level].present?
-          authorize! :change_visibility_level, user_project
-        end
+        authorize! :change_visibility_level, user_project if attrs[:visibility_level].present?
 
-        ::Projects::UpdateService.new(user_project,
-                                      current_user, attrs).execute
+        ::Projects::UpdateService.new(user_project, current_user, attrs).execute
 
         if user_project.errors.any?
           render_validation_error!(user_project)
@@ -297,12 +291,9 @@ module API
         end
       end
 
-      # Archive project
-      #
-      # Parameters:
-      #   id (required) - The ID of a project
-      # Example Request:
-      #   PUT /projects/:id/archive
+      desc 'Archive a project' do
+        success Entities::Project
+      end
       post ':id/archive' do
         authorize!(:archive_project, user_project)
 
@@ -311,12 +302,9 @@ module API
         present user_project, with: Entities::Project
       end
 
-      # Unarchive project
-      #
-      # Parameters:
-      #   id (required) - The ID of a project
-      # Example Request:
-      #   PUT /projects/:id/unarchive
+      desc 'Unarchive a project' do
+        success Entities::Project
+      end
       post ':id/unarchive' do
         authorize!(:archive_project, user_project)
 
@@ -325,12 +313,9 @@ module API
         present user_project, with: Entities::Project
       end
 
-      # Star project
-      #
-      # Parameters:
-      #   id (required) - The ID of a project
-      # Example Request:
-      #   POST /projects/:id/star
+      desc 'Star a project' do
+        success Entities::Project
+      end
       post ':id/star' do
         if current_user.starred?(user_project)
           not_modified!
@@ -342,12 +327,9 @@ module API
         end
       end
 
-      # Unstar project
-      #
-      # Parameters:
-      #   id (required) - The ID of a project
-      # Example Request:
-      #   DELETE /projects/:id/star
+      desc 'Unstar a project' do
+        success Entities::Project
+      end
       delete ':id/star' do
         if current_user.starred?(user_project)
           current_user.toggle_star(user_project)
@@ -359,67 +341,51 @@ module API
         end
       end
 
-      # Remove project
-      #
-      # Parameters:
-      #   id (required) - The ID of a project
-      # Example Request:
-      #   DELETE /projects/:id
+      desc 'Remove a project'
       delete ":id" do
         authorize! :remove_project, user_project
         ::Projects::DestroyService.new(user_project, current_user, {}).async_execute
       end
 
-      # Mark this project as forked from another
-      #
-      # Parameters:
-      #   id: (required) - The ID of the project being marked as a fork
-      #   forked_from_id: (required) - The ID of the project it was forked from
-      # Example Request:
-      #   POST /projects/:id/fork/:forked_from_id
+      desc 'Mark this project as forked from another'
+      params do
+        requires :forked_from_id, type: String, desc: 'The ID of the project it was forked from'
+      end
       post ":id/fork/:forked_from_id" do
         authenticated_as_admin!
+
         forked_from_project = find_project!(params[:forked_from_id])
-        unless forked_from_project.nil?
-          if user_project.forked_from_project.nil?
-            user_project.create_forked_project_link(forked_to_project_id: user_project.id, forked_from_project_id: forked_from_project.id)
-          else
-            render_api_error!("Project already forked", 409)
-          end
+        not_found!("Source Project") unless forked_from_project
+
+        if user_project.forked_from_project.nil?
+          user_project.create_forked_project_link(forked_to_project_id: user_project.id, forked_from_project_id: forked_from_project.id)
         else
-          not_found!("Source Project")
+          render_api_error!("Project already forked", 409)
         end
       end
 
-      # Remove a forked_from relationship
-      #
-      # Parameters:
-      #   id: (required) - The ID of the project being marked as a fork
-      # Example Request:
-      #  DELETE /projects/:id/fork
+      desc 'Remove a forked_from relationship'
       delete ":id/fork" do
         authorize! :remove_fork_project, user_project
+
         if user_project.forked?
           user_project.forked_project_link.destroy
+        else
+          not_modified!
         end
       end
 
-      # Share project with group
-      #
-      # Parameters:
-      #   id (required)           - The ID of a project
-      #   group_id (required)     - The ID of a group
-      #   group_access (required) - Level of permissions for sharing
-      #   expires_at (optional)   - Share expiration date
-      #
-      # Example Request:
-      #   POST /projects/:id/share
+      desc 'Share the project with a group' do
+        success Entities::ProjectGroupLink
+      end
+      params do
+        requires :group_id, type: Integer, desc: 'The ID of a group'
+        requires :group_access, type: Integer, values: Gitlab::Access.values, desc: 'The group access level'
+        optional :expires_at, type: Date, desc: 'Share expiration date'
+      end
       post ":id/share" do
         authorize! :admin_project, user_project
-        required_attributes! [:group_id, :group_access]
-        attrs = attributes_for_keys [:group_id, :group_access, :expires_at]
-
-        group = Group.find_by_id(attrs[:group_id])
+        group = Group.find_by_id(params[:group_id])
 
         unless group && can?(current_user, :read_group, group)
           not_found!('Group')
@@ -429,7 +395,7 @@ module API
           return render_api_error!("The project sharing with group is disabled", 400)
         end
 
-        link = user_project.project_group_links.new(attrs)
+        link = user_project.project_group_links.new(declared_params(include_missing: false))
 
         if link.save
           present link, with: Entities::ProjectGroupLink
@@ -451,40 +417,26 @@ module API
         no_content!
       end
 
-      # Upload a file
-      #
-      # Parameters:
-      #   id: (required) - The ID of the project
-      #   file: (required) - The file to be uploaded
+      desc 'Upload a file'
+      params do
+        requires :file, type: File, desc: 'The file to be uploaded'
+      end
       post ":id/uploads" do
         ::Projects::UploadService.new(user_project, params[:file]).execute
       end
 
-      # search for projects current_user has access to
-      #
-      # Parameters:
-      #   query (required) - A string contained in the project name
-      #   per_page (optional) - number of projects to return per page
-      #   page (optional) - the page to retrieve
-      # Example Request:
-      #   GET /projects/search/:query
-      get "/search/:query" do
-        search_service = Search::GlobalService.new(current_user, search: params[:query]).execute
-        projects = search_service.objects('projects', params[:page])
-        projects = projects.reorder(project_order_by => project_sort)
-
-        present paginate(projects), with: Entities::Project
+      desc 'Get the users list of a project' do
+        success Entities::UserBasic
+      end
+      params do
+        optional :search, type: String, desc: 'Return list of users matching the search criteria'
+        use :pagination
       end
-
-      # Get a users list
-      #
-      # Example Request:
-      #  GET /users
       get ':id/users' do
-        @users = User.where(id: user_project.team.users.map(&:id))
-        @users = @users.search(params[:search]) if params[:search].present?
-        @users = paginate @users
-        present @users, with: Entities::UserBasic
+        users = User.where(id: user_project.team.users.map(&:id))
+        users = users.search(params[:search]) if params[:search].present?
+
+        present paginate(users), with: Entities::UserBasic
       end
     end
   end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index e53ee2a4e761db33631b82e9950434f2eca20d99..482e81b29a66f7718d8b52ebd53cf8964be0cb51 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -415,16 +415,7 @@ describe API::API, api: true  do
         not_to change { Project.count }
 
       expect(response).to have_http_status(400)
-      expect(json_response['message']['name']).to eq([
-        'can\'t be blank',
-        'is too short (minimum is 0 characters)',
-        Gitlab::Regex.project_name_regex_message
-      ])
-      expect(json_response['message']['path']).to eq([
-        'can\'t be blank',
-        'is too short (minimum is 0 characters)',
-        Gitlab::Regex.send(:project_path_regex_message)
-      ])
+      expect(json_response['error']).to eq('name is missing')
     end
 
     it 'assigns attributes to project' do
@@ -438,6 +429,7 @@ describe API::API, api: true  do
 
       post api("/projects/user/#{user.id}", admin), project
 
+      expect(response).to have_http_status(201)
       project.each_pair do |k, v|
         next if %i[has_external_issue_tracker path].include?(k)
         expect(json_response[k.to_s]).to eq(v)
@@ -447,6 +439,8 @@ describe API::API, api: true  do
     it 'sets a project as public' do
       project = attributes_for(:project, :public)
       post api("/projects/user/#{user.id}", admin), project
+
+      expect(response).to have_http_status(201)
       expect(json_response['public']).to be_truthy
       expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PUBLIC)
     end
@@ -454,6 +448,8 @@ describe API::API, api: true  do
     it 'sets a project as public using :public' do
       project = attributes_for(:project, { public: true })
       post api("/projects/user/#{user.id}", admin), project
+
+      expect(response).to have_http_status(201)
       expect(json_response['public']).to be_truthy
       expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PUBLIC)
     end
@@ -461,6 +457,8 @@ describe API::API, api: true  do
     it 'sets a project as internal' do
       project = attributes_for(:project, :internal)
       post api("/projects/user/#{user.id}", admin), project
+
+      expect(response).to have_http_status(201)
       expect(json_response['public']).to be_falsey
       expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::INTERNAL)
     end
@@ -468,6 +466,7 @@ describe API::API, api: true  do
     it 'sets a project as internal overriding :public' do
       project = attributes_for(:project, :internal, { public: true })
       post api("/projects/user/#{user.id}", admin), project
+      expect(response).to have_http_status(201)
       expect(json_response['public']).to be_falsey
       expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::INTERNAL)
     end
@@ -848,7 +847,7 @@ describe API::API, api: true  do
         it 'is idempotent if not forked' do
           expect(project_fork_target.forked_from_project).to be_nil
           delete api("/projects/#{project_fork_target.id}/fork", admin)
-          expect(response).to have_http_status(200)
+          expect(response).to have_http_status(304)
           expect(project_fork_target.reload.forked_from_project).to be_nil
         end
       end
@@ -865,7 +864,7 @@ describe API::API, api: true  do
         post api("/projects/#{project.id}/share", user), group_id: group.id, group_access: Gitlab::Access::DEVELOPER, expires_at: expires_at
       end.to change { ProjectGroupLink.count }.by(1)
 
-      expect(response.status).to eq 201
+      expect(response).to have_http_status(201)
       expect(json_response['group_id']).to eq(group.id)
       expect(json_response['group_access']).to eq(Gitlab::Access::DEVELOPER)
       expect(json_response['expires_at']).to eq(expires_at.to_s)
@@ -873,18 +872,18 @@ describe API::API, api: true  do
 
     it "returns a 400 error when group id is not given" do
       post api("/projects/#{project.id}/share", user), group_access: Gitlab::Access::DEVELOPER
-      expect(response.status).to eq 400
+      expect(response).to have_http_status(400)
     end
 
     it "returns a 400 error when access level is not given" do
       post api("/projects/#{project.id}/share", user), group_id: group.id
-      expect(response.status).to eq 400
+      expect(response).to have_http_status(400)
     end
 
     it "returns a 400 error when sharing is disabled" do
       project.namespace.update(share_with_group_lock: true)
       post api("/projects/#{project.id}/share", user), group_id: group.id, group_access: Gitlab::Access::DEVELOPER
-      expect(response.status).to eq 400
+      expect(response).to have_http_status(400)
     end
 
     it 'returns a 404 error when user cannot read group' do
@@ -892,19 +891,20 @@ describe API::API, api: true  do
 
       post api("/projects/#{project.id}/share", user), group_id: private_group.id, group_access: Gitlab::Access::DEVELOPER
 
-      expect(response.status).to eq 404
+      expect(response).to have_http_status(404)
     end
 
     it 'returns a 404 error when group does not exist' do
       post api("/projects/#{project.id}/share", user), group_id: 1234, group_access: Gitlab::Access::DEVELOPER
 
-      expect(response.status).to eq 404
+      expect(response).to have_http_status(404)
     end
 
-    it "returns a 409 error when wrong params passed" do
+    it "returns a 400 error when wrong params passed" do
       post api("/projects/#{project.id}/share", user), group_id: group.id, group_access: 1234
-      expect(response.status).to eq 409
-      expect(json_response['message']).to eq 'Group access is not included in the list'
+
+      expect(response).to have_http_status(400)
+      expect(json_response['error']).to eq 'group_access does not have a valid value'
     end
   end
 
@@ -1017,7 +1017,6 @@ describe API::API, api: true  do
 
       it 'updates visibility_level from public to private' do
         project3.update_attributes({ visibility_level: Gitlab::VisibilityLevel::PUBLIC })
-
         project_param = { public: false }
         put api("/projects/#{project3.id}", user), project_param
         expect(response).to have_http_status(200)