Gaps and roadmap¶
Honest inventory of what Future still lacks or only partially implements. Use this when deciding what to build next.
Won’t do unless requested¶
| Item | Notes |
|---|---|
| Query / form → action kwargs | Path params and request.query / cached body() / json() / form() / files() are enough. Auto-inject into action kwargs fights explicit style. |
Database / ORM¶
MongoDB, ClickHouse, and Redis Active Record CRUD are usable and async (Redis has no schema/migrate). Remaining ORM gaps are driver-specific edge cases rather than stubs.
where(..., "like", ...)naming — Pattern matching works on all drivers (%/_, mapped per backend), but the operator name is SQL-flavored. Later: review a more generic, cross-store name (e.g.match/pattern/contains-style) that stays understandable for SQL, Elasticsearch, MongoDB, etc., without breaking the agnostic Model API. Keep one portable pattern language; avoid driver-specific wording in app code.- Elasticsearch 9 — Client is pinned to
>=8.15,<9so migrate/queries work against Elasticsearch 8.x (client 9.x sendscompatible-with=9and 8.x servers reject it withmedia_type_header_exception). Later: support ES 9 (bumpelasticsearchto 9.x and align Docker/docs), ideally without breaking ES 8 apps — or document a clear major-version split.
Middleware stock library¶
CORS, GZip, CSRF, and RateLimit are usable and exported. Confuser classes remain unfinished and are not exported from future.middleware.
- Configured middleware on routes — Today
middlewares=[CORSMiddleware]is a class list; Future constructs each with(request, response). Consider allowing per-route / per-group options without subclasses, e.g.middlewares=[CORSMiddleware(opt=opt)](or an equivalent factory / options object), so apps can set CORS origins, rate limits, etc. inroutes.py. Design carefully: keep no-decorator style, and do not break the current class +(request, response)injection.
Authentication¶
future.authentication.* are stubs (auth_type only) extending future.interfaces.IAuthentication. No JWT / OIDC / session-login middleware ships yet.
WebSockets¶
Usable duplex echo — see WebSockets. handle_websocket_request mirrors HTTP for middleware, controller DI, and path params; errors close with 1011. HTTP chunked bodies: StreamingResponse (same module as WebSocketResponse) — see Response. Later: whether streaming belongs on Response itself.
HTTP/2 / ASGI server¶
- Outbound: prefer httpx (already used by the test client).
- Inbound HTTP/2: reverse proxy (nginx / Hypercorn in front) is fine.
Future.runstays on uvicorn. Hypercorn is not a drop-in foruvicorn.run(...)(different API:Config+asyncio.run(serve(app, config)), different workers/reload). An optional Hypercorn backend can be a later feature, not a silent swap.
GraphQL¶
GraphQLController.query accepts POST JSON search terms (email, name, title, content, author_id, id, optional limit). Each term is its own query; hits are unioned by id. GraphQL only shapes the result (GetEverything). Register the route yourself (Post("/graphql", GraphQLController.query, …)). There is no GRAPHQL_SCHEMA config key and no app.add_graphql_route. See GraphQL.
strawberry.type(Model)vs Active Record — The demo usesUserType = strawberry.type(UserModel). NestedQueryfield annotations still uselist[UserModel]becauseUserTypeis not in that class scope. Strawberry’stype(...)wraps the class in place; if that starts breakingIModellookups, apps should keep separate GraphQL types. Later: see if one class can serve ORM + GraphQL without decorator hacks.
Plugins¶
IPlugin is a marker interface in future.interfaces. Only ElasticsearchPlugin is substantial. No lifecycle hooks into Future boot.
Testing¶
Most HTTP / middleware / path-syntax / DB coverage lives under tests/. Remaining gaps:
| Gap | Notes |
|---|---|
FutureTestClient sync API |
Prefer from future.testclient import FutureTestClient (async). |
Exploring (needs design — keep Future simple)¶
| Idea | Notes |
|---|---|
| Thin service container | Optional Masonite-like bind / singleton / make for app services (mail, clients, TradeService), while controllers keep explicit request / response DI. Not a full auto-wire IoC for every dependency. Must stay optional and obvious — no hidden constructor magic by default. Compare Masonite’s container; design carefully before shipping. |
C#-style interface naming + Interface base |
Shipped. future/interfacing.py = core validation. future/interfaces/ = I* types (IModel, IController, IMiddleware, IDatabase, IPlugin). App code extends those. Concrete classes stay in domain folders. |
Accepted validation behavior:
- Always — every stub method on an
Interfaceancestor must be implemented; signatures must match exactly;_-prefixed helpers ignored. __strict__ = Trueon the interface — no extra public methods on implementors (drop-in drivers). DefaultFalse(C#/PHP allow extra methods on the class).- Interface stub declarations (
raise NotImplementedError) are not valid inherited implementations; real methods on implementors (includingraise NotImplementedError("reason")) count.
Example:
class Interface:
__strict__ = False
class IDatabase(Interface):
async def connect(self) -> None: raise NotImplementedError
async def save(self, model) -> None: raise NotImplementedError
async def transaction(self): ... # default implementation, not required on subclasses
class PostgresDatabase(IDatabase):
async def connect(self) -> None: ...
async def save(self, model) -> None: ...
class IModel(Interface): # real methods — TradeModel(IModel) inherits without re-validation
async def save(self): ...
Explicitly out of scope (for now)¶
- Full IoC / auto-wiring every constructor argument (Laravel/Spring-style for the whole app)
- Shipping Redocly paid JS without a customer license
- Revel / Reef portal products (not embeddable OpenAPI UIs)
- Replacing uvicorn with Hypercorn as a silent default
When closing a gap, update the relevant guide and delete the row here.