定制 lindemannrock/craft-formie-rest-api 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

lindemannrock/craft-formie-rest-api

Composer 安装命令:

composer require lindemannrock/craft-formie-rest-api

包简介

REST API for Formie - Provides REST endpoints for accessing Formie forms and submissions

README 文档

README

Latest Version Craft CMS Formie PHP License

A REST API plugin for Craft CMS that exposes Formie forms and submissions through authenticated REST endpoints. Designed for external systems (e.g. SAP, BI tools, partner integrations) that need structured form data over HTTP.

Note on GraphQL: Formie ships with its own GraphQL schema at Craft's /api endpoint. This plugin does not add GraphQL — it adds a separate REST API with its own auth (X-API-Key), rate limiting, and access logging. If you want GraphQL, use Formie's built-in support directly.

License

This is a commercial plugin licensed under the Craft License. It will be available on the Craft Plugin Store soon. See LICENSE.md for details.

⚠️ Pre-Release

This plugin is in active development and not yet available on the Craft Plugin Store. Features and APIs may change before the initial public release.

Requirements

  • Craft CMS 5.0 or greater
  • PHP 8.2 or greater
  • Formie 3.0 or greater

Overview

The Formie REST API plugin provides:

  • REST endpoints for forms and submissions
  • Database-backed API keys managed in the control panel — one key per consumer, with per-key form scoping, submissions toggle, HMAC signing, IP whitelist, rate limit, and expiry
  • API-key authentication via X-API-Key header
  • Rate limiting with X-RateLimit-* response headers and 429 on exceed
  • Access logging for every request (partial key fingerprint, endpoint, IP, status), viewable in the CP log viewer
  • CLI key creation (ddev craft formie-rest-api/api-keys/create)
  • In-CP test page for verifying keys, endpoints, and filters without leaving Craft
  • Postman collection (in postman/) with auto-HMAC pre-request script
  • Translated CP UI — 12 languages (EN, DE, FR, NL, ES, IT, PT, AR, JA, SV, DA, NO)

Installation

Via Composer

cd /path/to/project
composer require lindemannrock/craft-formie-rest-api
./craft plugin/install formie-rest-api

Using DDEV

cd /path/to/project
ddev composer require lindemannrock/craft-formie-rest-api
ddev craft plugin/install formie-rest-api

Via Control Panel

In the Control Panel, go to Settings → Plugins and click "Install" for Formie REST API.

API Keys (CP-managed)

API keys are created and managed in the control panel under Formie REST API → API Keys — one key per consumer. Each key carries its own:

  • Allowed forms — all forms (*, including future ones) or an explicit list; a scoped key can never read other forms' data
  • Read submissions toggle — off limits the key to the forms endpoints
  • Require signing toggle — enforce HMAC request signing (each key gets a paired signing secret at creation)
  • IP whitelist — single IPs or CIDR ranges, IPv4/IPv6
  • Rate limit — requests per hour (default 100)
  • Expiry — optional auto-expiry datetime
  • Enabled switch — pause a key without deleting it; revoke deletes it permanently

The plaintext key and its signing secret are shown exactly once, right after creation. The plugin stores only a hash of the key and the signing secret encrypted at rest — if either is lost, revoke and create a new key (that is also the rotation procedure).

Keys can also be created headlessly:

php craft formie-rest-api/api-keys/create \
  --name="Partner integration" \
  --forms=contactForm,productRating \
  --rate-limit=200

(--forms="*", --no-submissions, --no-signing, --ip-whitelist=..., --valid-until=..., --disabled are also available.)

Environment Configuration (legacy)

Deprecated: environment-variable keys remain supported only as a migration bridge for existing consumers and will be removed in a future release. Create keys in the CP instead.

The plugin reads API keys from environment variables (or, optionally, config/general.php):

# Primary API key — read forms and submissions
FORMIE_API_KEY="..."

# Limited access key — read forms only (optional)
FORMIE_API_KEY_LIMITED="..."

# Test key — only valid when devMode=true (optional, local only)
FORMIE_API_KEY_TEST="..."

Use different values per environment (local, staging, production). Never share keys across environments.

Generating keys

A console command generates secure keys and (optionally) writes them to .env:

# See available Formie REST API console commands
ddev craft formie-rest-api/help

