-      
-        
-          
-        
-      
-
-      
-
-      
-        
-          Congrats! You're successfully running JSON Server.
-        
-      
-
-      
-
-      
Routes
-      
-        Here are the resources that JSON Server has loaded:
-      
-      
-        
-      
-
-      
-        You can view database current state at any time:
-        
-      
-
-      
-        You can use any HTTP verbs (GET, POST, PUT, PATCH and DELETE) and access your resources from anywhere
-        using CORS and JSONP.
-      
-
-      
Documentation
-      
-        View
-        README
-        on GitHub.
-      
-
-      
Issues
-      
Please go
-        here.
-      
-
-      
-
-      
-        To replace this page, create an index.html file in ./public, JSON Server will load it.
-      
-    
 
-
-
-    
-    
-    
-  
-
diff --git a/src/server/public/stylesheets/style.css b/src/server/public/stylesheets/style.css
deleted file mode 100644
index 098c6a4c2..000000000
--- a/src/server/public/stylesheets/style.css
+++ /dev/null
@@ -1,16 +0,0 @@
-a {
-  color: #1882BC !important;
-}
-
-img {
-  padding-top: 50px;
-  padding-bottom: 20px;
-}
-
-li {
-  list-style-type: square;
-}
-
-h4 {
-  padding-top: 20px;
-}
\ No newline at end of file
diff --git a/src/server/rewriter.js b/src/server/rewriter.js
deleted file mode 100644
index da80b513e..000000000
--- a/src/server/rewriter.js
+++ /dev/null
@@ -1,29 +0,0 @@
-var express = require('express')
-
-module.exports = function (routes) {
-  var router = express.Router()
-
-  Object.keys(routes).forEach(function (route) {
-
-    if (route.indexOf(':') !== -1) {
-      router.all(route, function (req, res, next) {
-        // Rewrite target url using params
-        var target = routes[route]
-        for (var param in req.params) {
-          target = target.replace(':' + param, req.params[param])
-        }
-        req.url = target
-        next()
-      })
-    } else {
-      router.all(route + '*', function (req, res, next) {
-        // Rewrite url by replacing prefix
-        req.url = req.url.replace(route, routes[route])
-        next()
-      })
-    }
-
-  })
-
-  return router
-}
diff --git a/src/server/router/index.js b/src/server/router/index.js
deleted file mode 100644
index 9fcb3cb1b..000000000
--- a/src/server/router/index.js
+++ /dev/null
@@ -1,91 +0,0 @@
-var express = require('express')
-var methodOverride = require('method-override')
-var bodyParser = require('body-parser')
-var _ = require('lodash')
-var _db = require('underscore-db')
-var low = require('lowdb')
-var fileAsync = require('lowdb/lib/file-async')
-var plural = require('./plural')
-var nested = require('./nested')
-var singular = require('./singular')
-var mixins = require('../mixins')
-
-module.exports = function (source) {
-
-  // Create router
-  var router = express.Router()
-
-  // Add middlewares
-  router.use(bodyParser.json({limit: '10mb'}))
-  router.use(bodyParser.urlencoded({extended: false}))
-  router.use(methodOverride())
-
-  // Create database
-  var db
-  if (_.isObject(source)) {
-    db = low()
-    db.state(source)
-  } else {
-    db = low(source, { storage: fileAsync })
-  }
-
-  // Add underscore-db methods to db
-  db._.mixin(_db)
-
-  // Add specific mixins
-  db._.mixin(mixins)
-
-  // Expose database
-  router.db = db
-
-  // Expose render
-  router.render = function (req, res) {
-    res.jsonp(res.locals.data)
-  }
-
-  // GET /db
-  function showDatabase (req, res, next) {
-    res.locals.data = db.state()
-    next()
-  }
-
-  router.get('/db', showDatabase)
-
-  router.use(nested())
-
-  // Create routes
-  db.forEach(function (value, key) {
-    if (_.isPlainObject(value)) {
-      router.use('/' + key, singular(db, key))
-      return
-    }
-
-    if (_.isArray(value)) {
-      router.use('/' + key, plural(db, key))
-      return
-    }
-
-    var msg =
-      'Type of "' + key + '" (' + typeof value + ') ' +
-      (_.isObject(source) ? '' : 'in ' + source) + ' is not supported. ' +
-      'Use objects or arrays of objects.'
-
-    throw new Error(msg)
-  }).value()
-
-  router.use(function (req, res) {
-    if (!res.locals.data) {
-      res.status(404)
-      res.locals.data = {}
-    }
-
-    router.render(req, res)
-  })
-
-  router.use(function (err, req, res, next) {
-    console.error(err.stack)
-    res.status(500).send(err.stack)
-  })
-
-  return router
-}
diff --git a/src/server/router/nested.js b/src/server/router/nested.js
deleted file mode 100644
index b8413e9f7..000000000
--- a/src/server/router/nested.js
+++ /dev/null
@@ -1,28 +0,0 @@
-var express = require('express')
-var pluralize = require('pluralize')
-var utils = require('../utils')
-
-module.exports = function () {
-
-  var router = express.Router()
-
-  // Rewrite URL (/:resource/:id/:nested -> /:nested) and request query
-  function get (req, res, next) {
-    var prop = pluralize.singular(req.params.resource)
-    req.query[prop + 'Id'] = utils.toNative(req.params.id)
-    req.url = '/' + req.params.nested
-    next()
-  }
-
-  // Rewrite URL (/:resource/:id/:nested -> /:nested) and request body
-  function post (req, res, next) {
-    var prop = pluralize.singular(req.params.resource)
-    req.body[prop + 'Id'] = utils.toNative(req.params.id)
-    req.url = '/' + req.params.nested
-    next()
-  }
-
-  return router
-    .get('/:resource/:id/:nested', get)
-    .post('/:resource/:id/:nested', post)
-}
diff --git a/src/server/router/plural.js b/src/server/router/plural.js
deleted file mode 100644
index c68c60f88..000000000
--- a/src/server/router/plural.js
+++ /dev/null
@@ -1,273 +0,0 @@
-var express = require('express')
-var _ = require('lodash')
-var pluralize = require('pluralize')
-var utils = require('../utils')
-
-module.exports = function (db, name) {
-
-  // Create router
-  var router = express.Router()
-
-  // Embed function used in GET /name and GET /name/id
-  function embed (resource, e) {
-    e && [].concat(e)
-      .forEach(function (externalResource) {
-        if (db.get(externalResource).value) {
-          var query = {}
-          var singularResource = pluralize.singular(name)
-          query[singularResource + 'Id'] = resource.id
-          resource[externalResource] = db.get(externalResource).filter(query).value()
-        }
-      })
-  }
-
-  // Expand function used in GET /name and GET /name/id
-  function expand (resource, e) {
-    e && [].concat(e)
-      .forEach(function (innerResource) {
-        var plural = pluralize(innerResource)
-        if (db.get(plural).value()) {
-          var prop = innerResource + 'Id'
-          resource[innerResource] = db.get(plural).getById(resource[prop]).value()
-        }
-      })
-  }
-
-  // GET /name
-  // GET /name?q=
-  // GET /name?attr=&attr=
-  // GET /name?_end=&
-  // GET /name?_start=&_end=&
-  // GET /name?_embed=&_expand=
-  function list (req, res, next) {
-
-    // Resource chain
-    var chain = db.get(name)
-
-    // Remove q, _start, _end, ... from req.query to avoid filtering using those
-    // parameters
-    var q = req.query.q
-    var _start = req.query._start
-    var _end = req.query._end
-    var _sort = req.query._sort
-    var _order = req.query._order
-    var _limit = req.query._limit
-    var _embed = req.query._embed
-    var _expand = req.query._expand
-    delete req.query.q
-    delete req.query._start
-    delete req.query._end
-    delete req.query._sort
-    delete req.query._order
-    delete req.query._limit
-    delete req.query._embed
-    delete req.query._expand
-
-    // Automatically delete query parameters that can't be found
-    // in the database
-    Object.keys(req.query).forEach(function (query) {
-      var arr = db.get(name).value()
-      for (var i in arr) {
-        if (
-          _.has(arr[i], query) ||
-          query === 'callback' ||
-          query === '_' ||
-          query.indexOf('_lte') !== -1 ||
-          query.indexOf('_gte') !== -1 ||
-          query.indexOf('_ne') !== -1 ||
-          query.indexOf('_like') !== -1
-        ) return
-      }
-      delete req.query[query]
-    })
-
-    if (q) {
-
-      // Full-text search
-      q = q.toLowerCase()
-
-      chain = chain.filter(function (obj) {
-        for (var key in obj) {
-          var value = obj[key]
-          if (db._.deepQuery(value, q)) {
-            return true
-          }
-        }
-      })
-
-    }
-
-    Object.keys(req.query).forEach(function (key) {
-      // Don't take into account JSONP query parameters
-      // jQuery adds a '_' query parameter too
-      if (key !== 'callback' && key !== '_') {
-        // Always use an array, in case req.query is an array
-        var arr = [].concat(req.query[key])
-
-        chain = chain.filter(function (element) {
-          return arr
-            .map(utils.toNative)
-            .map(function (value) {
-              var isDifferent = key.indexOf('_ne') !== -1
-              var isRange = key.indexOf('_lte') !== -1 || key.indexOf('_gte') !== -1
-              var isLike = key.indexOf('_like') !== -1
-              var path = key.replace(/(_lte|_gte|_ne|_like)$/, '')
-              var elementValue = _.get(element, path)
-
-              if (isRange) {
-                var isLowerThan = key.indexOf('_gte') !== -1
-
-                if (isLowerThan) {
-                  return value <= elementValue
-                } else {
-                  return value >= elementValue
-                }
-              } else if (isDifferent) {
-                return value !== elementValue
-              } else if (isLike) {
-                return new RegExp(value, 'i').test(elementValue)
-              } else {
-                return _.matchesProperty(key, value)(element)
-              }
-            }).reduce(function (a, b) {
-              return a || b
-            })
-        })
-      }
-    })
-
-    // Sort
-    if (_sort) {
-      _order = _order || 'ASC'
-
-      chain = chain.sortBy(function (element) {
-        return _.get(element, _sort)
-      })
-
-      if (_order === 'DESC') {
-        chain = chain.reverse()
-      }
-    }
-
-    // Slice result
-    if (_end || _limit) {
-      res.setHeader('X-Total-Count', chain.size())
-      res.setHeader('Access-Control-Expose-Headers', 'X-Total-Count')
-    }
-
-    _start = parseInt(_start, 10) || 0
-
-    if (_end) {
-      _end = parseInt(_end, 10)
-      chain = chain.slice(_start, _end)
-    } else if (_limit) {
-      _limit = parseInt(_limit, 10)
-      chain = chain.slice(_start, _start + _limit)
-    }
-
-    // embed and expand
-    chain = chain
-      .cloneDeep()
-      .forEach(function (element) {
-        embed(element, _embed)
-        expand(element, _expand)
-      })
-
-    res.locals.data = chain.value()
-    next()
-  }
-
-  // GET /name/:id
-  // GET /name/:id?_embed=&_expand
-  function show (req, res, next) {
-    var _embed = req.query._embed
-    var _expand = req.query._expand
-    var id = utils.toNative(req.params.id)
-    var resource = db.get(name).getById(id).value()
-
-    if (resource) {
-      // Clone resource to avoid making changes to the underlying object
-      resource = _.cloneDeep(resource)
-
-      // Embed other resources based on resource id
-      // /posts/1?_embed=comments
-      embed(resource, _embed)
-
-      // Expand inner resources based on id
-      // /posts/1?_expand=user
-      expand(resource, _expand)
-
-      res.locals.data = resource
-    }
-
-    next()
-  }
-
-  // POST /name
-  function create (req, res, next) {
-    for (var key in req.body) {
-      req.body[key] = utils.toNative(req.body[key])
-    }
-
-    var resource = db.get(name)
-      .insert(req.body)
-      .value()
-
-    res.status(201)
-    res.locals.data = resource
-    next()
-  }
-
-  // PUT /name/:id
-  // PATCH /name/:id
-  function update (req, res, next) {
-    for (var key in req.body) {
-      req.body[key] = utils.toNative(req.body[key])
-    }
-
-    var id = utils.toNative(req.params.id)
-    var chain = db.get(name)
-
-    chain = req.method === 'PATCH' ?
-      chain.updateById(id, req.body) :
-      chain.replaceById(id, req.body)
-
-    var resource = chain.value()
-
-    if (resource) {
-      res.locals.data = resource
-    }
-
-    next()
-  }
-
-  // DELETE /name/:id
-  function destroy (req, res, next) {
-    var resource = db.get(name).removeById(utils.toNative(req.params.id)).value()
-
-    // Remove dependents documents
-    var removable = db._.getRemovable(db.state())
-
-    _.each(removable, function (item) {
-      db.get(item.name).removeById(item.id).value()
-    })
-
-    if (resource) {
-      res.locals.data = {}
-    }
-
-    next()
-  }
-
-  router.route('/')
-    .get(list)
-    .post(create)
-
-  router.route('/:id')
-    .get(show)
-    .put(update)
-    .patch(update)
-    .delete(destroy)
-
-  return router
-}
diff --git a/src/server/router/singular.js b/src/server/router/singular.js
deleted file mode 100644
index 151abb9d5..000000000
--- a/src/server/router/singular.js
+++ /dev/null
@@ -1,41 +0,0 @@
-var express = require('express')
-
-module.exports = function (db, name) {
-
-  var router = express.Router()
-
-  function show (req, res, next) {
-    res.locals.data = db.get(name).value()
-    next()
-  }
-
-  function create (req, res, next) {
-    db.set(name, req.body).value()
-    res.locals.data = db.get(name).value()
-    res.status(201)
-    next()
-  }
-
-  function update (req, res, next) {
-    if (req.method === 'PUT') {
-      db.set(name, req.body)
-        .value()
-    } else {
-      db.get(name)
-        .assign(req.body)
-        .value()
-    }
-
-    res.locals.data = db.get(name).value()
-    next()
-  }
-
-  router.route('/')
-    .get(show)
-    .post(create)
-    .put(update)
-    .patch(update)
-
-  return router
-
-}
diff --git a/src/server/utils.js b/src/server/utils.js
deleted file mode 100644
index 397e2ffc4..000000000
--- a/src/server/utils.js
+++ /dev/null
@@ -1,22 +0,0 @@
-module.exports = {
-  toNative: toNative
-}
-
-// Turns string to native.
-// Example:
-//   'true' -> true
-//   '1' -> 1
-function toNative (value) {
-  if (typeof value === 'string') {
-    if (value === ''
-       || value.trim() !== value
-       || (value.length > 1 && value[0] === '0')) {
-      return value
-    } else if (value === 'true' || value === 'false') {
-      return value === 'true'
-    } else if (!isNaN(+value)) {
-      return +value
-    }
-  }
-  return value
-}
diff --git a/src/service.test.ts b/src/service.test.ts
new file mode 100644
index 000000000..818a2457b
--- /dev/null
+++ b/src/service.test.ts
@@ -0,0 +1,392 @@
+import assert from 'node:assert/strict'
+import test from 'node:test'
+
+import { Low, Memory } from 'lowdb'
+
+import { Data, Item, PaginatedItems, Service } from './service.js'
+
+const defaultData = { posts: [], comments: [], object: {} }
+const adapter = new Memory