APIs (REST, GraphQL, gRPC, SOAP)
🧪 Experimental — part of the
corelayer (kyrux/core), labeled "experimental" by the framework's ownUSE.md. The API may change between versions without notice.
All four protocols live as adapters (core/adapters/...) on top of
core.Core — a new orchestrator, parallel to the traditional bootstrap,
with its own lifecycle (Init → Configure → Start → Shutdown).
import "kyrux/core"
c := core.New()
// ... register REST/GraphQL/gRPC/SOAP ...
c.Run() // Start on every activated module
defer c.Shutdown() // Shutdown in reverse order
Two ways to activate a module:
- Sugar — ready-made namespaces (
core.API.REST(addr),core.Auth.OAuth2(...)) for lightweight/stdlib-only protocols. core.UseModule[T]— generic, used by the protocols with heavy dependencies (GraphQL, gRPC), which don't get sugar on purpose, so those dependencies aren't pulled in for people who don't use them.
None of the four read environment variables — address/port/options are always passed as Go arguments in your own code.
REST — core/adapters/restapi
Not a new protocol — it's the same core/router.Router as always, just
running on core.Core's own *http.Server/port, orchestrated by
Run()/Shutdown() instead of fw.Run().
import "kyrux/core"
c := core.New()
r, err := c.API.REST(":8080") // *router.Router — same API as always
r.Handle("GET /ping", func(ctx *router.Context) {
ctx.JSON(200, map[string]string{"pong": "kyrux"})
})
c.Run()
GraphQL — core/adapters/apigraphql
No sugar (heavy dependency — graphql-go brings its own parser and
template engine). You build the schema with
graphql-go/graphql's own API
and hand the finished schema to the adapter.
import (
"github.com/graphql-go/graphql"
"kyrux/core"
"kyrux/core/adapters/apigraphql"
)
queryType := graphql.NewObject(graphql.ObjectConfig{
Name: "Query",
Fields: graphql.Fields{
"version": &graphql.Field{
Type: graphql.String,
Resolve: func(p graphql.ResolveParams) (any, error) {
return "kyrux-core", nil
},
},
},
})
schema, _ := graphql.NewSchema(graphql.SchemaConfig{Query: queryType})
c := core.New()
_, err := core.UseModule[*graphql.Schema](c, apigraphql.New(":8081", schema), "api.graphql.principal")
c.Run()
Serves POST / with a {"query": "..."} body. The GraphiQL IDE is
enabled by default, with no toggle to disable it. Runs on its own
*http.Server — it's not mounted on the REST router.
gRPC — core/adapters/apigrpc
Also no sugar (HTTP/2 + protobuf runtime). The adapter only hosts the
*grpc.Server — you generate your service code from a .proto (protoc
protoc-gen-go+protoc-gen-go-grpc) and register it the normal gRPC way.
import (
"kyrux/core"
"kyrux/core/adapters/apigrpc"
)
c := core.New()
srv, err := core.UseModule[*grpc.Server](c, apigrpc.New(":9090"), "api.grpc.principal")
pb.RegisterGreeterServer(srv, &myImplementation{}) // standard gRPC registration
// gRPC and REST coexist on the same Core, different ports:
r, _ := c.API.REST(":8080")
c.Run()
Shutdown calls GracefulStop() with no timeout — add your own watchdog
if you need a time limit.
SOAP — core/soap + core/adapters/soapclient
A hand-rolled implementation (encoding/xml + net/http, no third-party
library — Go's SOAP ecosystem is weak). Aimed at cases like legacy/
government webservices.
Server — not a separate port, it's an http.Handler you mount on the
REST router:
import (
"kyrux/core"
"kyrux/core/soap"
)
server := soap.NewServer()
server.Handle("SumRequest", func(ctx context.Context, requestXML []byte) ([]byte, error) {
// parse requestXML, build the response
return responseXML, nil
})
c := core.New()
r, _ := c.API.REST(":8080")
r.HandlePrefix("/soap", server) // same pattern used to mount Prometheus metrics
c.Run()
Routing is by the root element name inside <Body> — pragmatic, not a
"formal" WSDL/SOAPAction-based dispatch.
Client, raw or via Core sugar:
// Raw:
client := soap.NewClient(endpoint, httpClient)
// Sugar (zero cost — it's pure stdlib):
client, err := c.API.SOAPClient("principal", endpoint)
var resp sumResponse
err = client.Call(ctx, "Sum", sumRequest{A: 4, B: 5}, &resp)
// a server-side error becomes a *soap.Fault (errors.As)
None of them are read from .env
Like NoSQL, none of the four protocols follow an API_REST_ADDR-style
convention — address, schema, grpc.Server options: everything is passed
as a Go argument in your own code, when you build the core.Core.