# Focused help for the key generator
ddev craft formie-rest-api/help security/generate-key

# Native Craft/Yii signature help
ddev craft help formie-rest-api/security/generate-key
# Local
ddev craft formie-rest-api/security/generate-key

# Staging / production (run on the server)
php craft formie-rest-api/security/generate-key

The command:

  1. Asks which key to generate — primary, limited, test, or all (generates all three in turn, sharing the same prefix; test is auto-skipped if devMode is off)
  2. Asks for a prefix — defaults to fra_, type - for none, or supply a custom prefix
  3. Prints the generated key (prefix + 64 hex chars)
  4. Warns and confirms before replacing an existing key (avoids surprise 401s for live consumers)
  5. Offers to also generate a paired HMAC signing secret (presence of the matching FORMIE_API_SIGNING_SECRET[_*] env var auto-enables required-signature mode for that key — see HMAC Request Signing)
  6. Asks whether to write to .env:
    • Yes → writes one consolidated block per key type (# Formie REST API — Primary / Limited / Test, key + secret co-located). Re-running the command for the same key cleans up any legacy scattered entries it owns and replaces them with the fresh block.
    • No → prints the block so you can paste it into a hosting panel (Servd, Forge, Cloudways, etc.) or a secrets store

Test keys are refused (when chosen explicitly) or skipped (in all mode) unless devMode=true.

When an existing signing secret is detected, the command asks whether to rotate it or keep it — so you can rotate just the API key without invalidating signed clients.

Authentication

REST API Authentication

Include API key in request headers:

curl -H "X-API-Key: your-api-key-here" https://yoursite.com/api/v1/formie/forms

HMAC Request Signing (optional, recommended for production)

For production keys, the plugin supports HMAC-SHA256 request signing. Signing adds:

  • Replay protection — captured requests expire after a 5-minute timestamp window
  • Tamper detection — the signature covers method, path, timestamp, and body
  • Defence-in-depth — a leaked API key alone is no longer sufficient; the attacker also needs the separate signing secret

Enable per key by setting the matching env var (presence of the env var auto-enables the requirement on that key):

FORMIE_API_SIGNING_SECRET="..."           # paired with FORMIE_API_KEY
FORMIE_API_SIGNING_SECRET_LIMITED="..."   # paired with FORMIE_API_KEY_LIMITED
FORMIE_API_SIGNING_SECRET_TEST="..."      # paired with FORMIE_API_KEY_TEST (devMode only)

ddev craft formie-rest-api/security/generate-key offers to generate the paired signing secret at the end of the key-generation flow.

Required headers when signing is enabled:

Header Value
X-API-Key The API key (as before)
X-Timestamp Current Unix epoch seconds
X-Signature Hex HMAC-SHA256 of the signature base, keyed by the signing secret

Signature base (joined with literal \n):

METHOD\nPATH_WITH_QUERY\nTIMESTAMP\nBODY

For a GET /api/v1/formie/forms request the body is empty.

Sort query parameters alphabetically before signing. When PATH_WITH_QUERY has a query string, sort its parameters alphabetically (byte-wise) before building the signature base — e.g. sign /api/v1/formie/submissions?formHandle=contact&limit=10&offset=0, not the order you happened to send them in. A CDN/proxy in front of the site (e.g. Cloudflare) normalizes query-string order before the request reaches the server, so an unsorted signature will fail with 401 Missing or invalid request signature once a request has two or more params that aren't already in alphabetical order. The order you actually send the params in doesn't matter — only the order you sign. (The server also accepts the as-received order, so single-param and already-sorted requests work either way.)

Example client (bash):

KEY="fra_..."
SECRET="..."
TS=$(date +%s)
PATH_Q="/api/v1/formie/forms"
SIG=$(printf "GET\n%s\n%s\n" "$PATH_Q" "$TS" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')

curl -H "X-API-Key: $KEY" \
     -H "X-Timestamp: $TS" \
     -H "X-Signature: $SIG" \
     "https://yoursite.com$PATH_Q"

Example client (PHP):

$key = getenv('FORMIE_API_KEY');
$secret = getenv('FORMIE_API_SIGNING_SECRET');
$path = '/api/v1/formie/forms';
$ts = (string) time();
$sig = hash_hmac('sha256', "GET\n{$path}\n{$ts}\n", $secret);

// Send X-API-Key, X-Timestamp, X-Signature headers

Example client (Node.js):

const crypto = require('crypto');

const key = process.env.FORMIE_API_KEY;
const secret = process.env.FORMIE_API_SIGNING_SECRET;

// Path must include the query string when present, e.g.
// '/api/v1/formie/submissions?formHandle=contact&limit=10'
const method = 'GET';
const pathQ = '/api/v1/formie/forms';
const body = '';                                   // empty for GET, but still signed
const ts = Math.floor(Date.now() / 1000).toString();

const base = `${method}\n${pathQ}\n${ts}\n${body}`;
const sig = crypto.createHmac('sha256', secret).update(base).digest('hex');

const res = await fetch('https://yoursite.com' + pathQ, {
    method,
    headers: {
        'X-API-Key': key,
        'X-Timestamp': ts,
        'X-Signature': sig,
        'Accept': 'application/json',
    },
});
const data = await res.json();

Postman collection

A ready-to-use Postman collection lives in postman/ — collection plus a single environment template. The collection-level pre-request script computes the HMAC signature automatically (sorting query params before signing) when signing_secret is set on the active environment, and skips it when empty (for keys without signing). Switch keys (Primary, Limited, Test) by pasting a different key into the environment. See postman/README.md for setup.

You can also download the collection and environment as a ZIP from Formie REST API → Settings → Test in the Craft control panel.

IP Whitelist (optional, defence-in-depth)

Restrict which client IPs can use a given API key. Useful for server-to-server integrations on stable infrastructure (SAP, ERP, internal networks).

Enable per key by setting the matching env var to a comma-separated list of IPs and/or CIDR ranges (presence of the env var auto-enables the restriction):

FORMIE_API_IP_WHITELIST="203.0.113.5,192.168.1.0/24,2001:db8::/32"
FORMIE_API_IP_WHITELIST_LIMITED="..."
FORMIE_API_IP_WHITELIST_TEST="..."

Empty/unset env → no IP restriction (request accepted from any IP). Non-matching IP → 401 Unauthorized with message Request originates from an IP not allowed for this key.

Supports IPv4 and IPv6. Single IPs (203.0.113.5, 2001:db8::1) and CIDR ranges (192.168.1.0/24, 2001:db8::/32).

CDN / reverse-proxy caveat: Craft::$app->request->getUserIP() returns whatever sent the request to PHP. Behind a CDN or reverse proxy you'll need to configure Craft's trustedHosts and proxy headers correctly — otherwise the whitelist matches the proxy IP, not the real client. See Craft's request docs for trustedHosts setup.

CORS support — not currently implemented

This plugin does not add Access-Control-Allow-* response headers and does not handle OPTIONS preflight requests. The API is currently designed for server-to-server consumers (your SAP/ERP integration, scripts, scheduled jobs) where CORS does not apply.

This means a browser-based client on a different origin (a React/Vue dashboard, a partner widget, etc.) cannot call this API directly. The browser will block the response with a CORS error.

If you need browser access today, the workaround is to proxy the request through your own server (your backend calls this API with X-API-Key and forwards the response to the browser).

CORS is on the roadmap as an opt-in, env-driven feature mirroring the HMAC and IP-whitelist patterns (per-key FORMIE_API_ALLOWED_ORIGINS[_*] env var). It will be implemented when the first browser consumer's requirements are known. Open an issue if you need it sooner.

Field metadata — units & notes

Field metadata returned by /forms/{id|handle} reflects Formie's own settings. A few values where the unit isn't obvious:

Field type Setting Unit
FileUpload sizeLimit, sizeMinLimit Megabytes (MB)
FileUpload limitFiles Number of files
Signature penWeight Pixels
SingleLineText / MultiLineText min, max See minType / maxType (characters or words)
Date minDateOffsetNumber, maxDateOffsetNumber See minDateOffsetType / maxDateOffsetType (days, weeks, months, years)
Date minYearRange, maxYearRange Years offset from current year (e.g. -100 = 100 years ago, 100 = 100 years ahead)

All datetime values across submission and field-value responses are ISO 8601 in the site timezone.

Submission filtering (default behaviour)

Both production (/api/v1/formie/submissions) and test (/api/test/formie/submissions) endpoints automatically exclude:

  • Incomplete submissions — abandoned drafts that were never finalised (isIncomplete = true)
  • Spam submissions — anything Akismet or another captcha integration flagged (isSpam = true)

This is hardcoded — the API contract is "completed, non-spam form submissions". There is currently no opt-in flag to include drafts or spam. If your integration needs them, open an issue describing the use case.

Submission query parameters

GET /api/v1/formie/submissions accepts:

Param Description
formHandle / formId Restrict to a single form
status Submission status (default live; pass all for any status)
limit / offset Pagination (defaults 100 / 0)
dateFrom / dateTo Filter by dateCreated. Accepts YYYY-MM-DD, YYYY-MM-DD HH:MM:SS, or ISO 8601. Use dateFrom for incremental sync — pull only submissions created since your last run instead of re-fetching everything.
fields Sparse fieldset — comma-separated field handles (e.g. fields=rating,email,name). Each submission's fields map then contains only those handles. The server skips value resolution for unrequested fields, so a narrow selection is faster on large pulls. Omit for all fields. Unknown handles are ignored. Top-level keys (id, dateCreated, status, …) are always returned.
# Incremental sync: only new submissions since the last run, just the fields we consume
curl -H "X-API-Key: your-api-key" \
     "https://yoursite.com/api/v1/formie/submissions?formHandle=productRating&dateFrom=2026-06-09T14:00:00&fields=rating,email"

When signing is enabled, remember to sort query params alphabetically before signing (see HMAC Request Signing).

REST API Endpoints

Versioning

The plugin uses two independent version axes — they don't move together:

Version Tracks Bumps when
Plugin (e.g. 3.3.0) This package's release Every feature/fix. Plugin major aligns to the Formie major (Formie 3.x → plugin 3.x.x; Formie 4 → plugin 4.0.0)
API URL (/api/v1/) The wire-format contract consumers integrate against Only on breaking contract changes (removed/renamed fields, changed semantics). Plugin major bumps do not automatically bump the API URL

Branch strategy. When Formie ships a new major, main tracks the new major (e.g. plugin 4.x for Formie 4), and the previous Formie major lives on a maintenance branch (e.g. 3.x) for security and critical fixes during a deprecation window. The /api/vN/ URL stays the same across these branches as long as the wire format is unchanged — so an integrator targeting /api/v1/ keeps working whether the site is on plugin 3.x or 4.x.

Production Endpoints

Method Endpoint Description
GET /api/v1/formie/forms List all forms
GET /api/v1/formie/forms/{id} Get form by ID
GET /api/v1/formie/forms/{handle} Get form by handle
GET /api/v1/formie/submissions List submissions
GET /api/v1/formie/submissions/{id} Get submission details

Test Endpoints (Development)

Method Endpoint Description
GET /api/test/formie/forms Test forms endpoint
GET /api/test/formie/submissions Test submissions endpoint
GET /api/test/formie/auth Test authentication

Example Usage

REST API Examples

# List all forms
curl -H "X-API-Key: your-api-key" \
     https://yoursite.com/api/v1/formie/forms

# Get specific form by handle
curl -H "X-API-Key: your-api-key" \
     https://yoursite.com/api/v1/formie/forms/contactForm

# Get form submissions
curl -H "X-API-Key: your-api-key" \
     "https://yoursite.com/api/v1/formie/submissions?form=contactForm&limit=10"

API Documentation

  • postman/ — Postman collection + environment templates with auto-HMAC pre-request script
  • .env.example — Environment variable configuration
  • In-CP Test tab — live API tester at Settings → Plugins → Formie REST API → Test

API Key Permissions

CP-managed keys carry per-key capabilities (allowed forms, submissions toggle) instead of fixed tiers — see API Keys (CP-managed). The fixed tiers below apply only to the legacy environment-variable keys:

Key Type (legacy env) Permissions Rate Limit
Primary Read forms, Read submissions 1000/hour
Limited Read forms only 100/hour
Test Full access (dev mode only) Unlimited

Two permission systems — don't confuse them

The plugin has two separate permission systems with different audiences. Both are called "permissions" but they don't overlap.

Craft user permissions API key scopes
Audience Logged-in CP users (humans) External clients with X-API-Key
Where defined Code → EVENT_REGISTER_PERMISSIONS Per key in the CP (or env-var tier for legacy keys)
Where assigned CP → Settings → Users → User Groups → "Formie REST API" section CP → Formie REST API → API Keys
What's enforced Manage settings, Manage/Create/Edit/Revoke API keys, View/Download system logs read_forms, read_submissions + per-key form scoping (gates each REST endpoint)
Failure status 403 from CP redirect / login 401/403 from API

In practice:

  • A CP user with Manage API keys can see the key list; creating, editing, and revoking each require their own nested permission.
  • An external API consumer's access is determined entirely by the capabilities saved on their key: which forms it may read, whether it may read submissions at all, and whether requests must be signed.

If you give a partner a forms-only key thinking they can't read submissions — they can't. The scope is enforced server-side.

Security Features

  • API key validation (per-key permission scopes: read_forms, read_submissions)
  • Hashed key storage — CP-managed keys are stored as HMAC-SHA256 hashes keyed by Craft's securityKey; the plaintext is shown once at creation and never persisted
  • Encrypted signing secrets — each key's HMAC secret is stored encrypted at rest (Security::encryptByKey()), recoverable only by the server for signature validation
  • Per-key form scoping — a scoped key cannot list, read, or query forms outside its allowlist (same 403 whether or not a probed handle exists)
  • HMAC request signing (per-key toggle, default on — replay protection, integrity, leaked-key mitigation)
  • IP whitelist (optional, opt-in per key — IPv4 + IPv6 + CIDR)
  • Rate limiting per key (atomic, mutex-serialized counter)
  • Access logging (key, endpoint, IP, user-agent, response code)
  • Development-mode restrictions (test endpoints + legacy test key only register when devMode = true)
  • Not yet: CORS for browser consumers — see CORS support — not currently implemented

Operational hardening (Craft-level, not plugin-level)

A couple of Craft framework settings worth knowing about — the plugin doesn't control these, but they affect API responses:

  • X-Powered-By: Craft CMS response header. Craft sends this by default. Some security-conscious deployments prefer to suppress it (defence in depth — don't tell unfamiliar attackers what stack to research). Disable it in config/general.php:

    return GeneralConfig::create()
        ->sendPoweredByHeader(false);
  • Server: nginx / similar. Set at the web server level, not Craft. Strip via server_tokens off; (nginx) or equivalent.

  • trustedHosts / proxy headers. If you put the API behind a CDN or reverse proxy and use the IP whitelist feature, configure Craft's trustedHosts correctly so getUserIP() returns the real client IP, not the proxy. See Craft docs.

Control Panel

The plugin has its own section in the CP nav (Formie REST API):

  • API Keys — create, scope, enable/disable, and revoke per-consumer keys (see API Keys (CP-managed))
  • Settings → General — plugin name and log level (overridable via config/formie-rest-api.php)
  • Settings → Interface — date/time display preferences inherited from LindemannRock Base
  • Settings → Test — live API tester: pick a configured key or paste one created in the CP (with its signing secret), choose an endpoint, set optional filters (form handle, date range, sparse fields, limit/offset), and view the response status, headers, body, and the equivalent curl command. Available regardless of devMode; test endpoints (/api/test/formie/*) only resolve when devMode = true
  • Logs — request/access log viewer (LindemannRock Logging Library)

Control Panel settings are validated and saved by active section, and persist in the plugin's own database table.

Support

License

This plugin is licensed under the Craft License. See LICENSE.md for details.

Developed by LindemannRock

Built for use with Formie by Verbb

lindemannrock/craft-formie-rest-api 适用场景与选型建议

lindemannrock/craft-formie-rest-api 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 109 次下载、GitHub Stars 达 0, 最近一次更新时间为 2025 年 10 月 16 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「api」 「cms」 「REST API」 「Craft」 「craftcms」 「craft-plugin」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 lindemannrock/craft-formie-rest-api 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 lindemannrock/craft-formie-rest-api 我们能提供哪些服务?
定制开发 / 二次开发

基于 lindemannrock/craft-formie-rest-api 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 109
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 29
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: proprietary
  • 更新时间: 2025-10-16