Lua API¶
gqlc render generates every call below. Read this page if you write the nginx configuration by
hand, or if you already have an init_by_lua ([nginx] init = false).
| Function | Phase | |
|---|---|---|
require("gqlgw").init(opts) |
init_by_lua |
loads libgql.so and the schema, configures the cache and the metrics |
require("gqlgw").serve() |
content_by_lua of the GraphQL location |
handles one GraphQL request |
require("gqlgw").log() |
log_by_lua of the GraphQL location |
request metrics, $gql_* log variables |
require("gqlgw").metrics() |
content_by_lua |
Prometheus text |
require("gqlgw").purge() |
content_by_lua |
POST ?source=<name>: purge a source |
init(opts)¶
opts is either the path of the gqlgw.lua written by gqlc render (recommended), or a table:
require("gqlgw").init("/tmp/gqlgw/gqlgw.lua")
-- or, by hand:
require("gqlgw").init({
schema = "/etc/gqlgw/schema.gqlb", -- routes: /etc/gqlgw/schema.routes.lua
lib = "/usr/local/lib/gqlgw/libgql.so",
introspection = true,
debug_headers = false,
strict_path_args = true,
cache = {
shm = "gqlgw_cache", -- a lua_shared_dict; omit for no L2
l2_ttl = 5,
memcached = { host = "127.0.0.1", port = 11211, timeout = 50, pool_size = 64 },
private_header = "Authorization",
},
metrics = { shm = "gqlgw_metrics", exclude_operations = { "^IntrospectionQuery$" } },
max_query_bytes = 65536, max_depth = 32, max_rounds = 16, max_fetches = 1000,
})
| Key | Default | gqlgw.toml equivalent |
|---|---|---|
schema, routes, lib |
required, <schema>.routes.lua, system library |
[gateway] |
introspection, debug_headers, strict_path_args |
true, false, true |
[gateway] |
max_body_bytes |
1048576 |
[gateway] max_body |
max_query_bytes, max_tokens, max_depth, max_errors |
65536, 20000, 32, 100 | [limits] |
max_plan_fields, max_memory |
20000, 16 MiB | [limits] |
max_rounds, max_fetches, concurrency, max_response_bytes |
16, 1000, 64, 16 MiB | [limits] |
cache_negative_ttl |
5 | [cache] negative_ttl |
cache |
none | [cache], [cache.memcached] |
metrics |
none (no metrics) | [metrics] |
sources |
not checked | the list of [sources.*]: init fails if the schema uses another one |
sources_prefix |
"/_src/" |
— |
Hand-written locations¶
Without gqlc render, you write the locations yourself. The contract:
- the GraphQL location declares
$gqlgw_path(set $gqlgw_path "";); - one location
/_src/<source>/per source, which sends$gqlgw_pathupstream as is; - these locations must be
internal, clearAccept-Encoding, and choose the client headers they forward.
# gqlgw: hand-written OpenResty configuration.
#
# The recommended way is gqlgw.toml + `gqlc render`, which generates the
# equivalent of this file (see README.md and examples/docker/gqlgw.toml).
# This example shows what a configuration without it looks like:
#
# gqlc compile -o /etc/gqlgw/schema.gqlb schema.graphqls # also writes schema.routes.lua
# openresty -c /etc/gqlgw/nginx.conf
worker_processes auto;
error_log logs/error.log warn;
events {
worker_connections 4096;
}
http {
lua_package_path "/opt/gqlgw/lua/?.lua;/opt/gqlgw/lua/?/init.lua;;";
# L2 cache (per node). Optional but recommended.
lua_shared_dict gqlgw_cache 128m;
init_by_lua_block {
require("gqlgw").init({
schema = "/etc/gqlgw/schema.gqlb", -- routes: /etc/gqlgw/schema.routes.lua
lib = "/opt/gqlgw/lib/libgql.so",
cache = {
shm = "gqlgw_cache",
l2_ttl = 5, -- seconds, cap for shm entries
memcached = { host = "127.0.0.1", port = 11211, timeout = 50, pool_size = 64 },
private_header = "Authorization", -- identity of @cache(scope: "PRIVATE")
},
introspection = true,
max_query_bytes = 65536,
max_depth = 32,
max_fetches = 1000, -- upstream requests per GraphQL request
max_rounds = 16, -- depth of fetch chains
strict_path_args = true, -- reject "/", "." and ".." in path segments
debug_headers = false, -- X-GQL-Rounds, X-GQL-Subrequests, X-GQL-Cache
})
}
upstream users_svc {
server 10.0.0.10:8080;
keepalive 64;
}
upstream posts_svc {
server 10.0.0.20:8080;
keepalive 64;
}
log_format gql '$remote_addr "$request" $status $request_time '
'rounds=$gql_rounds sub=$gql_subrequests cache=$gql_cache';
server {
listen 8080;
location = /graphql {
set $gqlgw_path ""; # declares the variable used by the /_src/ locations
set $gql_rounds "-";
set $gql_subrequests "-";
set $gql_cache "-";
client_max_body_size 1m;
client_body_buffer_size 1m; # keep request bodies in memory
access_log logs/graphql.log gql;
content_by_lua_block { require("gqlgw").serve() }
log_by_lua_block { require("gqlgw").log() }
}
# One internal location per @fetch(source: "...").
# The upstream path rendered from the SDL templates arrives in $gqlgw_path,
# already URI-escaped: nginx sends it verbatim.
location /_src/users/ {
internal;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Accept-Encoding ""; # subrequests inherit client headers
proxy_set_header Host users.internal;
proxy_connect_timeout 1s;
proxy_read_timeout 3s;
proxy_pass http://users_svc/api/v1$gqlgw_path;
}
location /_src/posts/ {
internal;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Accept-Encoding "";
proxy_set_header Host posts.internal;
proxy_connect_timeout 1s;
proxy_read_timeout 3s;
proxy_pass http://posts_svc$gqlgw_path;
}
# Cache purge (one source): curl -X POST 'http://127.0.0.1:8080/_gqlgw/purge?source=users'
location = /_gqlgw/purge {
allow 127.0.0.1;
deny all;
content_by_lua_block { require("gqlgw").purge() }
}
}
}
The TOML configuration remains the recommended way: it generates these locations with their
safeguards (allowlist, Host, TLS, Content-Type of bodies, upstream timing).