A Fastify-based REST API that translates the MINT Model Catalog OpenAPI spec into GraphQL queries against a Hasura backend.
The API connects to Hasura via two environment variables:
| Variable | Default | Description |
|---|---|---|
HASURA_GRAPHQL_URL |
http://testing-mint-hasura.mint.svc.cluster.local/v1/graphql |
Hasura GraphQL endpoint |
HASURA_ADMIN_SECRET |
CHANGEME |
Hasura admin secret (used for reads) |
PORT |
3000 |
Server port |
LOG_LEVEL |
info |
Pino log level (trace, debug, info, warn, error) |
For local development, point to your Hasura instance:
export HASURA_GRAPHQL_URL=http://localhost:8080/v1/graphql
export HASURA_ADMIN_SECRET=myadminsecretkey- Reads use the admin secret (
X-Hasura-Admin-Secretheader). No JWT required. - Writes (POST/PUT/DELETE) forward the caller's
Authorization: Bearer <token>to Hasura. Hasura validates the JWT and enforces row-level permissions. The API itself does not validate tokens.
npm install
npm run dev # tsx watch — restarts on file changesnpm run build # Compile TypeScript to dist/
npm start # Run compiled output
npm test # Run all tests with vitest
npm run codegen # Regenerate GraphQL types from Hasura schemanpx vitest run src/__tests__/integration.test.tsGET /health
Returns { status: "ok", hasura: "connected" } or 503 if Hasura is unreachable.
Swagger UI is served at /v2.0.0/docs when the server is running.
docker build -t model-catalog-api .
docker run -p 3000:3000 \
-e HASURA_GRAPHQL_URL=http://your-hasura/v1/graphql \
-e HASURA_ADMIN_SECRET=your-secret \
model-catalog-apiHTTP request
-> Fastify (fastify-openapi-glue maps operationId -> handler)
-> CatalogService Proxy (service.ts)
-> CatalogServiceImpl (list / getById / create / update / deleteResource)
-> Apollo Client (hasura/client.ts)
-> Hasura GraphQL
-
src/service.ts— A JavaScriptProxywrapsCatalogServiceImpland intercepts all operationId method calls fromfastify-openapi-glue. It parses the operationId pattern ({resource}_(id_)?(get|post|put|delete)) and dispatches to the correct generic CRUD handler. This replaces ~230 individual handler files with a single class. -
src/hasura/client.ts— Two Apollo Client instances:readClient(uses admin secret, shared singleton) andgetWriteClient(bearerToken)(per-request, forwards user JWT for Hasura permission enforcement). -
src/hasura/field-maps.ts— Field selections per Hasura table, used to build dynamic GraphQL queries. -
src/mappers/resource-registry.ts— Maps the 46 API resource path segments to their Hasura table name, OWL type URI, and relationship configuration (direct, object, array, junction-table). -
src/mappers/response.ts/src/mappers/request.ts— Transform between Hasura row format and the v1.8.0-compatible JSON the API returns. -
src/custom-handlers.ts— Handlers for the 13/custom/endpoints and/user/login. These perform multi-table aggregation queries that cannot be served by the generic Proxy (e.g. fetching a model with all its versions, configurations, and setups in one response). -
openapi.yaml— The canonical OpenAPI 3.0 spec (243 operations). Served unmodified by Swagger UI. On startup,app.tsstrips response and request body schemas before registering routes to avoid AJV compilation overhead (~30s -> ~2s startup).
Several resource types (models, empiricalmodels, hybridmodels, emulators, etc.) share the modelcatalog_software Hasura table and are distinguished by a type column containing the OWL class URI. The getSoftwareTypeFilter function in service.ts appends the appropriate where: { type: { _eq: ... } } clause automatically.
All resource IDs are full URIs (e.g. https://w3id.org/okn/i/mint/<uuid>). The API accepts plain short IDs and prepends the prefix automatically. New resources created via POST get a randomUUID()-based URI.
To regenerate TypeScript types from the live Hasura schema:
HASURA_GRAPHQL_URL=http://localhost:8080/v1/graphql \
HASURA_ADMIN_SECRET=myadminsecretkey \
npm run codegenOutput goes to src/generated/graphql.ts.