Repository navigation
Guide CRUD Blog
This guide builds a small JSON blog API backed by CWIST's SQLite integration. It intentionally keeps SQL explicit so that the request, validation, and response boundaries are easy to inspect.
#include <cwist/sys/app/app.h>
#include <cwist/core/db/sql.h>
#include <cwist/net/http/http.h>
static void list_posts(cwist_http_request *req, cwist_http_response *res) {
(void)req;
/* Query through your existing cwist_db handle and serialize rows as JSON. */
cwist_sstring_assign(res->body, "[]");
cwist_http_header_add(&res->headers, "Content-Type", "application/json");
}
int main(void) {
cwist_app *app = cwist_app_create();
cwist_app_use_db(app, "blog.db");
cwist_app_get(app, "/posts", list_posts);
int rc = cwist_app_listen(app, 8080);
cwist_app_destroy(app);
return rc;
}Create the table during application startup with a migration:
CREATE TABLE IF NOT EXISTS posts (
id INTEGER PRIMARY KEY,
title TEXT NOT NULL,
body TEXT NOT NULL,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);Use one handler per operation and parameterized routes for a post identifier.
| Method | Path | Action |
|---|---|---|
| GET | /posts |
List posts |
| GET | /posts/:id |
Read one post |
| POST | /posts |
Create a post |
| PATCH | /posts/:id |
Update title/body |
| DELETE | /posts/:id |
Delete a post |
cwist_app_get(app, "/posts", list_posts);
cwist_app_get(app, "/posts/:id", get_post);
cwist_app_post(app, "/posts", create_post);
cwist_app_patch(app, "/posts/:id", update_post);
cwist_app_delete(app, "/posts/:id", delete_post);Read the route parameter with cwist_query_map_get(req->path_params, "id").
Treat it as untrusted input: parse the whole integer, reject non-positive values,
and bind it to a prepared statement. Never interpolate JSON fields or route
parameters into SQL.
Require title and body strings on create. Return 201 Created after an
insert, 204 No Content after a successful delete, 400 Bad Request for a
malformed request, and 404 Not Found when an id has no row. Always set
Content-Type: application/json for JSON responses.
For browser clients, add CORS only for the origins you control and use CSRF middleware when cookie authentication is enabled. The existing WAF middleware is a useful additional boundary, but it does not replace parameterized SQL.
Use cwist_test_client to dispatch requests without binding a port. Cover the
entire lifecycle: create, list, fetch, patch, delete, then fetch again and
assert 404. Run the same tests against :memory: for fast isolation and a
temporary on-disk database to exercise migrations.
Source: docs/tutorials/blog-crud.md on dev. Edit the source file in the repository; this page is regenerated from it.
CWIST wiki, written against the dev branch of c4punks/CWIST. Pages marked "Source:" are generated from files under docs/; fix those in the repository. Questions: Discord.
Getting started
- Installation
- Quick Start
- Linking
- Server Modes
- Configuration and Environment
- Project CLI
- Tutorial / Korean
- Examples and Tutorials
Guides
Core
- API Reference
- Application
- Routing
- Middleware
- Requests and Responses
- Async Handlers
- Streaming Responses
- Error Handling
- Graceful Shutdown
- Multiport
Protocols
- HTTPS and TLS
- HTTP/2
- HTTP/3 and QUIC
- WebTransport
- WebSocket
- Server-Sent Events
- gRPC Server
- gRPC Client
- Protobuf and Codegen
- GraphQL
- WebRTC DataChannels
- HTTP Clients
Web features
- Static Files and Assets
- Big Dumb Reply Cache
- Compression
- Cookies
- Sessions and Flash
- Query Maps
- Multipart Uploads
- HTML Components
- Templates
- JSON
- Validation
- OpenAPI
Security
Data
Runtime
- Memory Management
- Full GC
- Async GC Ownership
- SString
- Reactor and I/O
- Metrics and Health
- Logging
- Testing
Platforms
Performance notes
- Benchmark Methodology
- C1M File Limits
- Reactor Fairness
- Cooperative Queuing
- Classic Pool Starvation
- Reactor Wakeup
- wrk Dual Histogram
- FIXED Endpoint Cache
- ADR-0001
- Durable Queue Gate
- Mux References
Project