在开发过程中,接口多半是滞后于页面开发的。利用JSON Server快速搭建模拟返回REST风格的后台数据,保证前后端开发的分离。前后端开发只要设定好接口以及数据的定义,剩下的就可以各自开发,最后集成测试。

JSON Server 作为工具,基于Express开发,而且它足够简单,写少量数据,即可使用,支持CORS和JSONP跨域请求,支持GET, POST, PUT, PATCH 和 DELETE 方法,更提供了一系列的查询方法,如limit,order等

​​

安装

npm install json-server -g

全局安装,可以在命令行下独立执行。安装完成后可以用 ​​json-server -h​​ 命令检查是否安装成功。

json-server [options] <source>

Options:
--config, -c 指定 config 文件 [默认: "json-server.json"]
--port, -p 设置端口号 [default: 3000]
--host, -H 设置主机 [默认: "0.0.0.0"]
--watch, -w 监控文件 [boolean]
--routes, -r 指定路由文件
--static, -s 设置静态文件
--read-only, --ro 只允许 GET 请求 [boolean]
--no-cors, --nc 禁止跨域资源共享 [boolean]
--no-gzip, --ng 禁止GZIP [boolean]
--snapshots, -S 设置快照目录 [默认: "."]
--delay, -d 设置反馈延时 (ms)
--id, -i 设置数据的id属性 (e.g. _id) [默认: "id"]
--quiet, -q 不输出日志信息 [boolean]
--help, -h 显示帮助信息 [boolean]
--version, -v 显示版本号 [boolean]

启动

创建一个目录server,在该目录下创建一个json文件,db.json.

{
"list": [
{
"id": 1,
"name": "张三",
"tel": "15223810923"
},
{
"id": 2,
"name": "李四",
"tel": "15223810923"
},
{
"id": 3,
"name": "王二",
"tel": "15223810923"
},
{
"id": 4,
"name": "陈五",
"tel": "15223810923"
},
{
"name": "赵六",
"tel": "123454323",
"id": 5
},
{
"name": "赵六",
"tel": "123454323",
"id": 6
},
{
"name": "赵六",
"tel": "123454323",
"id": 7
}
],
"users": [
{
"id": 1,
"name": "陈五",
"sex":"male",
"tel": "12345678",
"auther":{
"name":"陈五",
"age":"25"
}
},
{
"id": 2,
"name": "王二",
"sex":"male",
"tel": "15223810923",
"auther":{
"name":"王二",
"age":"22"
}
}
],
"user": {
"id": 1,
"name": "陈五",
"tel": "15223810923"
}
,
"posts": [
{ "id": 1, "title": "json-server", "author": "typicode" }
],
"comments": [
{ "id": 1, "body": "some comment", "postId": 1 }
],
"profile": { "name": "typicode" }
}

在server目录下执行

json-server db.json -p 3003

打开浏览器,​​http://localhost:3003​​​,查看页面。如果要监控json文件的变化,启动的时候加上参数​​--watch​​。

支持的方法

以API​​http://localhost:3003/list​​ 为例


  • GET /list 获取列表
  • GET /list/1 获取id=1的数据
  • POST /list 创建一个项目
  • PUT /list/1 更新一个id为1的数据
  • PATCH /list/1 部分更新id为1的数据
  • DELETE /list/1 删除id为1的数据

对于对象,例如​​http://localhost:3003/user​​,地址是相同的。


  • GET /user
  • POST /user
  • PUT /user
  • PATCH /user


  • 当你发送POST,PUT,PATCH 或者 DELETE请求时,变化会自动安全的保存到你的db.json文件中。
  • 你的请求体body应该是封闭对象。比如​​{"name": "Foobar"}​
  • id不是必须的,在PUT或者PATCH方法中,任何的id值将会被忽略。
  • 在POST请求中,id是可以被添加的,如果该值没有没占用,会使用该值,否则自动生成。
  • POST,PUT或者PATCH请求应该包含一个​​Content-Type:application/json​​的header,来确保在请求body中使用json。

高级查找

Filter

用​​.​​来访问深层属性,比如

GET /users?set=male&tel=12345678
GET /list?id=1&id=2
GET /users?author.age=25

Paginate

使用 ​​_page​​ 和可选的 ​​_limit​​来对返回数据定制(不设置默认返回10条)。

在返回的header中,有一个属性​​Link​​,里面会有first, prev, next and last links。

header中还有一个属性​​X-Total-Count​​,用来存储满足条件的数据的条数。

GET /list?_page=1
GET /list?_page=2&_limit=2

Sort

使用 ​​_sort​​​ 和 ​​_order​​ (默认是ascending)

GET /list?_sort=id&_order=asc

对于多字段的排序,可以参考下面的格式:

GET /list?_sort=id,name&_order=desc,asc

Slice

使用​​_start​​​和​​_end​​​或​​_limit​​(一个 X-Total-Count 自定义Header在Response里面),就像数据的slice的方法一样。

GET /list?_start=2&_end=5
GET /list?_start=2&_limit=5

Operators

用 ​​_gte​​​或​​_lte​​来得到一个范围。

GET /list?id_gte=2&id_lte=5

用​​_ne​​来不包含(exclude)一个值

GET /posts?id_ne=1
GET /list?id_gte=2&id_lte=5&id_ne=4

用 _like 来 filter (RegExp 支持)

GET /list?name_like=王

search

使用q

GET /list?q=1

Relationships

关联子资源, 添加​​_embed​

GET /posts?_embed=comments
GET /posts/1?_embed=comments

