- GitHub 上的项目(文章版本1.10)
本文介绍了用于CRUD(创建、读取、更新、删除)的模型驱动REST或GraphQL API。有了它,您可以编写简单的模型(指定数据库表和要公开的列集),CRUD的REST端点将自动可用。不需要任何SQL的手动编码。
这个概念可以在不同的技术栈和语言上实现。在这里,我将JavaScript(生成SQL)与Node.js、Express和PostgreSQL结合使用。
背景大多数项目需要Create、Read、Update和Delete对象。当这些对象足够简单时(一个驱动表和数据库中的几列),代码从一个对象到另一个对象非常相似。实际上,模式是相同的,唯一的区别是表的名称和列的名称和类型。
当然,总会有复杂的端点需要手工编写,但通过自动化简单的端点,我们可以节省大量时间。
示例数据库为待办事项列表、地址簿和图画小说库存提供了三个示例。这些示例使用模型中指定的对象ID“todo”、“contact”和“comics”。
使用它们之后,更改数据库(在config.js中),更改模型(在模型目录中),RESTful API将根据您的数据结构进行更改。
按照以下步骤使用示例数据库安装和设置项目。
安装从GitHub下载或克隆。
# To get the latest stable version, use git from the command line.
git clone https://github.com/evoluteur/evolutility-server-node
或使用npm包:
# To get the latest stable version, use npm from the command line.
npm install evolutility-server-node
安装Evolutility-Server-Node后,请执行以下步骤:
- 创建一个PostgreSQL数据库。
- 在文件config.js中,设置PostgreSQL连接字符串和模式名称以访问您的新数据库。
- 在命令行中,键入以下内容:
# Install dependencies
npm install
# Create sample database w/ demo tables
node js/setup/database.js
# Run the node.js server
npm start
在Web浏览器中,转到URL http://localhost:3000/api/v1/evolutility/todo。
配置在根目录下,编辑文件“config.js”来设置数据库连接和其他选项,如分页和上传目录。
Models要由REST API访问,必须在模型中描述每个数据库表。模型包含驱动表的名称和API中存在的字段/列的列表。
实体财产
意义
id
标识实体的唯一键(用作API参数)
table
数据库表名
fields
字段数组
titleField
记录标题的字段ID
searchFields
用于执行搜索的字段的字段ID数组
字段财产
意义
id
字段的唯一键(可以与列相同,但不一定)
column
字段的数据库列名称
lovtable
字段值要加入的表(仅适用于“lov”类型的字段)
lovcolumn
字段值的列名(在lovtable中)(仅适用于“lov”类型的字段)
type
字段类型不是数据库列类型,而是UI字段类型。可能的字段类型:
- boolean(是/否)
- date
- datetime
- decimal
- document
- image
- integer
- lov(值列表)
- money
- text
- textmultiline
- time
- url
readonly
防止字段修改
inMany
确定字段是否存在于记录列表中(默认情况下)
注意:更多字段属性(unique, min, max, minLength, maxLength...)将在稍后添加。
示例模型这是一个待办事项应用程序的模型。
module.exports = {
id: "todo",
table: "task",
titleField: "title",
searchFields: ["title", "duedate", "description"],
fields: [
{
id: "title",
column: "title",
type: "text",
inMany: true
},
{
id: "duedate",
column: "duedate",
type: "date",
inMany: true
},
{
id: "category",
column: "category_id",
type: "lov",
lovtable: "task_category",
inMany: true
},
{
id: "priority",
column: "priority_id",
type: "lov",
lovtable: "task_priority",
required: true,
inMany: true
{
id: "complete",
column: "complete",
type: "boolean",
inMany: true
},
{
id: "description",
column: "description",
type: "textmultiline"
}
]
};
此模型仅涵盖后端。也可以对前端进行建模(只要UX模式足够简单或者变得复杂到不值得)。
REST APIEvolutility-Server-Node的API受到PostgREST的极大启发(甚至部分复制),PostgREST也提供通用CRUD,但直接检查数据库模式而不是使用模型。
在本地运行项目时,“todo”应用程序的URL是http://localhost:3000/api/v1/evolutility/todo。
请求的信息得到一个
要通过ID获取特定记录,请使用“< ObjectName >/ID”。
GET //
GET /todo/12
获得许多
每个模型都暴露出来。您可以使用模型ID查询项目列表。
GET /
GET /todo
过滤
您可以通过在字段上添加条件来过滤结果行,每个条件都是一个查询字符串参数。
GET //=.
GET /todo?title=sw.a
GET /todo?priority=in.1,2,3
添加多个参数结合条件:
todo?complete=0&duedate=lt.2017-01-01
这些运算符可用的:
操作符
意义
例子
eq
等于
/todo?category=eq.1
gt
比...更大
/todo?duedate=gt.2017-01-15
lt
少于
/todo?duedate=lt.2017-01-15
gte
大于或等于
/todo?duedate=gte.2017-01-15
lte
小于或等于
/todo?duedate=lte.2017-01-15
ct
包含
/todo?title=ct.e
sw
从...开始
/todo?title=sw.a
fw
以…结束
/todo?title=fw.z
in
值列表之一
/todo?priority=in.1,2,3
0
是假的
/todo?complete=0
1
是真的
/todo?complete=1
null
是null
/todo?category=null
nn
不是null
/todo?category==nn
排序
保留字“order”对响应行重新排序。它使用逗号分隔的字段和方向列表:
GET /?order=.
GET /todo?order=priority.desc,title.asc
如果没有指定方向,则默认为升序:
GET /todo?order=duedate
限制和分页
保留字“page”和“pageSize”限制了响应行。
GET /?page=&pageSize=
GET /todo?page=0&pageSize=50
格式化
默认情况下,所有API都以JSON格式返回数据。此API调用允许以CSV格式请求数据(导出到Excel)。此功能使用express-csv。
GET /?format=csv
GET /todo?format=csv
注意:在返回的数据中,每个对象都有一个额外的属性“_full_count”,表示查询中的记录总数(限制之前)。
更新数据记录创建
要在数据库表中创建一行,请发布一个JSON对象,其键是您要创建的列的名称。缺少的键将在适用时设置为默认值。
POST /todo
{ title: 'Finish testing', priority: 2}
更新
PATCH /todo
{ title: 'Finish testing', priority: 2}
删除
只需使用带有记录id的动词DELETE即可删除。
DELETE //
DELETE /todo/5
除了CRUD,Evolutility-Server-Node还为常见的UI需求提供端点,例如图表和值列表。
发现
返回对象及其API的列表(仅包括标记为活动的对象)。
GET /
注意:必须在配置中使用{apiInfo: true}启用此端点。
图表
对于图表数据,可以获得聚合数据。
GET //chart/
GET /todo/chart/category
值列表
UI中的下拉字段(在模型中的field.type="lov")有一个REST端点来获取下拉列表的值。
GET //lov/
GET /todo/lov/category
统计数据
返回模型中数值字段的总数以及最小值、最大值、平均值和总计。
GET //stats
GET /todo/stats
上传文件
此端点允许您上传文件。当前(幼稚)实现仅将文件服务器上的文件保存在名为对象id的文件夹中。
POST //upload/
POST /comics/upload/5
带有查询参数:file和“fieldid”。
嵌套集合
如果模型定义了集合,则可以使用此端点查询它们。
GET //collec/?id=
GET /winecellar/collec/wine_tasting?id=1
API版本
此端点获取API版本(在项目的package.json文件中指定)。
GET /version
Evolutility-Server-Node提供基于与REST API相同模型的GraphQL接口。
在本地运行项目时,garphiQL的URL是http://localhost:2000/graphql。
请求的信息按ID获取一条记录
通过Id获取单个记录。
{
contact (id: 1 ){
firstname
lastname
category_txt
email
}
}
获得许多记录
所有对象都暴露给带有搜索和过滤器的查询。过滤器使用与REST API相同 的条件语法 (例如:{ firstname: "sw.A"} 表示“名字以“A”开头)。
“lov”(值列表)类型的字段表示为Id和值的2个字段。
{
urgent_tasks: todos ( complete: "false", priority: "lt.3" ){
title
description
priority
priority_txt
category
category_txt
complete
}
ab_a_contacts: contacts (search: "ab", firstname: "sw.A") {
id
firstname
lastname
category_txt
email
}
}
图表数据
对于所有对象,记录可以按字段聚合和计数(对于数字或“lov”类型的字段)。
{
contacts_by_category: contact_charts(fieldId:"category"){
label
value
}
task_by_priority: todo_charts(fieldId:"priority") {
label
value
}
restaurants_by_cuisine: restaurant_charts(fieldId:"cuisine") {
label
value
}
}
模型驱动架构(MDA)将使您更快地启动和运行,同时也节省您在项目的进化维护中的时间。
例如,当项目完成时,我们经常需要添加字段。通常,这意味着向数据库添加一列,并在每个使用数据库表的REST端点中手动添加该字段。使用模型驱动的方法,一旦将列添加到数据库中,您只需将字段添加到单个位置(模型),并且使用该模型的每个端点都会公开它。
模型驱动的方法也可以应用于UI。如果你想玩它,我做了两个不同的匹配模型驱动UI的实现,Evolutility-UI-React(用于React)和Evolutility-UI-jQuery(用于jQuery和BackboneJS)。
https://www.codeproject.com/Articles/1169060/Model-driven-REST-or-GraphQL-API-for-CRUD-and-More