Repository navigation
PerfectTemplate 4.0.0 replaces the 2017 single-file starter (3.0.0) with a reference app for the Swift 6 Perfect stack: a small "Posts" JSON API and browser UI built on Perfect-NIO, Perfect-CRUD, Perfect-MySQL and Perfect-Logger, all pinned to released versions. Copy it as the starting point for a new service. (#25)
Requirements
- Swift 6.2 or later (
swift-tools-version: 6.2), macOS 12 or later, or Linux. CI covers Swift 6.2 and 6.4 on Ubuntu 24.04 and Xcode 26.0.1 and 26.6 on macOS, in debug and release, with warnings as errors. - The MySQL client library:
mysql-clientfrom Homebrew on macOS,libmysqlclient-devon Debian/Ubuntu. - A MySQL 5.7.8+ or MariaDB 10.1+ server. Tested with MySQL 8.4 and MariaDB 11.8.
- Dependencies: Perfect-NIO 0.1.0, Perfect-CRUD 3.1.0, Perfect-MySQL 4.0.0, Perfect-Logger 4.0.0, swift-nio 2.65.0+.
Package.resolvedis committed.
What's in it
- JSON API:
GET /api/health,GET|POST /api/posts,GET|PUT|DELETE /api/posts/:id. UUID ids, ISO 8601createdAt, newest first. - Web UI:
/and/posts(create, edit, delete), plus/static/*. GET and HEAD. - Configuration from the environment:
PORT(9191),DB_HOST(127.0.0.1),DB_PORT(13306),DB_NAME,DB_USER,DB_PASS,WEBROOT,LOG_FILE,LOG_LEVEL. The defaults match the README's container quick start, so a bareswift runnever connects to a MySQL on 3306. A bad port exits with a clear message. - Schema: the table is created on the first successful connection, so the server can start before MySQL is up.
- Logging through Perfect-Logger to the console and a file, with CRUD's SQL at debug level.
- CI and a smoke test:
Scripts/smoke-test.shchecks the pages, health, a full create/list/get/update/delete round trip and the error cases;--no-dbchecks the outage behaviour.
Behaviour worth knowing
- Database work runs on its own 16-thread pool, not on Swift's cooperative thread pool. A slow or unreachable database can't stop the server answering health checks and pages.
- Timeouts: connect 5 s; client read and write 30 s; server-side lock waits and statement time 20 s, so MySQL gives up before the client does. A request that hits them answers 503 "Database busy." and, because each statement autocommits, nothing is written. On MariaDB the statement limit applies to every statement; on MySQL only to SELECTs.
- Outages: a failed connect answers 503 "Database unavailable.", and for 2 s afterwards requests fail fast instead of queuing for another connect.
- Validation: POST and PUT need
Content-Type: application/json(415). Titles are 1–200 characters on one line, without control, line-separator or bidi-override characters, and at most 2,000 code points; bodies are at most 20,000 characters and 100,000 code points. Request bodies are capped at 256 KiB (413). PUT changes only the fields sent. - Errors map to 400, 404, 413, 415, 503 or a generic 500; internal error text only goes to the log.
- The server listens on all interfaces (Perfect-NIO's default).
Known limitations
- "Database busy" is recognised from the MySQL error text, because Perfect-MySQL 4.0.0's errors don't carry the error number. A server set to non-English messages answers 500 for those timeouts instead of 503.
- A table dropped while the app runs needs an app restart.
- A SELECT that legitimately takes more than 20 s answers 503.
- One connection per request; for a busy service, look at CRUD's connection pool (see the README).
Upgrading from 3.0.0
There is no upgrade path: 4.0.0 is a new app on a new stack. The old Perfect 3 starter is on the legacy branch. README.zh_CN.md was removed rather than left out of date.