modular_api 0.7.0
modular_api: ^0.7.0 copied to clipboard
Use-case centric API toolkit for Dart — Shelf UseCase base class, HTTP adapters, CORS middleware, and automatic OpenAPI documentation.
Changelog #
All notable changes to this project will be documented in this file.
The format loosely follows Keep a Changelog and the project adheres to Semantic Versioning.
0.7.0 - 2026-08-01 #
trace_idchanges shape when tracing is configured. Withouttracingit stays a dashed UUID v4, exactly as before; withtracingit becomes the 32-hex W3C trace id of the server span, which is what a trace backend can join on. The change is gated on adopting tracing so that consumers who do not ask for it are unaffected. Check any query, dashboard or alert that assumes the dashed form — see the observability guide- add distributed tracing:
tracing: TracingOptions(tracerProvider: ...)on theModularApiconstructor. Absent means no spans, no propagation and no cost — there is notracingEnabledboolean, so "off" cannot be misconfigured - the framework instruments, the application supplies the OTel SDK. Core now declares the OpenTelemetry API as a direct dependency and will never declare an SDK, exporter, gRPC or protobuf; a test enforces that boundary. The API is no-op without an SDK, so tracing costs nothing until you configure a provider (ADR-0005 amendment A1/A2)
- the server span is opened by the host outside every plugin middleware slot, and made ambient, so a use case or satellite package reaches it with nothing threaded through any signature
- add
span_idandrequest_idto log lines.request_idcarries the client'sX-Request-IDverbatim and is never used as the trace id and never invented: a retried request reuses its request id on purpose, so one request id can span several traces - add a propagation chain, first-valid-wins: W3C
traceparent/tracestate, thenX-Cloud-Trace-Contextso a trace started by a Google load balancer is not orphaned.X-Cloud-Trace-Contextis read by default and written only when asked — the framework emits open formats and nothing vendor-specific - add
trustIncomingTraceContext(defaulttrue); set itfalseat an internet-facing edge, where a caller controllingtraceparentcontrols which trace their request joins - add
traceFieldFormatter, the hook through which a platform correlation field such aslogging.googleapis.com/tracereaches log lines. No vendor field is emitted by default, because the value needs a project id the framework does not know - add
onShutdown, invoked when the server closes. The framework owns the timing, the application owns the resource — it never shuts down or flushes a provider it did not create. On a scale-to-zero platform this is the only window a batching processor has to flush - operational routes (health, docs, openapi, metrics) produce no span; the exclusion list is derived from the same source as the routes rather than hardcoded
- tracing is not a plugin and has no endpoint. The span must sit outside every plugin middleware slot, while a plugin registers into one by definition (ADR-0005 amendment A7)
duration_msis now slightly smaller than the server span, because tracing is the outermost layer and logging sits inside it. Expected, not a regression- Dart only:
OTel.initializetakes a singularspanProcessor, and its default exporter speaks gRPC. Targeting an OTLP/HTTP Collector means passingOtlpHttpSpanExporterexplicitly
0.6.1 - 2026-07-02 #
Added #
- Web-safe DTO contract entry-point — new
package:modular_api/dto.dartlibrary exporting only the runtime-free contract surface (Input,Output,SchemaField,buildSchema,validateJsonFields,InputValidationException,UseCaseException). Import it from packages shared with web/desktop clients (e.g. a Flutter app's DTO package) to define and validate DTOs without pulling in the server runtime.
Changed #
- Logger no longer imports
dart:iodirectly — theModularLogger/LogLevelcontract moved to a puremodular_logger.dart, andRequestScopedLogger's default sink is resolved via a conditional import (stdoutondart:ioplatforms, aprint-based sink on web). Importing the full barrel — or theInput/Outputcontract — no longer breaks web compilation. - Corrected
modularApiPackageVersion(was stale at0.4.7) to track the package version.
0.6.0 - 2026-06-13 #
Changed #
- version bump for coordinated ecosystem release (ADR-0002); no functional changes in this package
0.5.0 - 2026-06-12 #
Added #
- Plugin OpenAPI contributions (ADR-0003) —
PluginRoutegains an optionalopenapifield (standard OpenAPI Operation object); the official OpenApiPlugin mergescustom/transportplugin routes into the generated spec, so plugin-served endpoints (e.g. binaries) appear in/openapi.json,/openapi.yaml, and/docs. PluginHost.routes()— read view of registered plugin routes (pluginId, method, mounted path, visibility, openapi operation).
Changed #
- Metrics route labels cover plugin routes — registered plugin route paths join
registeredPaths, so plugin routes report their realroutelabel instead ofUNMATCHED.
0.4.8 - 2026-06-06 #
Changed #
- Clean-room driver isolation — the base package no longer declares concrete SQL Server or Postgres drivers, so REST-only and GraphQL-base consumers can install/import
modular_apiwithout external database drivers. - Public physical model kept in core —
PhysicalCatalog,PhysicalObject,PhysicalField,PhysicalRelationSeed, andPhysicalObjectKindremain exported from the base package as GraphQL-base contracts.
Removed #
- SQL Server metadata reader ownership from core — the concrete SQL Server metadata reader and connection settings moved out of
modular_apiand intomodular_api_sqlserver, keeping driver ownership at the engine-package boundary.
0.4.7 - 2026-06-01 #
Changed #
- Plugin middleware guardrails — request-completed logs now annotate attributable short-circuit metadata (
short_circuit_*) when a plugin middleware terminates the pipeline before the core handler. - Host-owned error normalization — uncaught plugin-pipeline exceptions now return a structured JSON
500response instead of falling through to framework-default error handling.
0.4.6 - 2026-04-24 #
Changed #
- Version bump for cross-SDK parity — aligns with TypeScript 0.4.6 re-release (see ADR-0002). No functional changes.
0.4.5 - 2026-03-28 #
Added #
serversparameter inModularApiconstructor — configures the OpenAPIserversfield so Swagger UI "Try it out" targets the correct host (LAN IP, domain, reverse proxy URL). Defaults tolocalhost:{port}when omitted.corsMiddleware()— configurable CORS middleware replacingexampleCorsMiddleware(). Acceptsorigin(String or List),methods, andallowedHeaders. Aligned with TypeScriptcors()and Pythoncors_middleware().SchemaField.object()— factory constructor for nested JSON object fields (type: 'object'). Enables webhook payloads with arbitrary nested objects to be declared, validated, and documented in OpenAPI (issue #8).objectcase in_isJsonTypeValid— validates that a field declared asobjectreceives aMapvalue; rejects strings, arrays, and other types.Mapcase in_inferOpenApiType— infers'object'forMapvalues in example-based schema generation.
Fixed #
useCaseHttpHandlercatch blocks use scoped logger —UseCaseExceptionand unexpected errors are now logged throughreq.context['modular.logger']instead ofstderr.writeln, enabling Loki correlation withtrace_id(issue #7).
Removed #
exampleCorsMiddleware()— replaced by the configurablecorsMiddleware()
0.4.4 - 2026-03-14 #
Changed #
- Swagger UI replaced with
@macss/docs-ui— the ~200-line inline HTML/CSS/JS reduced to a ~15-line bootloader that loads@macss/docs-ui@0.1from jsdelivr CDN - Dark mode now delegated to
docs-uipackage — single source of truth across all three SDKs
0.4.3 - 2026-03-13 #
Changed (BREAKING) #
execute()returnsFuture<O>— no longerFuture<void>; the handler reads the returned Output directly- Removed
outputfield fromUseCase— no mutable state;execute()returns the result - Removed
toJson()fromUseCase— the handler callsoutput.toJson()on the returned value inputExample/outputExamplenow required inModuleBuilder.usecase()— OpenAPI schema extraction uses them directly- Removed Strategy 2 fallback in OpenAPI schema extraction — no
factory({}).outputpath
Removed #
@Fieldannotation — was decorative only (no runtime effect in Dart);schemaFieldsgetter is the sole schema source
0.4.2 - 2026-03-12 #
Added #
- Auto-schema generation —
InputandOutputDTOs derive OpenAPI 3.0.3 schemas automatically fromschemaFieldsgetter +SchemaFieldmetadata @Fieldannotation — decorative per-field documentation (@Field(description: 'x'))SchemaFieldclass — metadata with factories:.string(),.integer(),.number(),.boolean(),.array()buildSchema()utility — convertsList<SchemaField>to OpenAPI 3.0.3-compatibleMap<String, dynamic>Input/Outputbase classes gain generative constructors and defaulttoSchema()fromschemaFields- Cross-language schema conformance tests against shared JSON fixtures
Changed #
- DTOs now use
extends Input/extends Outputinstead ofimplements(for inheritedtoSchema()default) - Manual
toSchema()override is deprecated — useschemaFieldsgetter instead (removal in v0.5.0)
0.4.1 - 2026-03-12 #
Changed #
- Replaced
shelf_swagger_uiwith built-in Swagger UI served via CDN — zero external UI dependencies - Built-in dark mode support for Swagger UI (system-aware via
prefers-color-scheme)
Added #
SwaggerDocs— internal handler serving the Swagger UI HTML page at/docs- Cross-language parity with TypeScript and Python implementations
0.4.0 - 2026-03-03 #
Removed #
- BREAKING:
useCaseTestHandler— removed from public API and deletedlib/src/core/usecase/usecase_test_handler.dart- Testing now uses direct constructor injection: instantiate the UseCase with its Input, call
validate(),execute(), and assert onoutputdirectly - Barrel export removed from
lib/modular_api.dart
- Testing now uses direct constructor injection: instantiate the UseCase with its Input, call
Added #
GET /openapi.json— returns the full OpenAPI 3.0 specification asapplication/jsonGET /openapi.yaml— returns the full OpenAPI 3.0 specification asapplication/x-yamlOpenApi.openapiJson/OpenApi.openapiYaml— Shelf handlers for raw spec accessOpenApi.jsonToYaml()— zero-dependency JSON-to-YAML converter- Spec is cached at startup alongside Swagger UI (no per-request rebuild)
- 18 new tests: jsonToYaml unit (8), /openapi.json integration (4), /openapi.yaml integration (5), consistency (1)
Changed #
- Updated testing guide (
doc/testing_guide.md) to document the constructor-injection approach - Updated
doc/usecase_implementation.mdanddoc/logger_guide.mdto removeuseCaseTestHandlerreferences - Updated
AGENTS.mdandREADME.mdexamples to reflect the new testing pattern
0.3.0 - 2026-02-26 #
Added #
- Structured JSON Logger — request-scoped logging compatible with Loki, Grafana, Elasticsearch, and any JSON log aggregator
LogLevelenum — 8 RFC 5424 severity levels (emergency..debug) with configurable filteringModularLoggerinterface — 8 logging methods (one per level) with optional structuredfieldsRequestScopedLogger— implementation with injectableStringSinkfor testabilityloggingMiddleware()— Shelf middleware that creates a per-request logger with uniquetrace_idtrace_idauto-generated (UUID v4, zero dependencies) or propagated fromX-Request-IDheaderX-Request-IDresponse header set on every response for client-side correlation- Logger injected as
UseCase.loggerproperty — zero breaking change toexecute()signature - Automatic status-to-level mapping: 2xx→info, 4xx→warning, 5xx→error
- Excluded routes:
/health,/metrics,/docs,/docs/(no request/response logs) logLevelparameter onModularApiconstructor (default:LogLevel.info)useCaseTestHandlernow accepts optionalloggerparameter for test observabilitygenerateUuidV4()— manual UUID v4 usingdart:math Random.secure()(no external deps)- Barrel exports:
LogLevel,ModularLoggerfrompackage:modular_api/modular_api.dart - 68 new tests: logger (27), uuid (6), middleware (23), integration (12)
- Documentation:
doc/logger_guide.md
0.2.0 - 2026-02-24 #
Added #
- IETF Health Check Response Format —
GET /healthnow returnsapplication/health+jsonfollowing draft-inadarei-api-health-check HealthCheckabstract class — implement to register custom health checks (database, cache, queue, etc.)HealthCheckResult— result DTO withstatus,responseTime(ms), and optionaloutputHealthStatusenum —pass,warn,failwith worst-status-wins aggregationHealthService— executes checks in parallel with per-check configurable timeout (default: 5s)HealthResponse— aggregated response withversion,releaseId,checksmap, andhttpStatusCode(200 for pass/warn, 503 for fail)healthHandler()— Shelf handler forGET /healthModularApi.addHealthCheck()— register health checks via method chainingModularApiconstructor now acceptsversionand optionalreleaseIdparametersreleaseIddefaults toversion-debug; override at compile time with--define=RELEASE_ID=x.y.z- Prometheus Metrics Endpoint — opt-in
GET /metricsin Prometheus text exposition format Counter,Gauge,Histogram— pure Dart metric types (zero runtime dependencies)MetricsRegistrar— public API for registering custom metrics viaapi.metricsmetricsEnabled,metricsPath,excludedMetricsRoutesconstructor parameters- Built-in HTTP instrumentation:
http_requests_total,http_request_duration_seconds,http_requests_in_flight,process_start_time_seconds
Changed #
- BREAKING:
GET /healthresponse changed from plaintextokto JSONapplication/health+json - BREAKING:
ModularApiconstructor signature changed — addedversionparameter (defaults to'0.0.0')
0.1.0 - 2026-02-21 #
Changed #
- BREAKING: Stripped to core-only — aligned Dart and TypeScript SDKs at feature parity
- Version bump from 0.0.10 to 0.1.0 (semver: new public API surface)
ModularApiconstructor no longer acceptsoauthServiceparameterModuleBuilder.usecase()no longer acceptsrequiredScopesparameter- Reduced dependencies from 14 to 3 (
shelf,shelf_router,shelf_swagger_ui) - Simplified
doc/INDEX.mdto core-only development flow - Example no longer uses
Env— hardcoded port for simplicity
Removed #
lib/src/auth/—JwtHelper,OAuthService,OAuthClient,PasswordHasher,TokenHasher, all OAuth2 typeslib/src/core/oauth_handler.dart—createOAuthTokenHandlerlib/src/middlewares/apikey.dart—exampleApiKeyMiddlewarelib/src/middlewares/bearer.dart—bearer(),requireAuth()lib/src/utils/env.dart—Envutilitylib/src/utils/get_local_ip.dart—getLocalIp- Dependencies:
http,ffi,dotenv,path,cryptography,bcrypt,dart_jsonwebtoken,crypto,ffigen - Documentation:
auth_implementation_guide.md,authentication_guide.md,http_client_guide.md
Kept (Core) #
UseCase<I, O>,Input,Output— abstract base classesUseCaseException— structured error handlingModularApi,ModuleBuilder— routing and module registrationuseCaseHttpHandler— Shelf HTTP adapteruseCaseTestHandler— unit test helperexampleCorsMiddleware— CORS middlewareOpenApi— automatic Swagger/OpenAPI docs at/docsGET /health— health check endpoint
0.0.10 - 2025-12-23 #
Added #
UseCaseException— Dedicated exception for use case execution errors:statusCode— HTTP status code to return (400, 404, 422, 500, etc.)message— Human-readable error messageerrorCode— Optional error code for client-side handlingdetails— Optional additional details (validation errors, context)- Automatically caught by
useCaseHttpHandlerand converted to appropriate HTTP responses - Allows fine-grained control over error responses instead of generic 500 errors
Changed #
- Updated
useCaseHttpHandlerto catchUseCaseExceptionand return the specified status code - Enhanced error handling with structured error responses
0.0.9 - 2025-12-15 #
Added #
- OAuth2 Client Credentials Support — full OAuth2 implementation:
OAuthService— manages client authentication and JWT token generationOAuthClient— represents registered clients with credentials and scopesTokenRequest,TokenResponse,TokenErrorResponse— OAuth2 data structuresAccessToken— decoded JWT with scope validation methodscreateOAuthTokenHandler— POST /oauth/token endpoint handlerbearer()middleware — validates Bearer tokens and enforces scopesrequireAuth()middleware — marks routes as requiring authentication- HS256 (HMAC-SHA256) JWT signing algorithm
- Scope-based authorization for protected endpoints
- Follows RFC 6749 OAuth2 specification
Changed #
- Exported auth utilities:
JwtHelper,hashPassword,verifyPassword,hashToken - Updated exports to include OAuth2 types and services
Removed #
- Removed
template/folder — Simplified project structure by removing the full example template- Template project has been removed to reduce repository complexity
- Only the minimal
example/folder remains for quick reference - Previous template code included extensive examples, tests, and infrastructure that were redundant
Documentation #
- Added comprehensive OAuth2 guides and examples
- Updated AGENTS.md with OAuth2 usage patterns
- Updated all documentation to remove references to
template/folder - Simplified examples to focus on the minimal
example/implementation - Cleaned up README.md, AGENTS.md, and guides to reflect simplified structure
0.0.8 - 2025-12-04 #
Added #
- Output.statusCode — customizable HTTP status code for UseCase responses:
Outputabstract class now includesint get statusCode => 200;getter- Override in your Output DTO to return custom HTTP status codes (e.g., 201 for created, 400 for bad request)
useCaseHttpHandlerusesoutput.statusCodeinstead of hardcoded 200- Enables proper RESTful responses without modifying the HTTP handler
Changed #
- Output DTOs now use
extends Outputinstead ofimplements Outputto inherit the defaultstatusCodegetter - Example and template Output classes updated to extend Output
Migration Guide #
If you have existing Output DTOs using implements Output, change them to extends Output:
// Before
class MyOutput implements Output { ... }
// After
class MyOutput extends Output { ... }
Alternatively, add the statusCode getter manually: @override int get statusCode => 200;
0.0.7 - 2025-11-04 #
Added #
- httpClient — intelligent HTTP client with automatic authentication:
- Automatic token attachment for authenticated requests (
auth: true) - Auto-capture of access and refresh tokens from login responses
- Transparent retry with refresh token on 401 responses
- Throws
AuthReLoginExceptionwhen refresh fails (signals re-login required) - Per-user token management via
userparameter
- Automatic token attachment for authenticated requests (
- Token (in-memory session) — static class for managing access tokens:
Token.accessToken— current access tokenToken.accessExp— access token expiration timestampToken.isAuthenticated— check if user has valid tokenToken.isExpired— check if current token is expiredToken.clear()— clear session
- TokenVault (persistent storage) — configurable adapter for refresh tokens:
TokenVault.saveRefresh(userId, token)— persist refresh tokenTokenVault.readRefresh(userId)— retrieve refresh tokenTokenVault.deleteRefresh(userId)— delete specific refresh tokenTokenVault.deleteAll()— clear all tokensTokenVault.configure(adapter)— set storage adapter- Includes
MemoryStorageAdapter(default) andFileStorageAdapterwith optional encryption
- JwtHelper — JWT generation and validation utilities:
generateAccessToken({userId, username})— create access tokens (15 min)generateRefreshToken({userId, tokenId})— create refresh tokens (7 days)verifyToken(token)— validate and decode JWTcalculateRefreshTokenExpiration()— get refresh token expiry date- Reads secret from
JWT_SECRETenvironment variable
- PasswordHasher — bcrypt password hashing:
hash(password, {cost = 12})— generate bcrypt hashverify(password, hash)— verify password against hashneedsRehash(hash, {cost})— check if hash needs update
- TokenHasher — SHA-256 token hashing for secure storage:
hash(token)— generate SHA-256 hash (64 hex chars)verify(token, expectedHash)— verify token against hash
- AuthReLoginException — specialized exception for auth flow control
- Comprehensive authentication documentation:
- docs/auth_implementation_guide.md — Complete JWT authentication implementation guide with refresh tokens, including database setup, repository patterns, use cases, and client integration (Flutter & Dart)
- docs/http_client_guide.md — Focused guide for using httpClient in Flutter and pure Dart applications with configuration examples and best practices
- Complete E2E authentication test suite:
template/test/e2e/auth_flow_test.dart— 25+ comprehensive tests covering login, refresh, logout, protected endpoints, concurrent requests, auto-refresh behavior, and re-login exception handling
Changed #
- All documentation translated to English for consistency
0.0.6 - 2025-10-30 #
Added #
- Exported
useCaseTestHandlerin main library export (lib/modular_api.dart) for convenient unit testing of UseCases without starting an HTTP server. - Comprehensive documentation guides:
- AGENTS.md — Framework overview and implementation guide optimized for AI assistants
- docs/USECASE_DTO_GUIDE.md — Complete guide for creating Input/Output DTOs with type mapping reference and advanced examples
- docs/usecase_implementation.md — Step-by-step guide for implementing UseCases with validation, database access, and repository patterns
- docs/TESTING_GUIDE.md — Quick reference for testing UseCases using
useCaseTestHandler
- Complete test suite for template project:
template/test/module1/hello_world_test.dart— 5 tests for HelloWorld use casetemplate/test/module2/sum_case_test.dart— 7 tests for SumCase use casetemplate/test/module2/upper_case_test.dart— 7 tests for UpperCase use casetemplate/test/module3/lower_case_test.dart— 7 tests for LowerCase use casetemplate/test/module3/multiply_case_test.dart— 9 tests for MultiplyCase use case- All 35 tests demonstrate proper usage of
useCaseTestHandlerwith success and failure scenarios
0.0.5 - 2025-10-25 #
Changed #
Envnow initializes automatically on first access (lazy singleton pattern). No need to callEnv.init()explicitly, though it remains available for manual initialization if needed.- .usecase() now trims leading slashes from usecase names to prevent double slashes in registered paths.
0.0.4 - 2025-10-23 #
Changed #
Envbehavior: when a.envfile is not found the library reads values fromPlatform.environment. If a requested key is missing from both sources anEnvKeyNotFoundExceptionis thrown.
0.0.3 - 2025-10-23 #
Added #
- Automatic health endpoint: the server registers
GET /healthwhich responds withokon startup. Implemented inmodular_api.dart(exposes_root.get('/health', (Request request) => Response.ok('ok'));).
0.0.2 - 2025-10-21 #
Changed #
- refactor: improve OpenAPI initialization (now initialized automatically internally)
- Rename middlewares as examples
- rename example project to template
- Add a simple example
0.0.1 - 2025-10-21 #
Added #
- Initial release of modular_api. Main features:
- Use-case centric framework with
UseCase<I extends Input, O extends Output>base classes and DTO helpers (Input/Output). - HTTP adapter
useCaseHttpHandler()to expose UseCases as ShelfHandlers. - Built-in middlewares:
cors()andapiKey()for CORS handling and header-based API key authentication. - OpenAPI/Swagger generation helpers (
OpenApi.init,OpenApi.docs) that infer schemas from DTOtoSchema(). - Utilities:
Env.getString,Env.getInt,Env.setString(.env support via dotenv) andgetLocalIp. - Minimal ODBC
DbClient(DSN-based) exported for database access; example factories and usage provided inexample/(tested with Oracle and SQL Server; seeNOTICEfor provenance). - Example project demonstrating modules and usecases under
example/and unit-test helpers (useCaseTestHandler) undertest/. - Public API exports in
lib/modular_api.dartfor easy consumption.
- Use-case centric framework with