Skip to content

Latest commit

 

History

History
146 lines (116 loc) · 5.85 KB

File metadata and controls

146 lines (116 loc) · 5.85 KB

API

APIs are endpoint declarations. A page declares the exported same-package Go symbol, HTTP method, and endpoint path in .gwdk; normal Go owns the behavior.

package api

api Health GET "/api/health" error "/errors/api-health.html"

Supported methods are GET, POST, PUT, PATCH, and DELETE. The route must be a quoted absolute route path. Old api health { ... } blocks are rejected with a migration diagnostic.

Generated apps bind same-package Go handlers for the first API slice. api Health GET "/api/health" maps exactly to exported Go function Health in a same-package .go file or default go {} block when the function has signature:

func Health(context.Context, *http.Request) (response.Response, error)

Bound API handlers return runtime/response.Response. In development/default mode, missing or unsupported handlers are not build errors; generated apps return HTTP 501 for those routes with a clear message.

In production mode, explicitly declared APIs must bind to supported same-package Go handlers. Missing or unsupported handlers fail the build unless Build.AllowMissingBackend or --allow-missing-backend is set to intentionally generate HTTP 501 stubs during a migration.

Feature packages that declare API handlers may import stable public GOWDK packages such as runtime/response and runtime/app; they must not import generated app packages, generated gowdkapp output, generated cmd/server code, or build output directories. Generated app source imports feature packages, never the other way around.

Handler Helpers

API handlers can use github.com/cssbruno/gowdk/addons/api for the current public helper contract:

package api

import (
	"context"
	"net/http"

	gowdkapi "github.com/cssbruno/gowdk/addons/api"
	"github.com/cssbruno/gowdk/runtime/response"
)

type CreatePatientInput struct {
	Name string `json:"name"`
}

func CreatePatient(ctx context.Context, request *http.Request) (response.Response, error) {
	input, err := gowdkapi.DecodeJSON[CreatePatientInput](request)
	if err != nil {
		return gowdkapi.Error(http.StatusBadRequest, "invalid_json", "Invalid JSON body")
	}

	active, ok, err := gowdkapi.QueryBool(request, "active")
	if err != nil {
		return gowdkapi.Error(http.StatusBadRequest, "invalid_query", "Invalid query")
	}
	_ = input
	_ = active
	_ = ok

	return gowdkapi.JSON(http.StatusCreated, map[string]any{"ok": true})
}

DecodeJSON[T] decodes the capped request body into T, accepts application/json and +json content types, rejects unknown object fields, and rejects trailing JSON values. Empty Content-Type is accepted so simple clients can still post JSON bodies.

Query helpers read from request.URL.Query():

  • QueryString(request, name) (string, bool)
  • QueryStrings(request, name) []string
  • QueryBool(request, name) (bool, bool, error)
  • QueryInt(request, name) (int, bool, error)
  • QueryInt64(request, name) (int64, bool, error)

Response helpers return runtime/response.Response:

  • JSON(status, value) marshals a JSON response.
  • Error(status, code, message) returns { "ok": false, "error": ... }.
  • NoContent() returns a 204 response.

Generated bound API adapters attach endpoint metadata to the handler context. Handlers can call app.Endpoint(ctx) from runtime/app to read the generated endpoint kind, page ID, symbol name, method, path, and optional generated error page.

The optional endpoint-local error suffix selects a generated HTML error page for API panics before response headers are written. Returned handler errors still follow normal runtime/response.Response behavior.

APIs declared on guarded pages share the generated app guard hooks with SSR pages and actions. Custom guards require GOWDKGuardRegistry; native RBAC guard IDs such as role:admin and permission:reports.read require GOWDKAuthProvider. Missing backing hooks fail the generated app Go build. Generated API handlers run guards before user handler calls. Treat these as defense-in-depth redundancy for generated route/page access, never as backend resource authorization. If the page itself is protected, use request-time page rendering; build-time SPA HTML cannot enforce frontend page access.

Production Notes

  • API handlers own authentication, backend authorization, domain validation, storage, service calls, and response shape in normal Go.
  • addons/api helpers cover strict JSON body decoding, typed query access, and JSON response envelopes without requiring framework-specific adapters.
  • Bound API handlers return runtime/response.Response; generated adapters only dispatch by method/path, call the handler, and write the returned response.
  • Generated API responses and generated API error responses use Cache-Control: no-store in the current first slice.
  • Generated API adapters dispatch only the declared HTTP method/path pair; unsupported methods do not call user handlers.
  • Handler errors are written with runtime/response.HandlerStatus, defaulting to HTTP 500 when the error does not carry an explicit status. Ordinary 5xx responses use generic status text; expose only intentional client-facing messages through runtime/response.HandlerError.Message.
  • Missing or unsupported generated API bindings return HTTP 501 only in development/default mode or when an explicit missing-backend migration flag is set.
  • Generated action CSRF wiring does not protect API endpoints. State-changing APIs need normal Go auth/session checks and an explicit CSRF, same-site, or token strategy appropriate to the API client.

Future API behavior must define:

  • Authentication and authorization hooks.
  • Generated typed handler signatures beyond func(context.Context, *http.Request) (response.Response, error).
  • Per-route body/query/result contracts and route-param accessors.
  • CORS policy and richer content negotiation.
  • Interaction with SPA/action pages without full-page SSR.