Skip to content

Quick Start

Lee Yunjin edited this page Oct 7, 2026 · 1 revision

Quick Start

This page builds a small JSON API with a path parameter, middleware, a database and graceful shutdown. It assumes CWIST is installed (see Installation) and pkg-config can find cwist.pc.

1. Hello world

/* main.c */
#include <cwist/app.h>

static void hello(cwist_http_request *req, cwist_http_response *res) {
    CWIST_UNUSED(req);
    cwist_sstring_assign(res->body, "Hello from CWIST!");
}

int main(void) {
    cwist_app *app = cwist_app_create();
    cwist_app_get(app, "/", hello);
    cwist_app_listen(app, 8080);
    cwist_app_destroy(app);
    return 0;
}
cc -std=c17 -o server main.c $(pkg-config --cflags --libs --static cwist)
./server &
curl http://127.0.0.1:8080/

cwist_app_listen() blocks until SIGTERM, SIGINT or cwist_shutdown_request(). By default it forks one worker process per online CPU core and serves with the reactor; see Server Modes and Configuration and Environment.

2. Path parameters, query strings and JSON

#include <cwist/app.h>
#include <cwist/core/utils/json_builder.h>

static void get_user(cwist_http_request *req, cwist_http_response *res) {
    const char *id = cwist_query_map_get(req->path_params, "id");
    const char *fields = cwist_query_map_get(req->query_params, "fields"); /* NULL if absent */

    cwist_json_builder *jb = cwist_json_builder_create();
    cwist_json_begin_object(jb);
    cwist_json_add_string(jb, "id", id);
    cwist_json_add_string(jb, "fields", fields ? fields : "all");
    cwist_json_end_object(jb);

    cwist_http_header_add(&res->headers, "Content-Type", "application/json");
    cwist_sstring_assign(res->body, cwist_json_get_raw(jb));
    cwist_json_builder_destroy(jb);
}

Register it with cwist_app_get(app, "/users/:id", get_user);. A request to /users/7?fields=name returns {"id":"7","fields":"name"}. See Routing, Query Maps and JSON.

3. Middleware

#include <cwist/sys/app/middleware.h>

cwist_app_use(app, cwist_mw_request_id(NULL));          /* X-Request-Id */
cwist_app_use(app, cwist_mw_access_log(CWIST_LOG_COMBINED));
cwist_app_use(app, cwist_mw_cors());

Middleware runs in registration order around every route. See Middleware for the built-ins and for writing your own.

4. A database

cwist_app_use_db(app, "app.db");   /* SQLite; ":memory:" also works */

static void list_posts(cwist_http_request *req, cwist_http_response *res) {
    cJSON *rows = NULL;
    cwist_error_t err = cwist_db_query(req->db, "SELECT id, title FROM posts", &rows);
    if (!cwist_error_is_ok(&err)) {
        res->status_code = CWIST_HTTP_INTERNAL_ERROR;
        return;
    }
    char *json = cJSON_PrintUnformatted(rows);
    cwist_sstring_assign(res->body, json);
    cJSON_free(json);
    cJSON_Delete(rows);
}

Every request carries the app's database handle in req->db. See Database for pools, migrations, Nuke DB and encryption, and ORM for the query helpers.

5. TLS and newer protocols

cwist_app_use_https(app, "server.crt", "server.key");
cwist_app_use_pqc_layer(app, true);   /* TLS 1.3 only, X25519MLKEM768 first */
cwist_app_use_https2(app, true);      /* negotiate h2 via ALPN */
cwist_app_use_https3(app, true);      /* serve HTTP/3 on the same port over UDP */

See HTTPS and TLS, HTTP/2 and HTTP/3 and QUIC.

6. Observability and shutdown

cwist_app_enable_metrics(app);   /* GET /metrics in Prometheus text format */
cwist_app_enable_healthz(app);   /* GET /healthz, /live, /ready */

Send SIGTERM (or call cwist_shutdown_request() from any thread) and cwist_app_listen() stops accepting, drains in-flight requests for CWIST_DRAIN_TIMEOUT seconds (default 5) and returns. See Graceful Shutdown and Metrics and Health.

7. Test without a socket

#include <cwist/sys/app/test_client.h>

cwist_test_client *tc = cwist_test_client_create(app);
cwist_http_response *res = cwist_test_client_get(tc, "/users/7");
CWIST_ASSERT_STATUS(res, 200);
CWIST_ASSERT_BODY_CONTAINS(res, "\"id\":\"7\"");
cwist_http_response_destroy(res);
cwist_test_client_destroy(tc);

See Testing.

Next steps

Clone this wiki locally