Roughing out the demo app

This commit is contained in:
Nate Taylor
2019-10-23 07:41:03 -05:00
parent 5bc58a2d40
commit 94be06d3c4
2841 changed files with 274065 additions and 2 deletions

12
node_modules/json-server/.babelrc generated vendored Normal file
View File

@@ -0,0 +1,12 @@
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"node": "8"
}
}
]
]
}

2
node_modules/json-server/.eslintignore generated vendored Normal file
View File

@@ -0,0 +1,2 @@
src/server/public
lib

14
node_modules/json-server/.eslintrc.js generated vendored Normal file
View File

@@ -0,0 +1,14 @@
module.exports = {
extends: ['standard', 'prettier'],
plugins: ['prettier'],
rules: {
'prettier/prettier': [
'error',
{
singleQuote: true,
semi: false,
},
]
},
env: { jest: true }
}

2
node_modules/json-server/.github/FUNDING.yml generated vendored Normal file
View File

@@ -0,0 +1,2 @@
github: typicode
patreon: typicode

4
node_modules/json-server/.travis.yml generated vendored Normal file
View File

@@ -0,0 +1,4 @@
language: node_js
node_js:
- "node"
- "8"

303
node_modules/json-server/CHANGELOG.md generated vendored Normal file
View File

@@ -0,0 +1,303 @@
# Change Log
Changes for 0.15.1+ can now be found in https://github.com/typicode/json-server/releases
## 0.15.0 - 2019-05-21
* __Breaking__ Require Node 8
* Upgrade dependencies
* Reduce package size
## 0.14.2 - 2018-12-26
* Fix `db.getState is not a function`
## 0.14.1 - 2018-12-25
* Show error message if port is already used
* Upgrade to [lowdb](https://github.com/typicode/lowdb) `1.0`
## 0.14.0 - 2018-06-09
* Listen to `localhost` by default, instead of `0.0.0.0`
## 0.13.0 - 2018-05-30
* Bundle all index page assets so that you access it without network connection
* Drop Node 4 support
## 0.12.2 - 2018-04-26
* Add `_delay` query parameter
* Upgrade `please-upgrade-node` dependency
## 0.12.1 - 2017-11-02
* Disable logging for static content requests
* Remove bad `console.log`
* Update `dependencies`
* Use [`nanoid`](https://github.com/ai/nanoid)
## 0.12.0 - 2017-08-02
Re-include `body-parser` in `jsonServer.defaults()`
If you're using JSON Server in an Express server and experience issues, you can disable it by passing
```js
jsonServer.defaults({ bodyParser: false })
```
## 0.11.2 - 2017-07-10
Fix `engines` field in `package.json`
## 0.11.1 - 2017-07-10
Add [please-upgrade-node](https://github.com/typicode/please-upgrade-node)
## 0.11.0 - 2017-07-05
Switch to [express-urlrewrite](https://github.com/kapouer/express-urlrewrite) to support rewriting query parameters (e.g. `/articles?id=1 # → /posts/1`)
If you're rewriting default routes, you'll need to update your `routes.json` file
(see [add custom routes](https://github.com/typicode/json-server#add-custom-routes) for updated doc).
## 0.10.3 - 2017-06-28
* Fix `line-break` error in CLI
## 0.10.2 - 2017-06-28
* Add `--foreignKeySuffix` option (e.g. snake case `post_id`) [#556](https://github.com/typicode/json-server/pull/556) [#570](https://github.com/typicode/json-server/pull/570)
## 0.10.1 - 2017-05-16
* Multiple fields sorting `GET /posts?_sort=user,views&_order=desc,asc`
## 0.10.0 - 2017-04-26
* __Drop Node `v0.12` support__
* Prevent `TypeError` when a filter is applied on a `null` value [#510](https://github.com/typicode/json-server/issues/510)
## 0.9.6 - 2017-03-08
* Update index page
* Improve performances ([lowdb](https://github.com/typicode/lowdb) `v0.15`)
* Add `Location` header to newly created resources [#473](https://github.com/typicode/json-server/pull/473)
## 0.9.5 - 2017-02-11
* Display custom routes on homepage
* Fix duplicate query params error [#352](https://github.com/typicode/json-server/issues/352)
## 0.9.4 - 2016-12-08
* Improve rewriter [#431](https://github.com/typicode/json-server/issues/431)
* Improve watch mode [#427](https://github.com/typicode/json-server/pull/427)
## 0.9.3 - 2016-12-07
* Fix [#396](https://github.com/typicode/json-server/issues/396) PUT/PATCH saves the updated item with an id that has been converted to string
## 0.9.2 - 2016-11-29
* Fix [#221](https://github.com/typicode/json-server/issues/221) `nohup` support
* Fix [#420](https://github.com/typicode/json-server/issues/420) TypeError when watching `db.json`
## 0.9.1 - 2016-11-21
* Fix
* [#412](https://github.com/typicode/json-server/issues/412)
* [#451](https://github.com/typicode/json-server/issues/411)
## 0.9.0 - 2016-11-11
* Shorter `uuid`
* No automatic conversion of strings to boolean or integer
* Create a default `db.json` file if it doesn't exist
* Fix
* [#361](https://github.com/typicode/json-server/issues/361)
* [#363](https://github.com/typicode/json-server/issues/363) [#365](https://github.com/typicode/json-server/issues/365)
* [#374](https://github.com/typicode/json-server/issues/374)
* [#383](https://github.com/typicode/json-server/issues/383)
* Updated dependencies and codebase to ES6
## 0.8.23 - 2016-11-03
* Fix `Links` header
## 0.8.22 - 2016-10-04
* Fix `Links` header issue when using `_page`
* Add query params support to the route rewriter
## 0.8.21 - 2016-09-13
* Fix bodyParser issue when using custom routes
## 0.8.20 - 2016-09-12
* Fix [#355](https://github.com/typicode/json-server/issues/355)
* Add `_page` support
## 0.8.19 - 2016-08-18
* Fix [#341](https://github.com/typicode/json-server/issues/341)
## 0.8.18 - 2016-08-17
* Add CLI option `--middlewares` and support them in `json-server.json` config file
## 0.8.17 - 2016-07-25
* Fix snapshot creation for JS files (ex: `json-server generator.js`)
## 0.8.16 - 2016-07-11
* Support `x-www-form-urlencoded`
## 0.8.15 - 2016-07-03
* Bug fix: `--watch` option on OS X
## 0.8.14 - 2016-05-15
* Bug fix: data wasn't written to file in `v0.8.13` and `v0.8.12`
## 0.8.13 - 2016-05-12
* Make `_like` operator case insensitive
## 0.8.12 - 2016-05-08
* Minor bug fix
## 0.8.11 - 2016-05-08
* Support sort by nested field (e.g. `_sort=author.name`)
* Fix `graceful-fs` warning
## 0.8.10 - 2016-04-18
* CLI option `-ng/--no-gzip` to disable `gzip` compression
## 0.8.9 - 2016-03-17
* CLI can now read options from `json-server.json` if present
* CLI option `-c/--config` to point to a different configuration file
## 0.8.8 - 2016-02-13
### Fixed
* Fix #233
## 0.8.7 - 2016-01-22
### Added
* `gzip` compression to improve performances
* CLI option `-nc/--no-cors` to disable CORS
## 0.8.6 - 2016-01-07
### Added
* CLI option `-ro/--read-only` to allow only GET requests
## 0.8.5 - 2015-12-28
### Fixed
* Fix #177
## 0.8.4 - 2015-12-13
### Added
* Like operator `GET /posts?title_like=json` (accepts RegExp)
## 0.8.3 - 2015-11-25
### Added
* CLI option `-q/--quiet`
* Nested route `POST /posts/1/comments`
* Not equal operator `GET /posts?id_ne=1`
## 0.8.2 - 2015-10-15
### Added
* CLI option `-S/--snapshots` to set a custom snapshots directory.
### Fixed
* Fix plural resources: `DELETE` should return `404` if resource doesn't exist.
## 0.8.1 - 2015-10-06
### Fixed
* Fix plural resources: `PUT` should replace resource instead of updating properties.
* Fix singular resources: `POST`, `PUT`, `PATCH` should not convert resource properties.
## 0.8.0 - 2015-09-21
### Changed
* `jsonServer.defaults` is now a function and can take an object.
If you're using the project as a module, you need to update your code:
```js
// Before
jsonServer.defaults
// After
jsonServer.defaults()
jsonServer.defaults({ static: '/some/path'})
```
* Automatically ignore unknown query parameters.
```bash
# Before
GET /posts?author=typicode&foo=bar # []
# After
GET /posts?author=typicode&foo=bar # [{...}, {...}]
```
### Added
* CLI option for setting a custom static files directory.
```bash
json-server --static some/path
```
## 0.7.28 - 2015-09-09
```bash
# Support range
GET /products?price_gte=50&price_lte=100
```
## 0.7.27 - 2015-09-02
### Added
```bash
# Support OR
GET /posts?id=1&id2
GET /posts?category=javascript&category=html
```
## 0.7.26 - 2015-09-01
### Added
```bash
# Support embed and expand in lists
GET /posts?embed=comments
GET /posts?expand=user
```

20
node_modules/json-server/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2015 typicode
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

613
node_modules/json-server/README.md generated vendored Normal file
View File

@@ -0,0 +1,613 @@
# JSON Server [![](https://travis-ci.org/typicode/json-server.svg?branch=master)](https://travis-ci.org/typicode/json-server) [![](https://badge.fury.io/js/json-server.svg)](http://badge.fury.io/js/json-server)
Get a full fake REST API with __zero coding__ in __less than 30 seconds__ (seriously)
Created with <3 for front-end developers who need a quick back-end for prototyping and mocking.
* [Egghead.io free video tutorial - Creating demo APIs with json-server](https://egghead.io/lessons/nodejs-creating-demo-apis-with-json-server)
* [JSONPlaceholder - Live running version](https://jsonplaceholder.typicode.com)
* [__My JSON Server__ - no installation required, use your own data](https://my-json-server.typicode.com)
See also:
* :dog: [husky - Git hooks made easy](https://github.com/typicode/husky)
* :hotel: [hotel - developer tool with local .localhost domain and https out of the box](https://github.com/typicode/hotel)
<h2 align="center">Sponsors</h2>
<h3 align="center">Gold</h3>
<p align="center">
<a href="https://tryretool.com/?utm_source=sponsor&utm_campaign=typicode" target="_blank">
<img src="https://i.imgur.com/IBItATn.png" height="60px">
</a>
</p>
---
<h3 align="center">Bronze</h3>
<p align="center">
<a href="https://www.zinggrid.com/hello/json-server?utm_source=jsonserver&utm_medium=github&utm_campaign=sponsorship" target="_blank">
<img src="https://i.imgur.com/3mJGTAQ.png" height="30px">
</a>
</p>
---
<p>&nbsp;</p>
<p align="center">
<a href="https://github.com/users/typicode/sponsorship">Become a sponsor and have your company logo here</a>
</p>
## Table of contents
<!-- toc -->
- [Getting started](#getting-started)
- [Routes](#routes)
* [Plural routes](#plural-routes)
* [Singular routes](#singular-routes)
* [Filter](#filter)
* [Paginate](#paginate)
* [Sort](#sort)
* [Slice](#slice)
* [Operators](#operators)
* [Full-text search](#full-text-search)
* [Relationships](#relationships)
* [Database](#database)
* [Homepage](#homepage)
- [Extras](#extras)
* [Static file server](#static-file-server)
* [Alternative port](#alternative-port)
* [Access from anywhere](#access-from-anywhere)
* [Remote schema](#remote-schema)
* [Generate random data](#generate-random-data)
* [HTTPS](#https)
* [Add custom routes](#add-custom-routes)
* [Add middlewares](#add-middlewares)
* [CLI usage](#cli-usage)
* [Module](#module)
+ [Simple example](#simple-example)
+ [Custom routes example](#custom-routes-example)
+ [Access control example](#access-control-example)
+ [Custom output example](#custom-output-example)
+ [Rewriter example](#rewriter-example)
+ [Mounting JSON Server on another endpoint example](#mounting-json-server-on-another-endpoint-example)
+ [API](#api)
* [Deployment](#deployment)
- [Links](#links)
* [Video](#video)
* [Articles](#articles)
* [Third-party tools](#third-party-tools)
- [License](#license)
<!-- tocstop -->
## Getting started
Install JSON Server
```
npm install -g json-server
```
Create a `db.json` file with some data
```json
{
"posts": [
{ "id": 1, "title": "json-server", "author": "typicode" }
],
"comments": [
{ "id": 1, "body": "some comment", "postId": 1 }
],
"profile": { "name": "typicode" }
}
```
Start JSON Server
```bash
json-server --watch db.json
```
Now if you go to [http://localhost:3000/posts/1](http://localhost:3000/posts/1), you'll get
```json
{ "id": 1, "title": "json-server", "author": "typicode" }
```
Also when doing requests, it's good to know that:
- If you make POST, PUT, PATCH or DELETE requests, changes will be automatically and safely saved to `db.json` using [lowdb](https://github.com/typicode/lowdb).
- Your request body JSON should be object enclosed, just like the GET output. (for example `{"name": "Foobar"}`)
- Id values are not mutable. Any `id` value in the body of your PUT or PATCH request will be ignored. Only a value set in a POST request will be respected, but only if not already taken.
- A POST, PUT or PATCH request should include a `Content-Type: application/json` header to use the JSON in the request body. Otherwise it will result in a 200 OK but without changes being made to the data.
## Routes
Based on the previous `db.json` file, here are all the default routes. You can also add [other routes](#add-custom-routes) using `--routes`.
### Plural routes
```
GET /posts
GET /posts/1
POST /posts
PUT /posts/1
PATCH /posts/1
DELETE /posts/1
```
### Singular routes
```
GET /profile
POST /profile
PUT /profile
PATCH /profile
```
### Filter
Use `.` to access deep properties
```
GET /posts?title=json-server&author=typicode
GET /posts?id=1&id=2
GET /comments?author.name=typicode
```
### Paginate
Use `_page` and optionally `_limit` to paginate returned data.
In the `Link` header you'll get `first`, `prev`, `next` and `last` links.
```
GET /posts?_page=7
GET /posts?_page=7&_limit=20
```
_10 items are returned by default_
### Sort
Add `_sort` and `_order` (ascending order by default)
```
GET /posts?_sort=views&_order=asc
GET /posts/1/comments?_sort=votes&_order=asc
```
For multiple fields, use the following format:
```
GET /posts?_sort=user,views&_order=desc,asc
```
### Slice
Add `_start` and `_end` or `_limit` (an `X-Total-Count` header is included in the response)
```
GET /posts?_start=20&_end=30
GET /posts/1/comments?_start=20&_end=30
GET /posts/1/comments?_start=20&_limit=10
```
_Works exactly as [Array.slice](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/slice) (i.e. `_start` is inclusive and `_end` exclusive)_
### Operators
Add `_gte` or `_lte` for getting a range
```
GET /posts?views_gte=10&views_lte=20
```
Add `_ne` to exclude a value
```
GET /posts?id_ne=1
```
Add `_like` to filter (RegExp supported)
```
GET /posts?title_like=server
```
### Full-text search
Add `q`
```
GET /posts?q=internet
```
### Relationships
To include children resources, add `_embed`
```
GET /posts?_embed=comments
GET /posts/1?_embed=comments
```
To include parent resource, add `_expand`
```
GET /comments?_expand=post
GET /comments/1?_expand=post
```
To get or create nested resources (by default one level, [add custom routes](#add-custom-routes) for more)
```
GET /posts/1/comments
POST /posts/1/comments
```
### Database
```
GET /db
```
### Homepage
Returns default index file or serves `./public` directory
```
GET /
```
## Extras
### Static file server
You can use JSON Server to serve your HTML, JS and CSS, simply create a `./public` directory
or use `--static` to set a different static files directory.
```bash
mkdir public
echo 'hello world' > public/index.html
json-server db.json
```
```bash
json-server db.json --static ./some-other-dir
```
### Alternative port
You can start JSON Server on other ports with the `--port` flag:
```bash
$ json-server --watch db.json --port 3004
```
### Access from anywhere
You can access your fake API from anywhere using CORS and JSONP.
### Remote schema
You can load remote schemas.
```bash
$ json-server http://example.com/file.json
$ json-server http://jsonplaceholder.typicode.com/db
```
### Generate random data
Using JS instead of a JSON file, you can create data programmatically.
```javascript
// index.js
module.exports = () => {
const data = { users: [] }
// Create 1000 users
for (let i = 0; i < 1000; i++) {
data.users.push({ id: i, name: `user${i}` })
}
return data
}
```
```bash
$ json-server index.js
```
__Tip__ use modules like [Faker](https://github.com/Marak/faker.js), [Casual](https://github.com/boo1ean/casual), [Chance](https://github.com/victorquinn/chancejs) or [JSON Schema Faker](https://github.com/json-schema-faker/json-schema-faker).
### HTTPS
There are many ways to set up SSL in development. One simple way is to use [hotel](https://github.com/typicode/hotel).
### Add custom routes
Create a `routes.json` file. Pay attention to start every route with `/`.
```json
{
"/api/*": "/$1",
"/:resource/:id/show": "/:resource/:id",
"/posts/:category": "/posts?category=:category",
"/articles\\?id=:id": "/posts/:id"
}
```
Start JSON Server with `--routes` option.
```bash
json-server db.json --routes routes.json
```
Now you can access resources using additional routes.
```sh
/api/posts # → /posts
/api/posts/1 # → /posts/1
/posts/1/show # → /posts/1
/posts/javascript # → /posts?category=javascript
/articles?id=1 # → /posts/1
```
### Add middlewares
You can add your middlewares from the CLI using `--middlewares` option:
```js
// hello.js
module.exports = (req, res, next) => {
res.header('X-Hello', 'World')
next()
}
```
```bash
json-server db.json --middlewares ./hello.js
json-server db.json --middlewares ./first.js ./second.js
```
### CLI usage
```
json-server [options] <source>
Options:
--config, -c Path to config file [default: "json-server.json"]
--port, -p Set port [default: 3000]
--host, -H Set host [default: "localhost"]
--watch, -w Watch file(s) [boolean]
--routes, -r Path to routes file
--middlewares, -m Paths to middleware files [array]
--static, -s Set static files directory
--read-only, --ro Allow only GET requests [boolean]
--no-cors, --nc Disable Cross-Origin Resource Sharing [boolean]
--no-gzip, --ng Disable GZIP Content-Encoding [boolean]
--snapshots, -S Set snapshots directory [default: "."]
--delay, -d Add delay to responses (ms)
--id, -i Set database id property (e.g. _id) [default: "id"]
--foreignKeySuffix, --fks Set foreign key suffix, (e.g. _id as in post_id)
[default: "Id"]
--quiet, -q Suppress log messages from output [boolean]
--help, -h Show help [boolean]
--version, -v Show version number [boolean]
Examples:
json-server db.json
json-server file.js
json-server http://example.com/db.json
https://github.com/typicode/json-server
```
You can also set options in a `json-server.json` configuration file.
```json
{
"port": 3000
}
```
### Module
If you need to add authentication, validation, or __any behavior__, you can use the project as a module in combination with other Express middlewares.
#### Simple example
```sh
$ npm install json-server --save-dev
```
```js
// server.js
const jsonServer = require('json-server')
const server = jsonServer.create()
const router = jsonServer.router('db.json')
const middlewares = jsonServer.defaults()
server.use(middlewares)
server.use(router)
server.listen(3000, () => {
console.log('JSON Server is running')
})
```
```sh
$ node server.js
```
The path you provide to the `jsonServer.router` function is relative to the directory from where you launch your node process. If you run the above code from another directory, its better to use an absolute path:
```js
const path = require('path')
const router = jsonServer.router(path.join(__dirname, 'db.json'))
```
For an in-memory database, simply pass an object to `jsonServer.router()`.
Please note also that `jsonServer.router()` can be used in existing Express projects.
#### Custom routes example
Let's say you want a route that echoes query parameters and another one that set a timestamp on every resource created.
```js
const jsonServer = require('json-server')
const server = jsonServer.create()
const router = jsonServer.router('db.json')
const middlewares = jsonServer.defaults()
// Set default middlewares (logger, static, cors and no-cache)
server.use(middlewares)
// Add custom routes before JSON Server router
server.get('/echo', (req, res) => {
res.jsonp(req.query)
})
// To handle POST, PUT and PATCH you need to use a body-parser
// You can use the one used by JSON Server
server.use(jsonServer.bodyParser)
server.use((req, res, next) => {
if (req.method === 'POST') {
req.body.createdAt = Date.now()
}
// Continue to JSON Server router
next()
})
// Use default router
server.use(router)
server.listen(3000, () => {
console.log('JSON Server is running')
})
```
#### Access control example
```js
const jsonServer = require('json-server')
const server = jsonServer.create()
const router = jsonServer.router('db.json')
const middlewares = jsonServer.defaults()
server.use(middlewares)
server.use((req, res, next) => {
if (isAuthorized(req)) { // add your authorization logic here
next() // continue to JSON Server router
} else {
res.sendStatus(401)
}
})
server.use(router)
server.listen(3000, () => {
console.log('JSON Server is running')
})
```
#### Custom output example
To modify responses, overwrite `router.render` method:
```javascript
// In this example, returned resources will be wrapped in a body property
router.render = (req, res) => {
res.jsonp({
body: res.locals.data
})
}
```
You can set your own status code for the response:
```javascript
// In this example we simulate a server side error response
router.render = (req, res) => {
res.status(500).jsonp({
error: "error message here"
})
}
```
#### Rewriter example
To add rewrite rules, use `jsonServer.rewriter()`:
```javascript
// Add this before server.use(router)
server.use(jsonServer.rewriter({
'/api/*': '/$1',
'/blog/:resource/:id/show': '/:resource/:id'
}))
```
#### Mounting JSON Server on another endpoint example
Alternatively, you can also mount the router on `/api`.
```javascript
server.use('/api', router)
```
#### API
__`jsonServer.create()`__
Returns an Express server.
__`jsonServer.defaults([options])`__
Returns middlewares used by JSON Server.
* options
* `static` path to static files
* `logger` enable logger middleware (default: true)
* `bodyParser` enable body-parser middleware (default: true)
* `noCors` disable CORS (default: false)
* `readOnly` accept only GET requests (default: false)
__`jsonServer.router([path|object])`__
Returns JSON Server router.
### Deployment
You can deploy JSON Server. For example, [JSONPlaceholder](http://jsonplaceholder.typicode.com) is an online fake API powered by JSON Server and running on Heroku.
## Links
### Video
* [Creating Demo APIs with json-server on egghead.io](https://egghead.io/lessons/nodejs-creating-demo-apis-with-json-server)
### Articles
* [Node Module Of The Week - json-server](http://nmotw.in/json-server/)
* [Mock up your REST API with JSON Server](http://www.betterpixels.co.uk/projects/2015/05/09/mock-up-your-rest-api-with-json-server/)
* [ng-admin: Add an AngularJS admin GUI to any RESTful API](http://marmelab.com/blog/2014/09/15/easy-backend-for-your-restful-api.html)
* [Fast prototyping using Restangular and Json-server](https://glebbahmutov.com/blog/fast-prototyping-restangular-and-json-server/)
* [Create a Mock REST API in Seconds for Prototyping your Frontend](https://coligo.io/create-mock-rest-api-with-json-server/)
* [No API? No Problem! Rapid Development via Mock APIs](https://medium.com/@housecor/rapid-development-via-mock-apis-e559087be066#.93d7w8oro)
* [Zero Code REST With json-server](https://dzone.com/articles/zero-code-rest-with-json-server)
### Third-party tools
* [Grunt JSON Server](https://github.com/tfiwm/grunt-json-server)
* [Docker JSON Server](https://github.com/clue/docker-json-server)
* [JSON Server GUI](https://github.com/naholyr/json-server-gui)
* [JSON file generator](https://github.com/dfsq/json-server-init)
* [JSON Server extension](https://github.com/maty21/json-server-extension)
## License
MIT
[Supporters](https://thanks.typicode.com) ✨

21
node_modules/json-server/appveyor.yml generated vendored Normal file
View File

@@ -0,0 +1,21 @@
# Test against this version of Node.js
environment:
nodejs_version: "8"
# Install scripts. (runs after repo cloning)
install:
# Get the latest stable version of Node.js
- ps: Install-Product node $env:nodejs_version
# install modules
- npm install
# Post-install test scripts.
test_script:
# Output useful info for debugging.
- node --version
- npm --version
# run tests
- npm test
# Don't actually build.
build: off

9
node_modules/json-server/db.json generated vendored Normal file
View File

@@ -0,0 +1,9 @@
{
"posts": [
{ "id": 1, "title": "json-server", "author": "typicode" }
],
"comments": [
{ "id": 1, "body": "some comment", "postId": 1 }
],
"profile": { "name": "typicode" }
}

BIN
node_modules/json-server/dist/favicon.ico generated vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

82
node_modules/json-server/dist/index.html generated vendored Normal file
View File

@@ -0,0 +1,82 @@
<html>
<head>
<link
rel="stylesheet"
href="https://use.fontawesome.com/releases/v5.8.2/css/all.css"
integrity="sha384-oS3vJWv+0UjzBfQzYUhtDYW+Pj2yciDJxpsK1OYPAYjqT085Qq/1cq5FLXAZQ7Ay"
crossorigin="anonymous"
/>
<title>JSON Server</title>
<link rel="shortcut icon" href="favicon.ico"><link href="main.css" rel="stylesheet"></head>
<body>
<header>
<div class="container">
<nav>
<ul>
<li class="title">
JSON Server
</li>
<li>
<a href="https://github.com/users/typicode/sponsorship">
<i class="fas fa-heart"></i>GitHub Sponsors
</a>
</li>
<li>
<a href="https://my-json-server.typicode.com">
<i class="fas fa-burn"></i>My JSON Server
</a>
</li>
<li>
<a href="https://thanks.typicode.com">
<i class="far fa-laugh"></i>Supporters
</a>
</li>
</ul>
</nav>
</div>
</header>
<main>
<div class="container">
<h1>Congrats!</h1>
<p>
You're successfully running JSON Server
<br />
✧*。٩(ˊᗜˋ*)و✧*。
</p>
<div id="resources"></div>
<p>
To access and modify resources, you can use any HTTP method:
</p>
<p>
<code>GET</code>
<code>POST</code>
<code>PUT</code>
<code>PATCH</code>
<code>DELETE</code>
<code>OPTIONS</code>
</p>
<div id="custom-routes"></div>
<h1>Documentation</h1>
<p>
<a href="https://github.com/typicode/json-server">
README
</a>
</p>
</div>
</main>
<footer>
<div class="container">
<p>
To replace this page, create a
<code>./public/index.html</code> file.
</p>
</div>
</footer>
<script type="text/javascript" src="main.js"></script></body>
</html>

114
node_modules/json-server/dist/main.css generated vendored Normal file
View File

@@ -0,0 +1,114 @@
body {
display: flex;
min-height: 100vh;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif;
flex-direction: column;
padding: 0;
margin: 0;
color: #3b4252;
letter-spacing: 0;
}
.container {
max-width: 960px;
margin: auto;
padding: 1rem;
}
header {
border-bottom: 1px solid #eee;
}
header a {
color: inherit;
text-decoration: none;
}
header a:hover {
text-decoration: underline;
}
nav ul {
display: flex;
flex-wrap: nowrap;
justify-content: space-between;
}
nav li.title {
flex-grow: 5;
text-align: left;
font-weight: bold;
font-size: 1.4rem;
color: #3b4252;
}
nav li {
flex-grow: 1;
align-self: center;
text-align: right;
color: #4c566a;
}
.fa-heart {
color: deeppink;
}
main {
flex: 1;
}
footer {
margin-top: 4rem;
border-top: 1px solid #eee;
}
h1 {
margin-top: 4rem;
font-weight: normal;
}
i {
margin-right: 0.5rem;
}
a {
color: #5e81ac;
}
a:hover {
color: #81a1c1;
text-decoration: underline;
}
table {
margin-left: 0;
}
td {
border: 0;
padding: 0 1em 0.5em 0;
}
td:first-child {
width: 1%;
white-space: nowrap;
}
ul {
list-style-position: inside;
padding-left: 0;
}
li {
list-style-type: none;
margin-bottom: 0.2rem;
}
code {
padding: 0.2rem;
margin: 0rem 0.2rem;
border-radius: 0.2rem;
background: #e5e9f0;
}

1
node_modules/json-server/dist/main.js generated vendored Normal file

File diff suppressed because one or more lines are too long

6
node_modules/json-server/lib/cli/bin.js generated vendored Executable file
View File

@@ -0,0 +1,6 @@
#!/usr/bin/env node
"use strict";
require('please-upgrade-node')(require('../../package.json'));
require('./')();

87
node_modules/json-server/lib/cli/index.js generated vendored Normal file
View File

@@ -0,0 +1,87 @@
"use strict";
const updateNotifier = require('update-notifier');
const yargs = require('yargs');
const run = require('./run');
const pkg = require('../../package.json');
module.exports = function () {
updateNotifier({
pkg
}).notify();
const argv = yargs.config('config').usage('$0 [options] <source>').options({
port: {
alias: 'p',
description: 'Set port',
default: 3000
},
host: {
alias: 'H',
description: 'Set host',
default: 'localhost'
},
watch: {
alias: 'w',
description: 'Watch file(s)'
},
routes: {
alias: 'r',
description: 'Path to routes file'
},
middlewares: {
alias: 'm',
array: true,
description: 'Paths to middleware files'
},
static: {
alias: 's',
description: 'Set static files directory'
},
'read-only': {
alias: 'ro',
description: 'Allow only GET requests'
},
'no-cors': {
alias: 'nc',
description: 'Disable Cross-Origin Resource Sharing'
},
'no-gzip': {
alias: 'ng',
description: 'Disable GZIP Content-Encoding'
},
snapshots: {
alias: 'S',
description: 'Set snapshots directory',
default: '.'
},
delay: {
alias: 'd',
description: 'Add delay to responses (ms)'
},
id: {
alias: 'i',
description: 'Set database id property (e.g. _id)',
default: 'id'
},
foreignKeySuffix: {
alias: 'fks',
description: 'Set foreign key suffix (e.g. _id as in post_id)',
default: 'Id'
},
quiet: {
alias: 'q',
description: 'Suppress log messages from output'
},
config: {
alias: 'c',
description: 'Path to config file',
default: 'json-server.json'
}
}).boolean('watch').boolean('read-only').boolean('quiet').boolean('no-cors').boolean('no-gzip').help('help').alias('help', 'h').version(pkg.version).alias('version', 'v').example('$0 db.json', '').example('$0 file.js', '').example('$0 http://example.com/db.json', '').epilog('https://github.com/typicode/json-server').require(1, 'Missing <source> argument').argv;
run(argv);
};

233
node_modules/json-server/lib/cli/run.js generated vendored Normal file
View File

@@ -0,0 +1,233 @@
"use strict";
const fs = require('fs');
const path = require('path');
const jph = require('json-parse-helpfulerror');
const _ = require('lodash');
const chalk = require('chalk');
const enableDestroy = require('server-destroy');
const pause = require('connect-pause');
const is = require('./utils/is');
const load = require('./utils/load');
const jsonServer = require('../server');
function prettyPrint(argv, object, rules) {
const root = `http://${argv.host}:${argv.port}`;
console.log();
console.log(chalk.bold(' Resources'));
for (const prop in object) {
console.log(` ${root}/${prop}`);
}
if (rules) {
console.log();
console.log(chalk.bold(' Other routes'));
for (var rule in rules) {
console.log(` ${rule} -> ${rules[rule]}`);
}
}
console.log();
console.log(chalk.bold(' Home'));
console.log(` ${root}`);
console.log();
}
function createApp(db, routes, middlewares, argv) {
const app = jsonServer.create();
const {
foreignKeySuffix
} = argv;
const router = jsonServer.router(db, foreignKeySuffix ? {
foreignKeySuffix
} : undefined);
const defaultsOpts = {
logger: !argv.quiet,
readOnly: argv.readOnly,
noCors: argv.noCors,
noGzip: argv.noGzip,
bodyParser: true
};
if (argv.static) {
defaultsOpts.static = path.join(process.cwd(), argv.static);
}
const defaults = jsonServer.defaults(defaultsOpts);
app.use(defaults);
if (routes) {
const rewriter = jsonServer.rewriter(routes);
app.use(rewriter);
}
if (middlewares) {
app.use(middlewares);
}
if (argv.delay) {
app.use(pause(argv.delay));
}
router.db._.id = argv.id;
app.db = router.db;
app.use(router);
return app;
}
module.exports = function (argv) {
const source = argv._[0];
let app;
let server;
if (!fs.existsSync(argv.snapshots)) {
console.log(`Error: snapshots directory ${argv.snapshots} doesn't exist`);
process.exit(1);
} // noop log fn
if (argv.quiet) {
console.log = () => {};
}
console.log();
console.log(chalk.cyan(' \\{^_^}/ hi!'));
function start(cb) {
console.log();
console.log(chalk.gray(' Loading', source));
server = undefined; // create db and load object, JSON file, JS or HTTP database
return load(source).then(db => {
// Load additional routes
let routes;
if (argv.routes) {
console.log(chalk.gray(' Loading', argv.routes));
routes = JSON.parse(fs.readFileSync(argv.routes));
} // Load middlewares
let middlewares;
if (argv.middlewares) {
middlewares = argv.middlewares.map(function (m) {
console.log(chalk.gray(' Loading', m));
return require(path.resolve(m));
});
} // Done
console.log(chalk.gray(' Done')); // Create app and server
app = createApp(db, routes, middlewares, argv);
server = app.listen(argv.port, argv.host); // Enhance with a destroy function
enableDestroy(server); // Display server informations
prettyPrint(argv, db.getState(), routes); // Catch and handle any error occurring in the server process
process.on('uncaughtException', error => {
if (error.errno === 'EADDRINUSE') console.log(chalk.red(`Cannot bind to the port ${error.port}. Please specify another port number either through --port argument or through the json-server.json configuration file`));else console.log('Some error occurred', error);
process.exit(1);
});
});
} // Start server
start().then(() => {
// Snapshot
console.log(chalk.gray(' Type s + enter at any time to create a snapshot of the database')); // Support nohup
// https://github.com/typicode/json-server/issues/221
process.stdin.on('error', () => {
console.log(` Error, can't read from stdin`);
console.log(` Creating a snapshot from the CLI won't be possible`);
});
process.stdin.setEncoding('utf8');
process.stdin.on('data', chunk => {
if (chunk.trim().toLowerCase() === 's') {
const filename = `db-${Date.now()}.json`;
const file = path.join(argv.snapshots, filename);
const state = app.db.getState();
fs.writeFileSync(file, JSON.stringify(state, null, 2), 'utf-8');
console.log(` Saved snapshot to ${path.relative(process.cwd(), file)}\n`);
}
}); // Watch files
if (argv.watch) {
console.log(chalk.gray(' Watching...'));
console.log();
const source = argv._[0]; // Can't watch URL
if (is.URL(source)) throw new Error("Can't watch URL"); // Watch .js or .json file
// Since lowdb uses atomic writing, directory is watched instead of file
const watchedDir = path.dirname(source);
let readError = false;
fs.watch(watchedDir, (event, file) => {
// https://github.com/typicode/json-server/issues/420
// file can be null
if (file) {
const watchedFile = path.resolve(watchedDir, file);
if (watchedFile === path.resolve(source)) {
if (is.FILE(watchedFile)) {
let obj;
try {
obj = jph.parse(fs.readFileSync(watchedFile));
if (readError) {
console.log(chalk.green(` Read error has been fixed :)`));
readError = false;
}
} catch (e) {
readError = true;
console.log(chalk.red(` Error reading ${watchedFile}`));
console.error(e.message);
return;
} // Compare .json file content with in memory database
const isDatabaseDifferent = !_.isEqual(obj, app.db.getState());
if (isDatabaseDifferent) {
console.log(chalk.gray(` ${source} has changed, reloading...`));
server && server.destroy(() => start());
}
}
}
}
}); // Watch routes
if (argv.routes) {
const watchedDir = path.dirname(argv.routes);
fs.watch(watchedDir, (event, file) => {
if (file) {
const watchedFile = path.resolve(watchedDir, file);
if (watchedFile === path.resolve(argv.routes)) {
console.log(chalk.gray(` ${argv.routes} has changed, reloading...`));
server && server.destroy(() => start());
}
}
});
}
}
}).catch(err => {
console.log(err);
process.exit(1);
});
};

19
node_modules/json-server/lib/cli/utils/is.js generated vendored Normal file
View File

@@ -0,0 +1,19 @@
"use strict";
module.exports = {
FILE,
JS,
URL
};
function FILE(s) {
return !URL(s) && /\.json$/.test(s);
}
function JS(s) {
return !URL(s) && /\.js$/.test(s);
}
function URL(s) {
return /^(http|https):/.test(s);
}

74
node_modules/json-server/lib/cli/utils/load.js generated vendored Normal file
View File

@@ -0,0 +1,74 @@
"use strict";
const fs = require('fs');
const path = require('path');
const request = require('request');
const low = require('lowdb');
const FileAsync = require('lowdb/adapters/FileAsync');
const Memory = require('lowdb/adapters/Memory');
const is = require('./is');
const chalk = require('chalk');
const example = {
posts: [{
id: 1,
title: 'json-server',
author: 'typicode'
}],
comments: [{
id: 1,
body: 'some comment',
postId: 1
}],
profile: {
name: 'typicode'
}
};
module.exports = function (source) {
return new Promise((resolve, reject) => {
if (is.FILE(source)) {
if (!fs.existsSync(source)) {
console.log(chalk.yellow(` Oops, ${source} doesn't seem to exist`));
console.log(chalk.yellow(` Creating ${source} with some default data`));
console.log();
fs.writeFileSync(source, JSON.stringify(example, null, 2));
}
resolve(low(new FileAsync(source)));
} else if (is.URL(source)) {
// Load remote data
const opts = {
url: source,
json: true
};
request(opts, (err, response) => {
if (err) return reject(err);
resolve(low(new Memory()).setState(response.body));
});
} else if (is.JS(source)) {
// Clear cache
const filename = path.resolve(source);
delete require.cache[filename];
const dataFn = require(filename);
if (typeof dataFn !== 'function') {
throw new Error('The database is a JavaScript file but the export is not a function.');
} // Run dataFn to generate data
const data = dataFn();
resolve(low(new Memory()).setState(data));
} else {
throw new Error(`Unsupported source ${source}`);
}
});
};

77
node_modules/json-server/lib/front/index.js generated vendored Normal file
View File

@@ -0,0 +1,77 @@
"use strict";
require("promise-polyfill/src/polyfill");
require("whatwg-fetch");
require("./style.css");
function ResourceItem({
name,
length
}) {
return `
<li>
<a href="${name}">/${name}</a>
<sup>${length ? `${length}x` : 'object'}</sup>
</li>
`;
}
function ResourceList({
db
}) {
return `
<ul>
${Object.keys(db).map(name => ResourceItem({
name,
length: Array.isArray(db[name]) && db[name].length
})).join('')}
</ul>
`;
}
function NoResources() {
return `<p>No resources found</p>`;
}
function ResourcesBlock({
db
}) {
return `
<div>
<h1>Resources</h1>
${Object.keys(db).length ? ResourceList({
db
}) : NoResources()}
</div>
`;
}
window.fetch('db').then(response => response.json()).then(db => document.getElementById('resources').innerHTML = ResourcesBlock({
db
}));
function CustomRoutesBlock({
customRoutes
}) {
const rules = Object.keys(customRoutes);
if (rules.length) {
return `
<div>
<h1>Custom Routes</h1>
<table>
${rules.map(rule => `<tr>
<td>${rule}</td>
<td><code>⇢</code> ${customRoutes[rule]}</td>
</tr>`).join('')}
</table>
</div>
`;
}
}
window.fetch('__rules').then(response => response.json()).then(customRoutes => document.getElementById('custom-routes').innerHTML = CustomRoutesBlock({
customRoutes
}));

10
node_modules/json-server/lib/server/body-parser.js generated vendored Normal file
View File

@@ -0,0 +1,10 @@
"use strict";
const bodyParser = require('body-parser');
module.exports = [bodyParser.json({
limit: '10mb',
extended: false
}), bodyParser.urlencoded({
extended: false
})];

82
node_modules/json-server/lib/server/defaults.js generated vendored Normal file
View File

@@ -0,0 +1,82 @@
"use strict";
const fs = require('fs');
const path = require('path');
const express = require('express');
const logger = require('morgan');
const cors = require('cors');
const compression = require('compression');
const errorhandler = require('errorhandler');
const objectAssign = require('object-assign');
const bodyParser = require('./body-parser');
module.exports = function (opts) {
const userDir = path.join(process.cwd(), 'public');
const defaultDir = path.join(__dirname, '../../dist');
const staticDir = fs.existsSync(userDir) ? userDir : defaultDir;
opts = objectAssign({
logger: true,
static: staticDir
}, opts);
const arr = []; // Compress all requests
if (!opts.noGzip) {
arr.push(compression());
} // Enable CORS for all the requests, including static files
if (!opts.noCors) {
arr.push(cors({
origin: true,
credentials: true
}));
}
if (process.env.NODE_ENV === 'development') {
// only use in development
arr.push(errorhandler());
} // Serve static files
arr.push(express.static(opts.static)); // Logger
if (opts.logger) {
arr.push(logger('dev', {
skip: req => process.env.NODE_ENV === 'test' || req.path === '/favicon.ico'
}));
} // No cache for IE
// https://support.microsoft.com/en-us/kb/234067
arr.push((req, res, next) => {
res.header('Cache-Control', 'no-cache');
res.header('Pragma', 'no-cache');
res.header('Expires', '-1');
next();
}); // Read-only
if (opts.readOnly) {
arr.push((req, res, next) => {
if (req.method === 'GET') {
next(); // Continue
} else {
res.sendStatus(403); // Forbidden
}
});
} // Add middlewares
if (opts.bodyParser) {
arr.push(bodyParser);
}
return arr;
};

11
node_modules/json-server/lib/server/index.js generated vendored Normal file
View File

@@ -0,0 +1,11 @@
"use strict";
const express = require('express');
module.exports = {
create: () => express().set('json spaces', 2),
defaults: require('./defaults'),
router: require('./router'),
rewriter: require('./rewriter'),
bodyParser: require('./body-parser')
};

84
node_modules/json-server/lib/server/mixins.js generated vendored Normal file
View File

@@ -0,0 +1,84 @@
"use strict";
const nanoid = require('nanoid');
const pluralize = require('pluralize');
module.exports = {
getRemovable,
createId,
deepQuery // Returns document ids that have unsatisfied relations
// Example: a comment that references a post that doesn't exist
};
function getRemovable(db, opts) {
const _ = this;
const removable = [];
_.each(db, (coll, collName) => {
_.each(coll, doc => {
_.each(doc, (value, key) => {
if (new RegExp(`${opts.foreignKeySuffix}$`).test(key)) {
// Remove foreign key suffix and pluralize it
// Example postId -> posts
const refName = pluralize.plural(key.replace(new RegExp(`${opts.foreignKeySuffix}$`), '')); // Test if table exists
if (db[refName]) {
// Test if references is defined in table
const ref = _.getById(db[refName], value);
if (_.isUndefined(ref)) {
removable.push({
name: collName,
id: doc.id
});
}
}
}
});
});
});
return removable;
} // Return incremented id or uuid
// Used to override lodash-id's createId with utils.createId
function createId(coll) {
const _ = this;
const idProperty = _.__id();
if (_.isEmpty(coll)) {
return 1;
} else {
let id = _(coll).maxBy(idProperty)[idProperty]; // Increment integer id or generate string id
return _.isFinite(id) ? ++id : nanoid(7);
}
}
function deepQuery(value, q) {
const _ = this;
if (value && q) {
if (_.isArray(value)) {
for (let i = 0; i < value.length; i++) {
if (_.deepQuery(value[i], q)) {
return true;
}
}
} else if (_.isObject(value) && !_.isArray(value)) {
for (const k in value) {
if (_.deepQuery(value[k], q)) {
return true;
}
}
} else if (value.toString().toLowerCase().indexOf(q) !== -1) {
return true;
}
}
}

16
node_modules/json-server/lib/server/rewriter.js generated vendored Normal file
View File

@@ -0,0 +1,16 @@
"use strict";
const express = require('express');
const rewrite = require('express-urlrewrite');
module.exports = routes => {
const router = express.Router();
router.get('/__rules', (req, res) => {
res.json(routes);
});
Object.keys(routes).forEach(key => {
router.use(rewrite(key, routes[key]));
});
return router;
};

12
node_modules/json-server/lib/server/router/delay.js generated vendored Normal file
View File

@@ -0,0 +1,12 @@
"use strict";
const pause = require('connect-pause');
module.exports = function delay(req, res, next) {
// NOTE: for some reason unknown to me, if the default is 0, the tests seems to add 2 seconds
// NOTE: to each test, a default value of 1 does not seem to be effected by that issue
const _delay = !isNaN(parseFloat(req.query._delay)) ? parseFloat(req.query._delay) : 1;
delete req.query._delay;
pause(_delay)(req, res, next);
};

View File

@@ -0,0 +1,11 @@
"use strict";
const url = require('url');
module.exports = function getFullURL(req) {
const root = url.format({
protocol: req.protocol,
host: req.get('host')
});
return `${root}${req.originalUrl}`;
};

96
node_modules/json-server/lib/server/router/index.js generated vendored Normal file
View File

@@ -0,0 +1,96 @@
"use strict";
const express = require('express');
const methodOverride = require('method-override');
const _ = require('lodash');
const lodashId = require('lodash-id');
const low = require('lowdb');
const Memory = require('lowdb/adapters/Memory');
const FileSync = require('lowdb/adapters/FileSync');
const bodyParser = require('../body-parser');
const validateData = require('./validate-data');
const plural = require('./plural');
const nested = require('./nested');
const singular = require('./singular');
const mixins = require('../mixins');
module.exports = (db, opts = {
foreignKeySuffix: 'Id',
_isFake: false
}) => {
if (typeof db === 'string') {
db = low(new FileSync(db));
} else if (!_.has(db, '__chain__') || !_.has(db, '__wrapped__')) {
db = low(new Memory()).setState(db);
} // Create router
const router = express.Router(); // Add middlewares
router.use(methodOverride());
router.use(bodyParser);
validateData(db.getState()); // Add lodash-id methods to db
db._.mixin(lodashId); // Add specific mixins
db._.mixin(mixins); // Expose database
router.db = db; // Expose render
router.render = (req, res) => {
res.jsonp(res.locals.data);
}; // GET /db
router.get('/db', (req, res) => {
res.jsonp(db.getState());
}); // Handle /:parent/:parentId/:resource
router.use(nested(opts)); // Create routes
db.forEach((value, key) => {
if (_.isPlainObject(value)) {
router.use(`/${key}`, singular(db, key, opts));
return;
}
if (_.isArray(value)) {
router.use(`/${key}`, plural(db, key, opts));
return;
}
var sourceMessage = ''; // if (!_.isObject(source)) {
// sourceMessage = `in ${source}`
// }
const msg = `Type of "${key}" (${typeof value}) ${sourceMessage} is not supported. ` + `Use objects or arrays of objects.`;
throw new Error(msg);
}).value();
router.use((req, res) => {
if (!res.locals.data) {
res.status(404);
res.locals.data = {};
}
router.render(req, res);
});
router.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send(err.stack);
});
return router;
};

29
node_modules/json-server/lib/server/router/nested.js generated vendored Normal file
View File

@@ -0,0 +1,29 @@
"use strict";
const express = require('express');
const pluralize = require('pluralize');
const delay = require('./delay');
module.exports = opts => {
const router = express.Router();
router.use(delay); // Rewrite URL (/:resource/:id/:nested -> /:nested) and request query
function get(req, res, next) {
const prop = pluralize.singular(req.params.resource);
req.query[`${prop}${opts.foreignKeySuffix}`] = req.params.id;
req.url = `/${req.params.nested}`;
next();
} // Rewrite URL (/:resource/:id/:nested -> /:nested) and request body
function post(req, res, next) {
const prop = pluralize.singular(req.params.resource);
req.body[`${prop}${opts.foreignKeySuffix}`] = req.params.id;
req.url = `/${req.params.nested}`;
next();
}
return router.get('/:resource/:id/:nested', get).post('/:resource/:id/:nested', post);
};

303
node_modules/json-server/lib/server/router/plural.js generated vendored Normal file
View File

@@ -0,0 +1,303 @@
"use strict";
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
const express = require('express');
const _ = require('lodash');
const pluralize = require('pluralize');
const write = require('./write');
const getFullURL = require('./get-full-url');
const utils = require('../utils');
const delay = require('./delay');
module.exports = (db, name, opts) => {
// Create router
const router = express.Router();
router.use(delay); // Embed function used in GET /name and GET /name/id
function embed(resource, e) {
e && [].concat(e).forEach(externalResource => {
if (db.get(externalResource).value) {
const query = {};
const singularResource = pluralize.singular(name);
query[`${singularResource}${opts.foreignKeySuffix}`] = 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(innerResource => {
const plural = pluralize(innerResource);
if (db.get(plural).value()) {
const prop = `${innerResource}${opts.foreignKeySuffix}`;
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
let chain = db.get(name); // Remove q, _start, _end, ... from req.query to avoid filtering using those
// parameters
let q = req.query.q;
let _start = req.query._start;
let _end = req.query._end;
let _page = req.query._page;
const _sort = req.query._sort;
const _order = req.query._order;
let _limit = req.query._limit;
const _embed = req.query._embed;
const _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(query => {
const arr = db.get(name).value();
for (const i in arr) {
if (_.has(arr[i], query) || query === 'callback' || query === '_' || /_lte$/.test(query) || /_gte$/.test(query) || /_ne$/.test(query) || /_like$/.test(query)) return;
}
delete req.query[query];
});
if (q) {
// Full-text search
if (Array.isArray(q)) {
q = q[0];
}
q = q.toLowerCase();
chain = chain.filter(obj => {
for (const key in obj) {
const value = obj[key];
if (db._.deepQuery(value, q)) {
return true;
}
}
});
}
Object.keys(req.query).forEach(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
const arr = [].concat(req.query[key]);
const isDifferent = /_ne$/.test(key);
const isRange = /_lte$/.test(key) || /_gte$/.test(key);
const isLike = /_like$/.test(key);
const path = key.replace(/(_lte|_gte|_ne|_like)$/, '');
chain = chain.filter(element => {
return arr.map(function (value) {
// get item value based on path
// i.e post.title -> 'foo'
const elementValue = _.get(element, path); // Prevent toString() failing on undefined or null values
if (elementValue === undefined || elementValue === null) {
return;
}
if (isRange) {
const isLowerThan = /_gte$/.test(key);
return isLowerThan ? value <= elementValue : value >= elementValue;
} else if (isDifferent) {
return value !== elementValue.toString();
} else if (isLike) {
return new RegExp(value, 'i').test(elementValue.toString());
} else {
return value === elementValue.toString();
}
}).reduce((a, b) => isDifferent ? a && b : a || b);
});
}
}); // Sort
if (_sort) {
const _sortSet = _sort.split(',');
const _orderSet = (_order || '').split(',').map(s => s.toLowerCase());
chain = chain.orderBy(_sortSet, _orderSet);
} // Slice result
if (_end || _limit || _page) {
res.setHeader('X-Total-Count', chain.size());
res.setHeader('Access-Control-Expose-Headers', `X-Total-Count${_page ? ', Link' : ''}`);
}
if (_page) {
_page = parseInt(_page, 10);
_page = _page >= 1 ? _page : 1;
_limit = parseInt(_limit, 10) || 10;
const page = utils.getPage(chain.value(), _page, _limit);
const links = {};
const fullURL = getFullURL(req);
if (page.first) {
links.first = fullURL.replace(`page=${page.current}`, `page=${page.first}`);
}
if (page.prev) {
links.prev = fullURL.replace(`page=${page.current}`, `page=${page.prev}`);
}
if (page.next) {
links.next = fullURL.replace(`page=${page.current}`, `page=${page.next}`);
}
if (page.last) {
links.last = fullURL.replace(`page=${page.current}`, `page=${page.last}`);
}
res.links(links);
chain = _.chain(page.items);
} else if (_end) {
_start = parseInt(_start, 10) || 0;
_end = parseInt(_end, 10);
chain = chain.slice(_start, _end);
} else if (_limit) {
_start = parseInt(_start, 10) || 0;
_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) {
const _embed = req.query._embed;
const _expand = req.query._expand;
const resource = db.get(name).getById(req.params.id).value();
if (resource) {
// Clone resource to avoid making changes to the underlying object
const clone = _.cloneDeep(resource); // Embed other resources based on resource id
// /posts/1?_embed=comments
embed(clone, _embed); // Expand inner resources based on id
// /posts/1?_expand=user
expand(clone, _expand);
res.locals.data = clone;
}
next();
} // POST /name
function create(req, res, next) {
let resource;
if (opts._isFake) {
const id = db.get(name).createId().value();
resource = _objectSpread({}, req.body, {
id
});
} else {
resource = db.get(name).insert(req.body).value();
}
res.setHeader('Access-Control-Expose-Headers', 'Location');
res.location(`${getFullURL(req)}/${resource.id}`);
res.status(201);
res.locals.data = resource;
next();
} // PUT /name/:id
// PATCH /name/:id
function update(req, res, next) {
const id = req.params.id;
let resource;
if (opts._isFake) {
resource = db.get(name).getById(id).value();
if (req.method === 'PATCH') {
resource = _objectSpread({}, resource, {}, req.body);
} else {
resource = _objectSpread({}, req.body, {
id: resource.id
});
}
} else {
let chain = db.get(name);
chain = req.method === 'PATCH' ? chain.updateById(id, req.body) : chain.replaceById(id, req.body);
resource = chain.value();
}
if (resource) {
res.locals.data = resource;
}
next();
} // DELETE /name/:id
function destroy(req, res, next) {
let resource;
if (opts._isFake) {
resource = db.get(name).value();
} else {
resource = db.get(name).removeById(req.params.id).value(); // Remove dependents documents
const removable = db._.getRemovable(db.getState(), opts);
removable.forEach(item => {
db.get(item.name).removeById(item.id).value();
});
}
if (resource) {
res.locals.data = {};
}
next();
}
const w = write(db);
router.route('/').get(list).post(create, w);
router.route('/:id').get(show).put(update, w).patch(update, w).delete(destroy, w);
return router;
};

64
node_modules/json-server/lib/server/router/singular.js generated vendored Normal file
View File

@@ -0,0 +1,64 @@
"use strict";
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
const express = require('express');
const write = require('./write');
const getFullURL = require('./get-full-url');
const delay = require('./delay');
module.exports = (db, name, opts) => {
const router = express.Router();
router.use(delay);
function show(req, res, next) {
res.locals.data = db.get(name).value();
next();
}
function create(req, res, next) {
if (opts._isFake) {
res.locals.data = req.body;
} else {
db.set(name, req.body).value();
res.locals.data = db.get(name).value();
}
res.setHeader('Access-Control-Expose-Headers', 'Location');
res.location(`${getFullURL(req)}`);
res.status(201);
next();
}
function update(req, res, next) {
if (opts._isFake) {
if (req.method === 'PUT') {
res.locals.data = req.body;
} else {
const resource = db.get(name).value();
res.locals.data = _objectSpread({}, resource, {}, req.body);
}
} else {
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();
}
const w = write(db);
router.route('/').get(show).post(create, w).put(update, w).patch(update, w);
return router;
};

View File

@@ -0,0 +1,18 @@
"use strict";
const _ = require('lodash');
function validateKey(key) {
if (key.indexOf('/') !== -1) {
const msg = [`Oops, found / character in database property '${key}'.`, '', "/ aren't supported, if you want to tweak default routes, see", 'https://github.com/typicode/json-server/#add-custom-routes'].join('\n');
throw new Error(msg);
}
}
module.exports = obj => {
if (_.isPlainObject(obj)) {
Object.keys(obj).forEach(validateKey);
} else {
throw new Error(`Data must be an object. Found ${typeof obj}.` + 'See https://github.com/typicode/json-server for example.');
}
};

8
node_modules/json-server/lib/server/router/write.js generated vendored Normal file
View File

@@ -0,0 +1,8 @@
"use strict";
module.exports = function write(db) {
return (req, res, next) => {
db.write();
next();
};
};

32
node_modules/json-server/lib/server/utils.js generated vendored Normal file
View File

@@ -0,0 +1,32 @@
"use strict";
module.exports = {
getPage
};
function getPage(array, page, perPage) {
var obj = {};
var start = (page - 1) * perPage;
var end = page * perPage;
obj.items = array.slice(start, end);
if (obj.items.length === 0) {
return obj;
}
if (page > 1) {
obj.prev = page - 1;
}
if (end < array.length) {
obj.next = page + 1;
}
if (obj.items.length !== array.length) {
obj.current = page;
obj.first = 1;
obj.last = Math.ceil(array.length / perPage);
}
return obj;
}

152
node_modules/json-server/package.json generated vendored Normal file
View File

@@ -0,0 +1,152 @@
{
"_from": "json-server",
"_id": "json-server@0.15.1",
"_inBundle": false,
"_integrity": "sha512-6Vc6tC1uLasnMd6Ksnq+4gSQcRqLuSJ/yLoIG4fr4P8f5dAR1gbCqgaVRlk8jfRune0NXcrfDrz7liwAD2WEeQ==",
"_location": "/json-server",
"_phantomChildren": {},
"_requested": {
"type": "tag",
"registry": true,
"raw": "json-server",
"name": "json-server",
"escapedName": "json-server",
"rawSpec": "",
"saveSpec": null,
"fetchSpec": "latest"
},
"_requiredBy": [
"#USER",
"/"
],
"_resolved": "https://registry.npmjs.org/json-server/-/json-server-0.15.1.tgz",
"_shasum": "5d77575f1e15db20a361e0ed0a64bfbc3df11171",
"_spec": "json-server",
"_where": "/Users/nate/code/pluralsight/async-programming-promises",
"author": {
"name": "Typicode",
"email": "typicode@gmail.com"
},
"bin": {
"json-server": "./lib/cli/bin.js"
},
"bugs": {
"url": "https://github.com/typicode/json-server/issues"
},
"bundleDependencies": false,
"dependencies": {
"body-parser": "^1.19.0",
"chalk": "^2.4.2",
"compression": "^1.7.4",
"connect-pause": "^0.1.1",
"cors": "^2.8.5",
"errorhandler": "^1.5.1",
"express": "^4.17.1",
"express-urlrewrite": "^1.2.0",
"json-parse-helpfulerror": "^1.0.3",
"lodash": "^4.17.15",
"lodash-id": "^0.14.0",
"lowdb": "^1.0.0",
"method-override": "^3.0.0",
"morgan": "^1.9.1",
"nanoid": "^2.1.0",
"object-assign": "^4.1.1",
"please-upgrade-node": "^3.2.0",
"pluralize": "^8.0.0",
"request": "^2.88.0",
"server-destroy": "^1.0.1",
"update-notifier": "^3.0.1",
"yargs": "^14.0.0"
},
"deprecated": false,
"description": "Serves JSON files through REST routes.",
"devDependencies": {
"@babel/cli": "^7.5.5",
"@babel/core": "^7.5.5",
"@babel/node": "^7.5.5",
"@babel/plugin-transform-regenerator": "^7.4.5",
"@babel/polyfill": "^7.4.4",
"@babel/preset-env": "^7.5.5",
"@babel/register": "^7.5.5",
"babel-loader": "^8.0.6",
"clean-webpack-plugin": "^3.0.0",
"cross-env": "^5.2.1",
"css-loader": "^3.2.0",
"eslint": "^6.3.0",
"eslint-config-prettier": "^6.1.0",
"eslint-config-standard": "^14.1.0",
"eslint-plugin-import": "^2.18.2",
"eslint-plugin-node": "^9.2.0",
"eslint-plugin-prettier": "^3.1.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.1",
"html-webpack-plugin": "^3.2.0",
"husky": "^3.0.5",
"jest": "^24.9.0",
"markdown-toc": "^1.2.0",
"mini-css-extract-plugin": "^0.8.0",
"mkdirp": "^0.5.1",
"npm-run-all": "^4.1.5",
"os-tmpdir": "^2.0.0",
"pkg-ok": "^2.3.1",
"prettier": "^1.18.2",
"promise-polyfill": "^8.1.3",
"rimraf": "^3.0.0",
"server-ready": "^0.3.1",
"supertest": "^4.0.2",
"temp-write": "^4.0.0",
"webpack": "^4.39.3",
"webpack-cli": "^3.3.7",
"whatwg-fetch": "^3.0.0"
},
"directories": {
"test": "test"
},
"engines": {
"node": ">=8"
},
"homepage": "https://github.com/typicode/json-server",
"husky": {
"hooks": {
"pre-commit": "npm test"
}
},
"jest": {
"testURL": "http://localhost/"
},
"keywords": [
"JSON",
"server",
"fake",
"REST",
"API",
"prototyping",
"mock",
"mocking",
"test",
"testing",
"rest",
"data",
"dummy",
"sandbox"
],
"license": "MIT",
"main": "./lib/server/index.js",
"name": "json-server",
"repository": {
"type": "git",
"url": "git://github.com/typicode/json-server.git"
},
"scripts": {
"build": "babel src -d lib && webpack -p",
"fix": "npm run lint -- --fix",
"lint": "eslint . --ignore-path .gitignore",
"prepublishOnly": "npm test && npm run build && pkg-ok",
"start": "run-p start:**",
"start:babel-node": "babel-node src/cli/bin db.json -r routes.json",
"start:webpack": "webpack -d --watch",
"test": "npm run build && cross-env NODE_ENV=test jest && npm run lint",
"toc": "markdown-toc -i README.md"
},
"version": "0.15.1"
}

10
node_modules/json-server/postinstall.js generated vendored Normal file
View File

@@ -0,0 +1,10 @@
const chalk = require('chalk')
console.log(
'Like JSON Server? You can support the project on',
chalk.bold('GitHub Sponsors')
)
console.log(
chalk.underline('https://github.com/users/typicode/sponsorship'),
chalk.red('❤')
)

5
node_modules/json-server/routes.json generated vendored Normal file
View File

@@ -0,0 +1,5 @@
{
"/api/": "/",
"/blog/:resource/:id/show": "/:resource/:id",
"/blog/:category": "/posts?category=:category"
}

21
node_modules/json-server/webpack.config.js generated vendored Normal file
View File

@@ -0,0 +1,21 @@
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
entry: './src/front/index.js',
module: {
rules: [
{ test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader'] },
{ test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader' }
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
favicon: 'src/front/favicon.ico',
template: 'src/front/index.html'
}),
new MiniCssExtractPlugin()
]
}