Para 1.57.0 常用 CLI 命令与 API 实测详解
Para 1.57.0 命令行工具 para-cli 全部子命令实测详解与 REST API curl 实测:setup 认证、JWT 换发、对象 CRUD、搜索分页过滤、应用管理、401/400/404 错误码对照与排障实录。
Para 1.57.0 常用 CLI 命令与 API 实测详解
目标:系统梳理 Para 1.57.0 的两套操作入口——官方命令行工具 para-cli 的全部子命令与参数,以及 REST API 的认证与核心端点——所有命令与回显均在一台 Ubuntu 24.04 服务器(主机名 para-server,Para 1.57.0 监听 8080)上逐条实测,读者按本文命令可直接复现。
一、环境信息
本章列出实测环境,文中所有命令与回显均基于下表执行。
| 项目 | 值 |
|---|---|
| 操作系统 | Ubuntu 24.04.3 LTS(主机名 para-server) |
| Para 服务端 | 1.57.0,JAR 方式部署于 /opt/para,监听 http://localhost:8080(安装过程见 《Ubuntu 24.04 安装 Para 1.57.0 指南》) |
| para-cli | 1.24.9(npm 全局安装) |
| Node.js | v22.23.4(para-cli 的运行时) |
| curl | 系统自带 /usr/bin/curl |
| 根应用凭据 | accessKey 为 app:para,secretKey 由服务端首次启动自动生成于 /opt/para/para-application.conf |
| 实测日期 | 2026-10-24 |
💡 说明:Para 的 CLI 与 API 操作都围绕「应用(app)」展开——每个应用有独立的 accessKey/secretKey 与数据空间;本文在根应用
app:para下实测,另创建过子应用myapp、tempapp用于演示(后者演示完已删除)。
二、工具概览
2.1 两套操作入口与分工
Para 1.57.0 提供两套操作入口,定位不同:
| 入口 | 形态 | 适用场景 |
|---|---|---|
| para-cli | Node.js 命令行工具,凭据保存在本地 config.json | 服务器上的日常管理:批量导入文件、导出备份、索引重建、应用管理 |
| REST API | HTTP + JSON,签名或 JWT 认证 | 应用后端集成:程序内 CRUD、搜索、多语言客户端 |
分工建议:运维与数据搬运走 para-cli(文件即对象、交互友好),业务代码走 REST API。两者操作同一份数据,本文 4.x 与 5.x 的示例相互对应。
2.2 安装 para-cli
para-cli 是 npm 包,安装方式有两种(官方 README 给出):
# 方式一:全局安装
npm install -g para-cli
# 方式二:不经安装直接运行
npx para-cli
本文实测为全局安装方式,安装后确认版本:
para-cli --version
实测回显:
1.24.9
para-cli 依赖 Node.js 运行时,实测环境为 Node.js v22.23.4(node --version 回显)。
2.3 命令与参数总表(help 实测)
para-cli --help 输出全部子命令与选项,实测回显(全量照抄):
Command-line tool for Para backend servers
Usage:
$ para-cli [command] [file]
Commands:
setup Initial setup, prompts you to enter your Para API keys and endpoint
apps Returns a list of all Para apps
types Returns an object containing all currently defined data types in Para
select <appid> Selects a Para app as a target for all subsequent read/write requests
endpoints [add|remove] List and select Para server endpoints, add new or remove an exiting one
create <file|glob> [--id] [--type] Persists files as Para objects and makes them searchable
read --id 123 [--id 345 ...] Fetches objects with the given ids
update <file.json|glob> ... Updates Para objects with the data from a JSON file (must contain id field)
delete [glob] --id 123 ... Deletes one or more objects from Para
search "query" [--limit --page --sort] Searches the Para index for objects given a query string
rebuild-index Rebuilds the entire search index
app-settings Returns all settings for the authenticated app
new-key Generates a new secret key and saves it to config.json
new-jwt Generates a new JWT super token to be used for app authentication (use --print for console output)
new-app <name> --name --shared Creates a new Para app (only works if you have the keys for the "root" app)
delete-app <id> Deletes an existing Para app (only works for child apps, not the "root" app)
export Exports all data from the app's table
import <file> Imports data from a previously exported ZIP archive
ping Tests the connection to the Para server
me Returns the JSON for the currently authenticated user or app
Options:
--type Sets the "type" field of an object
--id Sets the "id" field of an object
--sanitize Strips all symbols from input files
--accessKey Sets the Para access key
--secretKey Sets the Para secret key
--endpoint Sets the URL of the Para server
--sort Sets the field on which to sort search results
--desc Descending sort for search results (default: true)
--page Page number for search results, "all" will auto-paginate through all results
--limit Limits the number of search results
--lastKey Sets the last id for search-after pagination
--cwd Sets the current directory - used for resolving file paths
--encodeId By default all ids are Base64 encoded, unless this is set to 'false'
--help Prints the list of commands
--version Prints the version of the program
Examples:
$ para-cli setup
$ para-cli create my-blog-post.md
$ para-cli read --id my-blog-post.md
$ para-cli create index.html --type webpage --id "My new article" --sanitize
$ para-cli delete --id 123 --id "my-blog-post.md"
$ para-cli search "type:article AND title:*" --sort timestamp --desc false --limit 10
$ para-cli search "*" --type article --page all
$ para-cli new-key
$ para-cli new-app "mynewapp" --name "Full app name"
$ para-cli new-jwt --print
$ para-cli apps
$ para-cli types
$ para-cli select scoold
$ para-cli endpoints
总表解读:命令分五组——连接与身份(setup/ping/me/endpoints)、应用管理(apps/select/new-app/delete-app/app-settings)、对象读写(create/read/update/delete/search/types)、令牌与密钥(new-key/new-jwt)、数据搬运(export/import/rebuild-index)。--encodeId 默认 true(id 自动 Base64 编码)是后续 read/delete 实测行为的关键背景。
三、前置条件:认证配置
3.1 para-cli setup 配置凭据
para-cli 所有读写命令都需要凭据。首次使用运行 setup,按提示依次输入 accessKey、secretKey 与服务端地址:
para-cli setup
setup 为交互式向导:依次提示输入 Para access key、secret key 与 endpoint,输入 app:para、服务端自动生成的 secretKey、http://localhost:8080 后,凭据被保存到本地配置文件 ~/.config/para-cli-nodejs/config.json。配置结果可用无认证也能执行的 endpoints 命令交叉验证:
para-cli endpoints
实测回显(显示已配置的端点与 root 应用状态;提示选择时直接回车取消):
➤ 1. http://localhost:8080 ✔ root app configured
? Type the number of the Para endpoint to select:
Select endpoint cancelled.
3.2 凭据文件 config.json 与 JWT
查看配置文件内容:
cat /root/.config/para-cli-nodejs/config.json
实测回显(secretKey 为服务端生成的密钥,此处隐去,请勿外泄):
{
"accessKey": "app:para",
"secretKey": "(服务端自动生成的 Base64 密钥)",
"endpoint": "http://localhost:8080",
"selectedApp": {
"accessKey": "app:myapp",
"secretKey": "(myapp 的密钥,仅在选择子应用后存在)"
},
"jwt": "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3OTMzOTQ3OTksImlhdCI6MTc5Mjc4OTk5OSwibmJmIjoxNzkyNzg5OTk0LCJhcHBpZCI6ImFwcDpteWFwcCJ9.PR3JvLXGeqcS3t9xQRr0zUUORVzqQsr4pYS7Kb0dLOc"
}
结构说明:accessKey/secretKey 是根应用凭据;selectedApp 在执行 para-cli select <appid> 切换应用后出现,后续读写以它为准;jwt 是最近一次 new-jwt 生成的令牌缓存。若不想走交互式 setup,也可在每条命令后直接挂参数:para-cli --accessKey app:para --secretKey <密钥> --endpoint http://localhost:8080 <命令>。
四、para-cli 子命令详解
本章逐个演示核心子命令。实测均在 /opt/para 目录下执行(create 的文件路径为相对路径)。
4.1 连接与身份:ping / me / endpoints
ping——测试连通性与认证状态:
para-cli ping
实测回显:
✔ Connected to Para server v1.57.0 on http://localhost:8080. Authenticated as: app para (app:para)
me——返回当前认证主体的完整 JSON:
para-cli me
实测回显(输出节选,实测首尾):
{
"id": "app:para",
"type": "app",
"name": "para",
"stored": true,
"indexed": true,
"cached": true,
"version": 0,
"active": true,
"appIdentifier": "para",
"appid": "para",
"datatypes": {
"posts": "post",
"objectses": "objects"
},
"objectURI": "/app/app%3Apara",
"plural": "apps",
"timestamp": 1792789694560,
"tokenValiditySec": 86400
}
me 回显中的 datatypes 字段记录了该应用注册过的数据类型(此处 posts: post 是此前实测注册的)。
endpoints——多端点管理与选择,回显见 3.1 节:列出所有已配置端点(➤ 标记当前选中项,✔ root app configured 表示该端点已验证过 root 凭据),输入序号切换。
4.2 应用管理:apps / select / new-app / delete-app / app-settings
apps——列出服务器上全部应用:
para-cli apps
实测回显(此时服务器上有 3 个应用):
Found 3 apps on http://localhost:8080:
[tempapp] [myapp] [para]
Typing para-cli select tempapp will switch to that app.
Current app: app:para
new-app——创建子应用(仅 root 应用凭据可执行):
para-cli new-app tempapp --name "Temp App"
实测回显:
✔ App created:
{
"accessKey": "app:tempapp",
"message": "Save these keys - they are shown only once!",
"secretKey": "PA5jiFbHaumjDBociKvH/LQMMgkrC7izjVemFqGa3v9ANXUMyezXrA=="
}
⚠️ 注意:secretKey 只显示这一次,务必当场保存;丢失后无法找回,只能对该应用重新生成密钥。上面回显中的应用是实测时创建、演示删除后已销毁的临时应用,密钥已随之失效。
select——把后续读写切换到指定应用(输出包含在 apps 命令的提示里,Typing para-cli select tempapp will switch to that app):
para-cli select myapp
执行后 config.json 出现 selectedApp 段(见 3.2 节),后续 create/read/search 都作用于 myapp 的数据空间;切回根应用执行 para-cli select para。
delete-app——删除子应用(不能删 root 应用),交互式确认:
para-cli delete-app app:tempapp
实测回显:
? Are you sure you want to delete app:tempapp? ALL DATA FOR THAT APP WILL BE LOS
T! yes/No
交互终端中输入 yes 回车即执行删除。实测确认:未交互确认时(如管道输入)命令输出 Delete cancelled. 且应用列表保持不变——自动化脚本中删除应用请改用 REST API(见 5.5 节)。
app-settings——返回当前应用全部设置:
para-cli app-settings
实测回显(默认设置下为空对象):
{}
4.3 数据类型总表:types
types 返回 Para 内置的数据类型清单(复数名 → 单数类型名):
para-cli types
实测回显:
{
"linkers": "linker",
"addresses": "address",
"sysprops": "sysprop",
"webhooks": "webhook",
"translations": "translation",
"votes": "vote",
"forms": "form",
"users": "user",
"apps": "app",
"tags": "tag"
}
这份清单是 create 命令 --type 参数的合法取值参考(post、page 等自定义类型也可用,写入后同样出现在 search 结果中)。
4.4 对象写入:create
create 把本地文件持久化为 Para 对象并自动建立检索索引。先准备一个 Markdown 文件:
cat > hello-world.md << 'EOF'
---
title: Hello Para
tags: [demo]
---
My first Para object
EOF
para-cli create hello-world.md
实测回显:
✔ Creating hello-world
✔ Created 1 objects with a total size of 0 KB.
✔ Created 1 objects.
带参数创建示例:指定 id 与类型(命令形式来自 help 示例清单,--id "My new article" 直接决定对象 id,--sanitize 清除文件中的特殊符号):
para-cli create index.html --type webpage --id "My new article" --sanitize
验证:create 成功后立即 search 该文件名(search 详解见 4.8 节):
para-cli search "name:hello-world"
实测回显(输出节选,实测首尾):
[
{
"id": "aGVsbG8td29ybGQ=",
"type": "post",
"name": "hello-world",
"stored": true,
"indexed": true,
"cached": true,
"version": 0,
"appid": "para",
"creatorid": null,
"objectURI": "/post/aGVsbG8td29ybGQ%3D",
"parentid": null,
"plural": "posts",
"tags": [
"demo"
],
"timestamp": 1792789930344,
"updated": null,
"votes": 0,
"title": "Hello Para",
"content": "My first Para object"
}
]
关键事实:id aGVsbG8td29ybGQ= 正是 hello-world 的 Base64 编码——--encodeId 默认开启所致(总表 Options 段原文:By default all ids are Base64 encoded)。
4.5 对象读取:read
read 按 id 取回对象,--id 可重复传多个:
para-cli read --id plain-post
实测回显:
[
{
"id": "plain-post",
"type": "post",
"name": "plain-post",
"stored": true,
"indexed": true,
"cached": true,
"version": 0,
"appid": "para",
"creatorid": null,
"objectURI": "/post/plain-post",
"parentid": null,
"plural": "posts",
"tags": [
"demo"
],
"timestamp": 1792789982141,
"updated": null,
"votes": 0,
"title": "Hello Para",
"content": "My first Para object"
}
]
读取不存在的 id 返回空数组:
para-cli read --id no-such-object
实测回显:
[]
⚠️ 注意:read 的
--id值是「编码前」的原始 id,para-cli 内部自动做 Base64 处理;直接传 Base64 串(如aGVsbG8td29ybGQ=)反而会因二次编码而读不到。
4.6 对象更新:update
update 依据 JSON 文件中的 id 字段更新对象(help 原文:must contain id field)。先写更新文件再执行:
cat > plain-post.json << 'EOF'
{
"id": "plain-post",
"title": "Hello Para v2",
"content": "updated content",
"tags": ["demo"]
}
EOF
para-cli update plain-post.json
实测回显:
✔ Updating plain-post
✔ Updated 1 files.
验证:重新 read 确认字段已变:
para-cli read --id plain-post
实测回显(输出节选,实测首尾):
[
{
"id": "plain-post",
"type": "post",
"name": "plain-post",
"stored": true,
"indexed": true,
"cached": true,
"version": 0,
"appid": "para",
"objectURI": "/post/plain-post",
"plural": "posts",
"tags": [
"demo"
],
"timestamp": 1792789982141,
"updated": 1792789999121,
"votes": 0,
"title": "Hello Para v2",
"content": "updated content"
}
]
title/content 已更新,updated 时间戳从 null 变为更新时刻(1792789999121),timestamp(创建时间)不变。
4.7 对象删除:delete
delete 按 id 删除对象,支持一次多个:
para-cli delete --id hello-world
实测回显:
✔ Deleted objects " [ 'aGVsbG8td29ybGQ=' ] " from Para.
回显中括号内是被删除对象的 Base64 编码 id(与 4.4 节 search 看到的 id 一致)。验证:再 read 该对象应返回空数组:
para-cli read --id hello-world
实测回显:
[]
4.8 搜索:search
search 全文检索当前应用的索引,查询语法与分页参数是重点。搜索全部对象:
para-cli search "*"
实测回显(3 个对象:一个子应用、一个 post、根应用):
[
{
"id": "app:myapp",
"type": "app",
"name": "My App",
"stored": true,
"indexed": true,
"cached": true,
"version": 0,
"active": true,
"appIdentifier": "myapp",
"appid": "para",
"creatorid": null,
"datatypes": {},
"deleteOn": null,
"objectCount": 0,
"objectURI": "/app/app%3Amyapp",
"parentid": null,
"plural": "apps",
"readOnly": false,
"resourcePermissions": {},
"sharingIndex": false,
"sharingTable": false,
"tags": null,
"timestamp": 1792789919308,
"tokenValiditySec": 86400,
"updated": null,
"validationConstraints": {},
"votes": 0
},
{
"id": "aGVsbG8td29ybGQ=",
"type": "post",
"name": "hello-world",
"stored": true,
"indexed": true,
"cached": true,
"version": 0,
"appid": "para",
"creatorid": null,
"objectURI": "/post/aGVsbG8td29ybGQ%3D",
"parentid": null,
"plural": "posts",
"tags": [
"demo"
],
"timestamp": 1792789930344,
"updated": null,
"votes": 0,
"title": "Hello Para",
"content": "My first Para object"
},
{
"id": "app:para",
"type": "app",
"name": "para",
"stored": true,
"indexed": true,
"cached": true,
"version": 0,
"active": true,
"appIdentifier": "para",
"appid": "para",
"creatorid": null,
"datatypes": {
"posts": "post"
},
"deleteOn": null,
"objectCount": 0,
"objectURI": "/app/app%3Apara",
"parentid": null,
"plural": "apps",
"readOnly": false,
"resourcePermissions": {},
"sharingIndex": false,
"sharingTable": false,
"tags": null,
"timestamp": 1792789694560,
"tokenValiditySec": 86400,
"updated": 1792789930358,
"validationConstraints": {},
"votes": 0
}
]
无匹配结果时返回空数组:
para-cli search "title:nonexistent"
实测回显:
[]
排序与限量组合(help 示例原文,参数默认值:--desc 默认 true 即默认降序):
para-cli search "type:post AND title:*" --sort timestamp --desc false --limit 10
💡 技术拓展:搜索查询串的写法不止一种。Para 检索底层是 Apache Lucene,常用查询语法对比——
语法 示例 含义 通配匹配 *全部对象 字段精确/前缀 name:hello-world字段等于(或按 Lucene 前缀规则匹配) 布尔组合 type:post AND title:*AND/OR/NOT 组合条件 短语检索 title:"Hello Para"引号内按短语整体匹配 本文实测以通配与字段匹配为主(
*、name:、type:),复杂组合查询可按 Lucene 语法书写。查询语法的完整规则见官方文档 Search 章节与 《Para 1.57.0 介绍与选型指南》中的检索层说明。
4.9 令牌与密钥:new-jwt / new-key
new-jwt——为当前应用生成 JWT 超级令牌(供 REST API 认证用,--print 直接输出到控制台):
para-cli new-jwt --print
实测回显(myapp 的令牌,有效期 7 天):
eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3OTMzOTQ3OTksImlhdCI6MTc5Mjc4OTk5OSwibmJmIjoxNzkyNzg5OTk0LCJhcHBpZCI6ImFwcDpteWFwcCJ9.PR3JvLXGeqcS3t9xQRr0zUUORVzqQsr4pYS7Kb0dLOc
令牌本体是三段式 JWT:对中间段做 Base64 解码,实测载荷为 {"exp":1793394799,"iat":1792789999,"nbf":1792789994,"appid":"app:myapp"}——appid 标明令牌所属应用,exp - iat = 604800 秒即 7 天有效期。不传 --print 时令牌静默写入 config.json 的 jwt 字段(见 3.2 节)。
new-key——为当前应用重新生成 secretKey 并保存到 config.json(help 原文:Generates a new secret key and saves it to config.json)。该命令会使旧密钥立即失效,多客户端场景需同步更新所有使用方,故本文未在实际环境执行,读者使用前先确认影响面。
4.10 导出、导入与索引维护:export / import / rebuild-index
export——导出当前应用全部数据为 ZIP:
para-cli export
实测回显:
✔ Exported 0MB of data to file para_20261023_211541.zip
导出文件落在当前工作目录,文件名自动带时间戳(para_年月日_时分秒.zip)。
import——把先前导出的 ZIP 导回应用(实测把 export 产物导回):
para-cli import /tmp/import_test.zip
实测回显:
✔ Imported 3 object into app "para"
rebuild-index——重建整个检索索引(存储与索引不一致时使用):
para-cli rebuild-index
实测回显:
{
"tookMillis": 23,
"reindexed": 3
}
五、REST API 实测详解
para-cli 的每个操作都有对应的 REST 端点;程序集成时直接调 REST API。本章全部用 curl 实测,端点前缀 http://localhost:8080。
5.1 认证方式总览与 JWT 获取
Para 服务端对受保护端点要求认证,可选方式如下:
| 方式 | 机制 | 适用 |
|---|---|---|
| Sig4 签名 | 请求按 AccessKey/SecretKey 计算 AWS 风格签名头(para-cli 内部即此方式) | 正式客户端库 |
| JWT Bearer | 先用签名换发一次性 JWT,后续请求带 Authorization: Bearer <token> | 脚本与 curl 调试,最简便 |
| 免认证 | 仅 para.security.ignored 列出的路径(/v1/_health、/v1/_config/options 等) | 健康检查、监控探测 |
本文按 JWT Bearer 路线实测。第一步用 para-cli 换发 root 应用令牌(内部走 Sig4 签名,无需手动算签名):
cd /opt/para
para-cli --accessKey app:para --secretKey "$(grep root_secret_key para-application.conf | cut -d'"' -f2)" new-jwt --print
实测回显(root 应用令牌):
eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3OTM0MTk4NjUsImlhdCI6MTc5MjgxNTA2NSwibmJmIjoxNzkyODE1MDYwLCJhcHBpZCI6ImFwcDpwYXJhIn0.xShf_nQVTaatHS01cTP4_S5JgwAPp_wuzDkpxUnuGQg
第二步存入 shell 变量供后续所有 curl 复用(取值来自上一步输出):
JWT="eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3OTM0MTk4NjUsImlhdCI6MTc5MjgxNTA2NSwibmJmIjoxNzkyODE1MDYwLCJhcHBpZCI6ImFwcDpwYXJhIn0.xShf_nQVTaatHS01cTP4_S5JgwAPp_wuzDkpxUnuGQg"
不认证直接访问受保护端点的实测结果见 5.6 节(401)。
5.2 免认证端点:_health 与 _config/options
健康检查(监控探测直接用):
curl -s http://localhost:8080/v1/_health
实测回显:
{"message":"healthy"}
配置选项(返回服务端全部配置项的元信息):
curl -s http://localhost:8080/v1/_config/options
实测回显(输出节选,实测首尾):
{"Core":{"para.app_name":{"defaultValue":"para","description":"The formal name of the web application.","type":"String","tags":""},"para.root_secret_override":{"defaultValue":"","description":"Overrides the secret key for the root Para app `app:para` once, upon first initialization.","type":"String"
该端点返回按模块(Core/Security 等)组织的配置项字典,含每个键的 defaultValue/description/type——排查配置问题时比翻文档更快,参数逐键详解见 《Para 1.57.0 配置参数详解》。
5.3 对象 CRUD
创建:POST /v1/objects。注意实测行为——该端点创建的对象使用默认类型:
curl -s -X POST -H "Authorization: Bearer $JWT" -H "Content-Type: application/json" \
-d '{"type":"post","title":"API Test","content":"created via REST API","tags":["demo"]}' \
http://localhost:8080/v1/objects
实测回显:
{"id":"2024718385085943808","appid":"para","cached":true,"creatorid":null,"indexed":true,"name":"ParaObject 1792815065096","objectURI":"/objects/2024718385085943808","parentid":null,"plural":"objectses","stored":true,"tags":["demo"],"timestamp":1792815065145,"type":"objects","updated":null,"version":0,"votes":0,"title":"API Test","content":"created via REST API"}
回显关键点:即使请求体写了 "type":"post",POST /v1/objects 创建的对象 type 仍是默认的 objects(plural 为 objectses),id 由服务端生成(雪花 id 风格的长数字串)。要让对象落到指定类型,应 POST 到复数类型端点:
curl -s -X POST -H "Authorization: Bearer $JWT" -H "Content-Type: application/json" \
-d '{"title":"Post via /v1/posts","content":"typed object","tags":["api"]}' \
http://localhost:8080/v1/posts
实测回显:
{"id":"2024718511435157504","appid":"para","cached":true,"creatorid":null,"indexed":true,"name":"ParaObject 1792815095269","objectURI":"/post/2024718511435157504","parentid":null,"plural":"posts","stored":true,"tags":["api"],"timestamp":1792815095269,"type":"post","updated":null,"version":0,"votes":0,"title":"Post via /v1/posts","content":"typed object"}
这次 type 正确落为 post、objectURI 为 /post/<id>。
读取:GET /v1/objects/{id}:
curl -s -H "Authorization: Bearer $JWT" http://localhost:8080/v1/objects/2024718385085943808
实测回显(与创建回显完全一致,逐字段相同):
{"id":"2024718385085943808","appid":"para","cached":true,"creatorid":null,"indexed":true,"name":"ParaObject 1792815065096","objectURI":"/objects/2024718385085943808","parentid":null,"plural":"objectses","stored":true,"tags":["demo"],"timestamp":1792815065145,"type":"objects","updated":null,"version":0,"votes":0,"title":"API Test","content":"created via REST API"}
更新:PATCH /v1/objects/{id},只传要改的字段:
curl -s -X PATCH -H "Authorization: Bearer $JWT" -H "Content-Type: application/json" \
-d '{"title":"API Test v2"}' \
http://localhost:8080/v1/objects/2024718385085943808
实测回显(输出节选,title 已更新、updated 时间戳出现):
{"id":"2024718385085943808","appid":"para","cached":true,"creatorid":null,"indexed":true,"name":"ParaObject 1792815065096","objectURI":"/objects/2024718385085943808","parentid":null,"plural":"objectses","stored":true,"tags":["demo"],"timestamp":1792815065145,"updated":1792815065358,"version":0,"votes":0,"title":"API Test v2","content":"created via REST API"}
删除:DELETE /v1/objects/{id},成功返回 200 与空响应体:
curl -s -o /dev/null -w "%{http_code}\n" -X DELETE -H "Authorization: Bearer $JWT" \
http://localhost:8080/v1/posts/2024718511435157504
实测回显:
200
验证删除生效:再读已删除的对象,返回 404:
curl -s -w "\n%{http_code}\n" -H "Authorization: Bearer $JWT" \
http://localhost:8080/v1/posts/2024718511435157504
实测回显:
{"code":404,"message":"Not Found"}
404
5.4 搜索、分页与过滤
搜索端点 GET /v1/search,q 为查询串。全量搜索:
curl -s -H "Authorization: Bearer $JWT" "http://localhost:8080/v1/search?q=*"
实测回显(输出节选,实测首尾;共 4 个命中:2 个 app 对象 + 2 个内容对象):
{"totalHits":4,"lastKey":"2024718511439351808","page":0,"items":[{"id":"app:para","active":true,"appIdentifier":"para","appid":"para","cached":true,"creatorid":null,"datatypes":{"posts":"post","objectses":"objects"},"deleteOn":null,"indexed":true,"name":"para","objectCount":0,"objectURI":"/app/app%3Apara","parentid":null,"plural":"apps","readOnly":false,"resourcePermissions":{},"sharingIndex":false,"sharingTable":false,"stored":true,"tags":null,"timestamp":1792789694560,"tokenValiditySec":86400,"type":"app","updated":1792789930358,"validationConstraints":{},"version":0,"votes":0},{"id":"app:myapp",...},{"id":"2024718385085943808",...},{"id":"plain-post",...}]}
响应结构说明:totalHits 是总命中数,page 是当前页号,items 是对象数组,lastKey 用于游标翻页(下一页请求带上 &lastKey=<lastKey 值>)。
类型过滤 + 限量(type 与 limit 参数组合):
curl -s -H "Authorization: Bearer $JWT" "http://localhost:8080/v1/search?q=*&type=post&limit=1"
实测回显:
{"totalHits":1,"lastKey":"2024613845670039554","page":0,"items":[{"id":"plain-post","appid":"para","cached":true,"creatorid":null,"indexed":true,"name":"plain-post","objectURI":"/post/plain-post","parentid":null,"plural":"posts","stored":true,"tags":["demo"],"timestamp":1792789982141,"type":"post","updated":1792789999121,"version":0,"votes":0,"title":"Hello Para v2","content":"updated content"}]}
type=post 过滤后只返回 post 对象,limit=1 限制每页条数——totalHits 也随之按过滤条件统计(此处为 1)。
非法查询串的报错(实测 Lucene 语法约束):
curl -s -H "Authorization: Bearer $JWT" "http://localhost:8080/v1/search?q=title:*"
实测回显:
{"code":400,"message":"Invalid query string syntax q=title:* in request GET /v1/search"}
单独的 字段:* 通配被服务端拒绝;组合条件(如 type:post AND title:*)或全量 * 可正常执行——这是 Lucene 查询解析层的限制,遇到 400 先调整查询串语法。
5.5 应用管理:GET /v1/apps 与 DELETE /v1/apps/{id}
应用列表(root 应用令牌可访问):
curl -s -H "Authorization: Bearer $JWT" http://localhost:8080/v1/apps
实测回显(输出节选,实测首尾;当时共 2 个应用):
{"totalHits":2,"lastKey":"2024718385736060928","page":0,"items":[{"id":"app:myapp","active":true,"appIdentifier":"myapp","appid":"para","cached":true,"creatorid":null,"datatypes":{"posts":"post"},"deleteOn":null,"indexed":true,"name":"My App","objectCount":0,"objectURI":"/app/app%3Amyapp","parentid":null,"plural":"apps","readOnly":false,"resourcePermissions":{},"sharingIndex":false,"sharingTable":false,"stored":true,"tags":null,"timestamp":1792789919308,"tokenValiditySec":86400,"type":"app","updated":1792790033405,"validationConstraints":{},"version":0,"votes":0},{"id":"app:para",...}]}
删除应用(DELETE /v1/apps/{accessKey},实测删除 tempapp,不可删除 root 应用):
curl -s -o /dev/null -w "%{http_code}\n" -X DELETE -H "Authorization: Bearer $JWT" \
http://localhost:8080/v1/apps/app:tempapp
实测回显:
200
验证:再次 GET /v1/apps,totalHits 从 3 变为 2,tempapp 消失(实测回显省略,验证方法同上比对 totalHits)。这补上了 4.2 节 delete-app 交互确认无法自动化的缺口:自动化场景删应用走此 API。
5.6 错误响应实测:401 / 400 / 404
三类典型错误的实测回显,排障时可直接对照。
401 未认证——不带 Authorization 头访问受保护端点:
curl -s -w "\n%{http_code}\n" http://localhost:8080/v1/objects/aGVsbG8td29ybGQ=
实测回显:
{"code":401,"message":"You don't have permission to access this resource. [GET /v1/objects/aGVsbG8td29ybGQ=]"}
401
400 请求非法——查询串语法错误(完整实测回显见 5.4 节:{"code":400,"message":"Invalid query string syntax q=title:* in request GET /v1/search"})与空请求体触发 Jackson 解析错误均属此类。
404 资源不存在——读取已删除对象(见 5.3 节实测):
{"code":404,"message":"Not Found"}
404
排障对照:401 查凭据(令牌缺失/过期/应用错)、400 查请求体与查询串语法、404 查 id 是否存在与类型端点是否匹配(/v1/objects/{id} 与 /v1/posts/{id} 各自管各自的数据)。
六、注意事项
- secretKey 只显示一次:new-app 回显(4.2 节)明确提示 Save these keys - they are shown only once!,漏存只能换密钥重来;config.json 与 para-application.conf 是密钥的两个落盘点,注意文件权限,勿提交版本库。
- id 的 Base64 编码:para-cli 默认对 id 做 Base64 编解码(
--encodeId默认 true),命令参数里传原始 id;REST API 路径中的 id 同样可传原始值由服务端处理,但 delete 回显与 objectURI 中展示的是编码后的形式(4.4/4.7 节实测对照)。 - JWT 有效期:new-jwt 生成的令牌 7 天有效(实测载荷 exp-iat=604800 秒),过期后 401,重新生成即可;para-cli 内部会在调用时自动续期缓存到 config.json。
- 健康检查瞬时失败属正常:服务端日志偶见
Server is no longer healthy! Health check failed for services: Cache(实测 journalctl 记录,1 分钟后自动恢复Server is healthy.),默认 CaffeineCache 的瞬时抖动,监控告警阈值建议放行单次失败。 - 交互式命令无法自动化:delete-app 的确认(4.2 节实测 Delete cancelled.)与 setup/endpoints 的选择提示都需要交互终端;脚本场景用 REST API 或直接挂 --accessKey/--secretKey 参数。
七、常见问题(FAQ)
Q1:para-cli read 返回 [],对象明明创建过?
两个最常见原因(均有实测对应):一是对象已被删除(4.7 节删除后 read 返回 []);二是 id 传错形态——para-cli 传原始 id 而非 Base64 编码值(4.5 节注意事项)。用 para-cli search "*" 先确认对象在不在、拿到原始 name 后再 read。
Q2:search 报 Invalid query string syntax(400)?
查询串不合法。实测 q=title:* 这种「单字段 + 纯通配」被服务端拒绝(5.4 节),改为全量 q=* 或组合条件 q=type:post AND title:* 即可。原则:* 单独用合法,跟在字段冒号后作为唯一条件会触发 Lucene 解析拒绝。
Q3:curl 调 API 全部返回 401?
认证头没带对。JWT 方式必须 Authorization: Bearer <token>(token 与 Bearer 之间一个空格);检查 token 是否属于目标应用(载荷 appid 字段)、是否过期(exp 字段)。快速自检:先用免认证端点 curl -s http://localhost:8080/v1/_health 确认服务本身正常,再排查认证(5.6 节 401 实测回显对照)。
Q4:POST /v1/objects 创建的对象 type 不对,怎么落成 post?
POST /v1/objects 固定创建默认类型 objects(5.3 节实测);指定类型要 POST 到复数端点 /v1/posts、/v1/pages 等(5.3 节实测 type 正确落为 post)。可用类型清单见 4.3 节 types 命令。
Q5:导出的 ZIP 导不回去或报数据异常?
import 只接受 export 产物(同版本导出的 ZIP,4.10 节实测导回 3 个对象)。跨版本迁移先升级两侧到同版本再导出导入;导出前用 para-cli apps 确认当前应用——export/import 只作用于当前选中应用的数据空间,别把 A 应用数据导进 B。
八、参考链接
- Para 官方文档(API 参考):https://paraio.org/docs
- para-cli 仓库与 README:https://github.com/Erudika/para-cli
- para-cli npm 包:https://www.npmjs.com/package/para-cli
- Para Web Console(官方在线管理界面):https://console.paraio.org
- 站内相关文档:《Para 1.57.0 介绍与选型指南》、《Ubuntu 24.04 安装 Para 1.57.0 指南》、《Para 1.57.0 配置参数详解》