包含父资源, 添加​​_expand​

GET /comments?_expand=post
GET /comments/1?_expand=post

要获得或创建nested resources(默认一层,多层的话,自定义routes)

GET  /posts/1/comments
POST /posts/1/comments

Database

GET /db

Homepage

返回默认的index文件,或者./public目录

GET /

拓展功能

静态文件服务器

你也可以用json server来托管你的静态HTML,JS和CSS文件。仅仅需要简单的创建一个​​./public​​​目录。或者用​​--static​​来指定一个不同的静态文件路径。

mkdir public
echo 'hello world' > public/index.html
json-server db.json
json-server db.json --static ./some-other-dir

改变端口号

你可以改变端口号使用​​--port​

$ json-server --watch db.json --port 3004

从任何地方访问

你可以使用CORS和JSONP从任何地方访问你的API。

远程文件

你可以加载远程文件

$ json-server http://example.com/file.json
$ json-server http://jsonplaceholder.typicode.com/db

生成随机数据

使用js文件替代json文件,可以动态生成数据。还可以借助其他模块生成,比如Faker, Casual, Chance or JSON Schema Faker。

// 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
}
$ json-server index.js

HTTPS

有很多方式在开发中使用SSL。一个简单的方式就是使用​​hotel​​.

自定义路由

创建routes.json文件。注意每个路由文件应该以​​/​​开头。

{
"/api/*": "/$1",
"/:resource/:id/show": "/:resource/:id",
"/posts/:category": "/posts?category=:category",
"/articles\\?id=:id": "/posts/:id"
}

启动json server时加上​​--routes​​选项。

json-server db.json --routes routes.json

现在你可以用附加路由访问资源了。

/api/posts # → /posts
/api/posts/1 # → /posts/1
/posts/1/show # → /posts/1
/posts/javascript # → /posts?category=javascript
/articles?id=1 # → /posts/1

增加中间件

命令行中,你可以使用​​--middlewares​​选项。

// hello.js
module.exports = (req, res, next) => {
res.header('X-Hello', 'World')
next()
}
json-server db.json --middlewares ./hello.js
json-server db.json --middlewares ./first.js ./second.js

CLI usage

你可以将设置放在​​json-server.json​​配置文件里。

将json server作为一个模块

在项目中,如何你需要增加认证,验证或者其他功能,你可以将json server作为一个模块,合并其他的Express中间件。

一个简单的例子

// 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(3003, () => {
console.log('JSON Server is running')
})
$ node server.js

你提供给​​jsonServer.router​​方法的路径,是相对于你的node的。

如果你从另外一个路径运行上面的代码,最好用绝对路径。

const path = require('path')
const router = jsonServer.router(path.join(__dirname, 'db.json'))

对于内存数据库,传递一个对象给​​jsonServer.router()​​​。注意,​​jsonServer.router()​​可以被用在一个已经存在的Express项目中。

自定义路由动态数据的例子

//db.js

let Mock = require('mockjs');
let Random = Mock.Random;

module.exports = function() {
var data = {
news: [],
type:{
"a":"a",
"b":"b",
}
};

var images = [1,2,3].map(x=>Random.image('200x100', Random.color(), Random.word(2,6)));

for (var i = 0; i < 10; i++) {

var content = Random.cparagraph(0,10);

data.news.push({
id: i,
title: Random.cword(8,20),
desc: content.substr(0,40),
tag: Random.cword(2,6),
views: Random.integer(100,5000),
images: images.slice(0,Random.integer(1,3))
})
}

return data
}
//server.js
const path = require('path');
const config = require('./config');
const jsonServer = require('json-server');
const rules = require('./routes');
const dbfile = require(config.DB_FILE);

const ip = config.SERVER;
const port = config.PORT;
const db_file = config.DB_FILE;

const server = jsonServer.create();
const router = jsonServer.router(dbfile());
const middlewares = jsonServer.defaults();


server.use(jsonServer.bodyParser);

// Set default middlewares (logger, static, cors and no-cache)
server.use(middlewares);


server.use((req, res, next) => {
res.header('X-Hello', 'World');
next();
})


router.render = (req, res) => {
res.jsonp({
body: res.locals.data,
code: 0
})
}

server.use("/api",router);

server.use(jsonServer.rewriter(rules));

server.use(router);

server.listen({
host: ip,
port: port,
}, function() {
console.log(JSON.stringify(jsonServer));
console.log(`JSON Server is running in http://${ip}:${port}`);
});
//routes.js

module.exports= {
"/api/": "/",
"/:id": "/news/:id",
"/news/show/:id": "/news/:id",
"/topics/:id/show": "/news/:id"
}

添加一个路由输出query parameters

// Add custom routes before JSON Server router
server.get('/echo', (req, res) => {
res.jsonp(req.query)
})

添加一个时间戳


支持post请求需要使用​​bodyParser​


// 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()
})

添加控制

server.use((req, res, next) => {
if (isAuthorized(req)) { // add your authorization logic here
next() // continue to JSON Server router
} else {
res.sendStatus(401)
}
})

修改response数据结构

需要​override​ ​​router.render​​方法。

// In this example, returned resources will be wrapped in a body property
router.render = (req, res) => {
res.jsonp({
body: res.locals.data
})
}

自定义reponse状态码

// In this example we simulate a server side error response
router.render = (req, res) => {
res.status(500).jsonp({
error: "error message here"
})
}

Rewriter例子

添加rewrite rules,使用​​jsonServer.rewriter()​​:

// 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

更改挂载点

挂在​​/api​​上

server.use('/api', router)