diff --git a/package.json b/package.json
index 0bd1c26..b228472 100644
--- a/package.json
+++ b/package.json
@@ -32,7 +32,7 @@
   },
   "devDependencies": {
     "babel-cli": "^6.24.0",
-    "babel-eslint": "^7.1.1",
+    "babel-eslint": "^7.2.0",
     "babel-plugin-transform-flow-strip-types": "^6.22.0",
     "babel-plugin-transform-object-rest-spread": "^6.22.0",
     "babel-preset-env": "^1.2.2",
@@ -48,7 +48,7 @@
     "express-graphql": "^0.6.3",
     "flow-bin": "^0.42.0",
     "graphql": "^0.9.1",
-    "graphql-compose": "^1.17.3",
+    "graphql-compose": "^1.18.0",
     "jest": "^19.0.2",
     "jest-babel": "^1.0.1",
     "npm-run-all": "^4.0.1",
diff --git a/src/elasticDSL/Commons/FieldNames.js b/src/elasticDSL/Commons/FieldNames.js
index 2a90d77..834e174 100644
--- a/src/elasticDSL/Commons/FieldNames.js
+++ b/src/elasticDSL/Commons/FieldNames.js
@@ -89,6 +89,21 @@ export function getAllAsFieldConfigMap(opts: mixed, fc: mixed) {
   return getFieldConfigMap(opts, ['_all'], fc);
 }
 
+export function getFieldNamesByElasticType(
+  fieldMap: any,
+  types: ElasticDataType[]
+): string[] {
+  const fieldNames = [];
+  types.forEach(type => {
+    if (typeof fieldMap[type] === 'object') {
+      Object.keys(fieldMap[type]).forEach(fieldName => {
+        fieldNames.push(fieldName);
+      });
+    }
+  });
+  return fieldNames;
+}
+
 export function getFieldNamesType(
   opts: mixed,
   types: ElasticDataType[],
@@ -134,19 +149,15 @@ function getEnumValues(
   addAll: boolean = false
 ): GraphQLEnumValueConfigMap {
   const values = {};
-  types.forEach(type => {
-    if (addAll) {
-      values._all = {
-        value: '_all',
-      };
-    }
-    if (typeof fieldMap[type] === 'object') {
-      Object.keys(fieldMap[type]).forEach(fieldName => {
-        values[fieldName] = {
-          value: fieldName.replace('__', '.'),
-        };
-      });
-    }
+  if (addAll) {
+    values._all = {
+      value: '_all',
+    };
+  }
+  getFieldNamesByElasticType(fieldMap, types).forEach(fieldName => {
+    values[fieldName] = {
+      value: fieldName.replace('__', '.'),
+    };
   });
   return values;
 }
@@ -175,12 +186,8 @@ export function getFieldConfigMap(
     // $FlowFixMe
     fcMap._all = fc;
   }
-  types.forEach(type => {
-    if (typeof opts.fieldMap[type] === 'object') {
-      Object.keys(opts.fieldMap[type]).forEach(fieldName => {
-        fcMap[fieldName] = fc;
-      });
-    }
+  getFieldNamesByElasticType(opts.fieldMap, types).forEach(fieldName => {
+    fcMap[fieldName] = fc;
   });
 
   if (Object.keys(fcMap).length === 0) {
diff --git a/src/elasticDSL/SearchBody.js b/src/elasticDSL/SearchBody.js
index abd4da6..84a7930 100644
--- a/src/elasticDSL/SearchBody.js
+++ b/src/elasticDSL/SearchBody.js
@@ -4,6 +4,7 @@ import { InputTypeComposer } from 'graphql-compose';
 import type { FieldsMapByElasticType } from '../mappingConverter';
 import { getQueryITC, prepareQueryInResolve } from './Query/Query';
 import { getAggsITC, prepareAggsInResolve } from './Aggs/Aggs';
+import { getSortITC } from './Sort';
 import { getTypeName, getOrSetType, desc } from '../utils';
 
 export type SearchOptsT = {
@@ -31,7 +32,7 @@ export function getSearchBodyITC(opts: SearchOptsT = {}): InputTypeComposer {
         aggs: () => getAggsITC(opts),
         size: 'Int',
         from: 'Int',
-        sort: 'JSON',
+        sort: () => [getSortITC(opts)],
         _source: 'JSON',
         script_fields: 'JSON',
         post_filter: () => getQueryITC(opts),
diff --git a/src/elasticDSL/Sort.js b/src/elasticDSL/Sort.js
new file mode 100644
index 0000000..3c22757
--- /dev/null
+++ b/src/elasticDSL/Sort.js
@@ -0,0 +1,62 @@
+/* @flow */
+
+import { InputTypeComposer } from 'graphql-compose';
+import { GraphQLEnumType } from 'graphql';
+import { getTypeName, getOrSetType } from '../utils';
+import { getFieldNamesByElasticType } from './Commons/FieldNames';
+
+const sortableTypes = [
+  'byte',
+  'short',
+  'integer',
+  'long',
+  'double',
+  'float',
+  'half_float',
+  'scaled_float',
+  'token_count',
+  'date',
+  'boolean',
+  'ip',
+  'keyword',
+];
+
+export function getSortITC(opts: any = {}): InputTypeComposer | string {
+  const name = getTypeName('SortEnum', opts);
+  const description = 'Sortable fields from mapping';
+
+  if (!opts.fieldMap) {
+    return 'JSON';
+  }
+
+  return getOrSetType(name, () => {
+    const sortableFields = getFieldNamesByElasticType(
+      opts.fieldMap,
+      sortableTypes
+    );
+    if (sortableFields.length === 0) {
+      return 'JSON';
+    }
+
+    const values = {
+      _score: {
+        value: '_score',
+      },
+    };
+    sortableFields.forEach(fieldName => {
+      const dottedName = fieldName.replace('__', '.');
+      values[`${fieldName}__asc`] = {
+        value: { [dottedName]: 'asc' },
+      };
+      values[`${fieldName}__desc`] = {
+        value: { [dottedName]: 'desc' },
+      };
+    });
+
+    return new GraphQLEnumType({
+      name,
+      description,
+      values,
+    });
+  });
+}
diff --git a/src/resolvers/search.js b/src/resolvers/search.js
index ec61000..94f62fa 100644
--- a/src/resolvers/search.js
+++ b/src/resolvers/search.js
@@ -89,16 +89,18 @@ export default function createSearchResolver(
   const bodyITC = InputTypeComposer.create(argsConfigMap.body.type);
   argsConfigMap.query = bodyITC.getField('query');
   argsConfigMap.aggs = bodyITC.getField('aggs');
+  argsConfigMap.sort = bodyITC.getField('sort');
   argsConfigMap.highlight = bodyITC.getField('highlight');
 
   const topLevelArgs = [
-    'limit',
-    'skip',
     'q',
-    'opts',
     'query',
+    'sort',
+    'limit',
+    'skip',
     'aggs',
     'highlight',
+    'opts',
   ];
   argsConfigMap.opts = InputTypeComposer.create({
     name: `${sourceTC.getTypeName()}Opts`,
@@ -111,6 +113,7 @@ export default function createSearchResolver(
   });
 
   const type = getSearchOutputTC({ prefix, fieldMap, sourceTC });
+  const hitsType = type.get('hits.hits');
   type
     .addFields({
       // $FlowFixMe
@@ -118,7 +121,7 @@ export default function createSearchResolver(
       // $FlowFixMe
       max_score: 'Float',
       // $FlowFixMe
-      hits: [type.get('hits.hits')],
+      hits: hitsType ? [hitsType] : 'JSON',
     })
     .reorderFields([
       'hits',
@@ -185,6 +188,11 @@ export default function createSearchResolver(
         delete args.aggs;
       }
 
+      if (args.sort) {
+        args.body.sort = args.sort;
+        delete args.sort;
+      }
+
       if (args.opts) {
         args = {
           ...args.opts,
@@ -212,7 +220,7 @@ export default function createSearchResolver(
 
       return res;
     },
-  }).reorderArgs(['q', 'query', 'aggs', 'limit', 'skip']);
+  }).reorderArgs(['q', 'query', 'sort', 'limit', 'skip', 'aggs']);
 }
 
 export function toDottedList(
diff --git a/src/resolvers/searchConnection.js b/src/resolvers/searchConnection.js
index 765c0d4..40ff13d 100644
--- a/src/resolvers/searchConnection.js
+++ b/src/resolvers/searchConnection.js
@@ -19,7 +19,16 @@ export default function createSearchConnectionResolver(
       before: 'String',
     })
     .removeArg(['limit', 'skip'])
-    .reorderArgs(['q', 'query', 'aggs', 'first', 'after', 'last', 'before']);
+    .reorderArgs([
+      'q',
+      'query',
+      'sort',
+      'aggs',
+      'first',
+      'after',
+      'last',
+      'before',
+    ]);
 
   const searchType = searchResolver.getTypeComposer();
   const typeName = searchType.getTypeName();
@@ -43,7 +52,7 @@ export default function createSearchConnectionResolver(
   );
 
   resolver.resolve = async rp => {
-    const { args = {} } = rp;
+    const { args = {}, projection = {} } = rp;
 
     const first = parseInt(args.first, 10) || 0;
     if (first < 0) {
@@ -75,9 +84,15 @@ export default function createSearchConnectionResolver(
     args.limit = limit + 1; // +1 document, to check next page presence
     args.skip = skip;
 
+    if (projection.edges) {
+      projection.hits = projection.edges.node;
+      delete projection.edges;
+    }
+
     const res = await searchResolver.resolve(rp);
 
-    let list = res.hits.hits;
+    let list = res.hits || [];
+
     const hasExtraRecords = list.length > limit;
     if (hasExtraRecords) list = list.slice(0, limit);
     const edges = list.map(node => ({ node, cursor: dataToCursor(node.sort) }));
diff --git a/yarn.lock b/yarn.lock
index bdf5d45..f21290a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -314,15 +314,15 @@ babel-core@^6.0.0, babel-core@^6.24.0:
     slash "^1.0.0"
     source-map "^0.5.0"
 
-babel-eslint@^7.1.1:
-  version "7.1.1"
-  resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-7.1.1.tgz#8a6a884f085aa7060af69cfc77341c2f99370fb2"
+babel-eslint@^7.2.0:
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-7.2.0.tgz#8941514b9dead06f0df71b29d5d5b193a92ee0ae"
   dependencies:
-    babel-code-frame "^6.16.0"
-    babel-traverse "^6.15.0"
-    babel-types "^6.15.0"
-    babylon "^6.13.0"
-    lodash.pickby "^4.6.0"
+    babel-code-frame "^6.22.0"
+    babel-traverse "^6.23.1"
+    babel-types "^6.23.0"
+    babylon "^6.16.1"
+    lodash "^4.17.4"
 
 babel-generator@^6.18.0, babel-generator@^6.24.0:
   version "6.24.0"
@@ -796,7 +796,7 @@ babel-template@^6.16.0, babel-template@^6.22.0, babel-template@^6.23.0:
     babylon "^6.11.0"
     lodash "^4.2.0"
 
-babel-traverse@^6.15.0, babel-traverse@^6.18.0, babel-traverse@^6.22.0, babel-traverse@^6.23.0, babel-traverse@^6.23.1:
+babel-traverse@^6.18.0, babel-traverse@^6.22.0, babel-traverse@^6.23.0, babel-traverse@^6.23.1:
   version "6.23.1"
   resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.23.1.tgz#d3cb59010ecd06a97d81310065f966b699e14f48"
   dependencies:
@@ -810,7 +810,7 @@ babel-traverse@^6.15.0, babel-traverse@^6.18.0, babel-traverse@^6.22.0, babel-tr
     invariant "^2.2.0"
     lodash "^4.2.0"
 
-babel-types@^6.15.0, babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.22.0, babel-types@^6.23.0:
+babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.22.0, babel-types@^6.23.0:
   version "6.23.0"
   resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.23.0.tgz#bb17179d7538bad38cd0c9e115d340f77e7e9acf"
   dependencies:
@@ -819,11 +819,11 @@ babel-types@^6.15.0, babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.22
     lodash "^4.2.0"
     to-fast-properties "^1.0.1"
 
-babylon@6.15.0:
+babylon@6.15.0, babylon@^6.11.0, babylon@^6.13.0, babylon@^6.15.0:
   version "6.15.0"
   resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.15.0.tgz#ba65cfa1a80e1759b0e89fb562e27dccae70348e"
 
-babylon@^6.11.0, babylon@^6.13.0, babylon@^6.15.0:
+babylon@^6.16.1:
   version "6.16.1"
   resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.16.1.tgz#30c5a22f481978a9e7f8cdfdf496b11d94b404d3"
 
@@ -2001,9 +2001,9 @@ graceful-fs@^4.1.2, graceful-fs@^4.1.4, graceful-fs@^4.1.6:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
 
-graphql-compose@^1.17.3:
-  version "1.17.3"
-  resolved "https://registry.yarnpkg.com/graphql-compose/-/graphql-compose-1.17.3.tgz#ad4f19570aedc6647d6addb43d6f52d3804029b9"
+graphql-compose@^1.18.0:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/graphql-compose/-/graphql-compose-1.18.0.tgz#bb28c2336bb0e4fdd87efeb92747e781a8691752"
   dependencies:
     babel-runtime "^6.23.0"
     object-path "^0.11.4"
@@ -2865,10 +2865,6 @@ lodash.padstart@^4.1.0:
   version "4.6.1"
   resolved "https://registry.yarnpkg.com/lodash.padstart/-/lodash.padstart-4.6.1.tgz#d2e3eebff0d9d39ad50f5cbd1b52a7bce6bb611b"
 
-lodash.pickby@^4.6.0:
-  version "4.6.0"
-  resolved "https://registry.yarnpkg.com/lodash.pickby/-/lodash.pickby-4.6.0.tgz#7dea21d8c18d7703a27c704c15d3b84a67e33aff"
-
 lodash.restparam@^3.0.0:
   version "3.6.1"
   resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805"
@@ -2877,7 +2873,7 @@ lodash@^3.6.0, lodash@^3.7.0:
   version "3.10.1"
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
 
-lodash@^4.0.0, lodash@^4.12.0, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.2.0, lodash@^4.3.0:
+lodash@^4.0.0, lodash@^4.12.0, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0:
   version "4.17.4"
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"