Skip to content

Routing

Routing defines the mapping between URLs and handlers. HTTP and WebSocket protocols are supported.

Basic configuration

json
"http": {
    "routes": {
        "/": {
            "GET": { "file": "handlers/libindex.so", "function": "index" }
        },
        "/api/users": {
            "GET": { "file": "handlers/libapi.so", "function": "get_users" },
            "POST": { "file": "handlers/libapi.so", "function": "create_user" }
        }
    }
}

Dynamic parameters

Route parameters are specified in the format {name|pattern}:

json
"routes": {
    "/api/users/{id|\\d+}": {
        "GET": { "file": "handlers/libapi.so", "function": "get_user" },
        "PUT": { "file": "handlers/libapi.so", "function": "update_user" },
        "DELETE": { "file": "handlers/libapi.so", "function": "delete_user" }
    },
    "/api/posts/{slug|[a-z0-9-]+}": {
        "GET": { "file": "handlers/libapi.so", "function": "get_post" }
    }
}

Extracting parameters in a handler

c
#include "query.h"

void get_user(httpctx_t* ctx) {
    int ok = 0;
    const char* id = query_param_char(ctx->request, "id", &ok);
    // id = "123" for request /api/users/123

    if (!ok) {
        // handle error
        return;
    }

    // Convert to number
    int user_id = atoi(id);

    // Find user...
}

void get_post(httpctx_t* ctx) {
    int ok = 0;
    const char* slug = query_param_char(ctx->request, "slug", &ok);
    // slug = "my-first-post" for /api/posts/my-first-post
}

Regular expressions

For complex patterns, use full regular expressions:

json
"routes": {
    "^/managers/{name|[a-z]+}$": {
        "GET": { "file": "handlers/libapi.so", "function": "get_manager" }
    },
    "^/files/(.+)\\.pdf$": {
        "GET": { "file": "handlers/libfiles.so", "function": "get_pdf" }
    }
}

HTTP methods

Supported methods:

MethodDescription
GETRetrieve a resource
POSTCreate a resource
PUTFull update
PATCHPartial update
DELETEDelete a resource
OPTIONSCORS preflight requests
json
"/api/resource": {
    "GET": { "file": "handlers/lib.so", "function": "get" },
    "POST": { "file": "handlers/lib.so", "function": "create" },
    "PUT": { "file": "handlers/lib.so", "function": "replace" },
    "PATCH": { "file": "handlers/lib.so", "function": "update" },
    "DELETE": { "file": "handlers/lib.so", "function": "delete" },
    "OPTIONS": { "file": "handlers/lib.so", "function": "options" }
}

Middleware

Middleware are executed before the handler:

Global middleware

json
"http": {
    "middlewares": ["middleware_auth", "middleware_log"],
    "routes": {
        // All routes pass through middleware_auth and middleware_log
    }
}

Middleware in a handler

c
#include "httpmiddlewares.h"

void protected_handler(httpctx_t* ctx) {
    // Authentication check
    middleware(
        middleware_http_auth(ctx)
    )

    // This code will only execute if the user is authorized
    user_t* user = httpctx_get_user(ctx);
    ctx->response->send_data(ctx->response, "Welcome!");
}

Rate Limiting

Request rate limiting:

json
"ratelimits": {
    "default": { "burst": 15, "rate": 15 },
    "strict": { "burst": 1, "rate": 0 },
    "api": { "burst": 100, "rate": 100 }
},
"http": {
    "ratelimit": "default",
    "routes": {
        "/api/users": {
            "GET": {
                "file": "handlers/libapi.so",
                "function": "get_users",
                "ratelimit": "api"
            },
            "POST": {
                "file": "handlers/libapi.so",
                "function": "create_user",
                "ratelimit": "strict"
            }
        }
    }
}

Redirects

json
"http": {
    "redirects": {
        "/old-page": "/new-page",
        "/blog/(\\d+)": "/posts/{1}",
        "/user/(.*)": "/profile/{1}"
    }
}

Capture groups are available via {1}, {2}, etc.

WebSocket routes

WebSocket uses a similar syntax:

json
"websockets": {
    "default": {
        "file": "handlers/libws.so",
        "function": "ws_default"
    },
    "routes": {
        "/ws": {
            "GET": { "file": "handlers/libws.so", "function": "ws_connect" },
            "POST": { "file": "handlers/libws.so", "function": "ws_message" }
        },
        "/ws/chat/{room|\\d+}": {
            "GET": { "file": "handlers/libws.so", "function": "ws_join_room" },
            "POST": { "file": "handlers/libws.so", "function": "ws_send_message" }
        }
    }
}

WebSocket handler

c
#include "websockets.h"

void ws_connect(wsctx_t* ctx) {
    ctx->response->send_text(ctx->response, "Connected");
}

void ws_message(wsctx_t* ctx) {
    char* message = websocketsrequest_payload(ctx->request->protocol);

    if (message) {
        ctx->response->send_text(ctx->response, message);
        free(message);
    }
}

Configuration examples

REST API

json
"http": {
    "middlewares": ["middleware_cors"],
    "routes": {
        "/api/v1/users": {
            "GET": { "file": "handlers/libusers.so", "function": "list" },
            "POST": { "file": "handlers/libusers.so", "function": "create" }
        },
        "/api/v1/users/{id|\\d+}": {
            "GET": { "file": "handlers/libusers.so", "function": "show" },
            "PUT": { "file": "handlers/libusers.so", "function": "update" },
            "DELETE": { "file": "handlers/libusers.so", "function": "delete" }
        },
        "/api/v1/auth/login": {
            "POST": { "file": "handlers/libauth.so", "function": "login" }
        },
        "/api/v1/auth/logout": {
            "POST": { "file": "handlers/libauth.so", "function": "logout" }
        }
    }
}

Mixed site (pages + API)

json
"http": {
    "routes": {
        "/": {
            "GET": { "file": "handlers/libpages.so", "function": "home" }
        },
        "/about": {
            "GET": { "file": "handlers/libpages.so", "function": "about" }
        },
        "/api/data": {
            "GET": { "file": "handlers/libapi.so", "function": "get_data" }
        }
    }
}

Released under the MIT License.