diff --git a/docs/designs/Enable.MinimalApi.md b/docs/designs/Enable.MinimalApi.md
new file mode 100644
index 000000000..28b546c7e
--- /dev/null
+++ b/docs/designs/Enable.MinimalApi.md
@@ -0,0 +1,473 @@
+# Enable OData Minimal API (Design Draft)
+See issue: https://github.com/OData/AspNetCoreOData/issues/578
+
+## Problem
+Minimal APIs are a simplified approach for building HTTP APIs fast with ASP.NET Core. Customers can build fully functioning REST endpoints with minimal code and configuration, especially without controller, action and even the formatters.
+OData works with controller/action, but OData developers also want to enable OData query on minimal API pattern.
+
+## Prerequisites
+
+Here’s the basic minimal API service app:
+```C#
+var builder = WebApplication.CreateBuilder(args);
+var app = builder.Build();
+app.MapGet("/customers", () => new Customer[] { …. });
+app.Run();
+```
+This service provides a Web API as:
+
+`GET {host}/customers`
+
+This API returns a collection of customers, then OData developers want to enable OData functionalities on this API, for example:
+
+`GET {host}/customers?$select=name&$top=1`
+
+### Route handlers
+The lambda expression in preceding `MapGet` is called Route Handler. They are methods that execute when the route matches. Route handlers can be:
+
+- A lambda expression,
+- A local function,
+- An instance method
+- A static method
+- Or a RequestDelegate
+
+where:
+```C#
+public delegate Task RequestDelegate(HttpContext context);
+```
+Route handlers can be synchronous or asynchronous.
+
+## Scenarios
+
+### Enable OData response
+
+We enable developers to call an extension method named `WithODataResult()` on the route handler to get 'OData format' response:
+
+
+
+Be noted, It only contains the 'Id' and 'Name' in the OData format. This is because an Edm model is built on the fly since we don't provide the model explicity. In this case, all other properties are built as navigation properties by default.
+
+Developers can call `WithODataModel(model)` to provide an Edm model (`Info` property is built as complex type property in the pre-built Edm Model) explicitly, in this case, the serialization of OData format can use that model directly.
+
+Developers can also call `WithODataVersion(version)` to get different OData format. See below:
+
+
+
+Except the above extension methods, we want to provide more to enable developers to customize the OData format response:
+
+* WithODataServices(lambda) : to config the services within dependency injection
+* WithODataBaseAddressFactory(lambda): to config the base address for OData format, especially for the context URI
+* WithODataPathFactory(lambda) : to config the related OData path for the request
+
+For example:
+
+
+### Enable OData query explicity
+
+We want to enable binding `ODataQueryOptions` as route handler parameter and empower developers to call `ApplyTo()` explicitly as follows:
+
+
+
+As mentioned, the model is built on the fly and all complex properties are built as navigation properties. That's why `$expand=info` is used.
+
+Developers can combine the extension methods `WithOData*()` to get other payload. For example:
+
+
+
+In this case, developer should use `$select=info` to get the value since `info` is a complex type property in the pre-built Edm model.
+
+### Enable OData query implicitly
+
+We enable developers to call an extension method named `AddODataQueryEndpointFilter(...)` on the route handler to enable OData query functionality implicitly.
+
+
+
+Again, developer can combine other extensions method together to get other result, for example:
+
+
+
+
+### Use RouteHandlerGroup
+
+If lots of same route handlers have the same metadata, developers can enable the metadata on the Group.
+
+```C#
+var group = app.MapGroup("")
+ .WithODataResult()
+ .WithODataModel(model);
+
+group.MapGet("v0/orders", (AppDb db) => db.Orders);
+
+group.MapGet("v1/orders", (AppDb db, ODataQueryOptions queryOptions) =>
+{
+ db.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; // This line seems required otherwise it will throw exception
+ return queryOptions.ApplyTo(db.Orders);
+});
+
+group.MapGet("v2/orders", (AppDb db) =>
+{
+ db.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
+ return db.Orders;
+})
+ .AddODataQueryEndpointFilter();
+```
+
+In this scenario, all three endpoints use the same 'Model' and return OData format response.
+
+Developer can also call `WithOData*()` method on certain route handler to overwrite the metadata on the group.
+
+
+## Design Details
+
+### ODataResult
+
+We can get OData format repsone by implementing a custom `IResult` type.
+
+We create a class named `ODataResult` as below
+
+```C#
+public interface IODataResult
+{
+ object Value { get; }
+}
+
+internal class ODataResult : IResult, IODataResult, IEndpointMetadataProvider
+{
+ public async Task ExecuteAsync(HttpContext httpContext)
+ {
+ ...
+ }
+
+ public static void PopulateMetadata(MethodInfo method, EndpointBuilder builder)
+ {
+ // ... Think more: will we need this to do something? maybe no needed.
+ // Maybe we don't need 'WithODataResult()' method and use this to update the metadata?
+ }
+}
+```
+
+Make `ODataResult` as internal class to hide the details and it maybe change later. Developer can call `WithODataResult()` extension methods to enable a route handler to return OData format response.
+
+### WithODataResult()
+
+`WithODataResult()` is an extension method as below:
+
+```C#
+public static TBuilder WithODataResult(this TBuilder builder) where TBuilder : IEndpointConventionBuilder
+{
+ builder.AddEndpointFilter(async (invocationContext, next) =>
+ {
+ object result = await next(invocationContext);
+
+ // If it's null or if it's already the ODataResult, simply do nothing
+ if (result is null || result is ODataResult)
+ {
+ return result;
+ }
+
+ return new ODataResult(result);
+ });
+
+ // Add/update odata metadata
+}
+```
+
+Be noted, `With` prefix means to add/update endpoint metadata.
+
+
+### Other WithOData*() extensions
+
+Other `WithOData*()` are similiar extension methods same as `WithODataResult()`, For example:
+
+```C#
+public static TBuilder WithODataModel(this TBuilder builder, IEdmModel model) where TBuilder : IEndpointConventionBuilder
+{
+ // Add/update odata metadata
+}
+```
+
+All of them are used to add/update certain part of OData metadata.
+
+### OData metadata
+
+We define a class named `ODataMiniMetadata` to hold the metadata used for OData functionalities.
+
+All `WithOData*()` add `ODataMinimetadata` if it's non-existed and update part of its content.
+
+```C#
+public class ODataMiniMetadata
+{
+ public IEdmModel Model { get; set; }
+ public bool IsODataFormat { get; set; }
+ public Func PathFactory { get; set; }
+ ......
+}
+```
+
+### Parameter binding for ODataQueryOptions< T >
+
+In controller/action scenario, [model binding](https://learn.microsoft.com/en-us/aspnet/core/mvc/models/model-binding?view=aspnetcore-9.0) is used to bind `ODataQueryOptions` as action parameter. In Minimal API, there’s no model binding because it’s “MINIMAL”. To enable it as route handler parameter, we should 'customize' parameter binding for `ODataQueryOptions`.
+There are two ways to customize parameter binding:
+1. For route, query, and header binding sources, bind custom types by adding a static `TryParse` method for the type.
+2. Control the binding process by implementing a `BindAsync` method on a type.
+
+For parameter binding in minimal API, see [here](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/parameter-binding?view=aspnetcore-9.0)
+
+I prefer option #2 because it provides `HttpContext` and parameter information for more functionaliiesy. As a result, we add a static method in `ODataQueryOptions` as:
+```C#
+public class ODataQueryOptions : ODataQueryOptions
+{
+ ……
+ public static ValueTask> BindAsync(HttpContext context, ParameterInfo parameter)
+ {
+ // 1) Get or generate Edm model
+ // 2) Build ODataQueryOptions and return
+ }
+}
+```
+
+In this usage, we can re-use most functionalities from the existing `ODataQueryOptions` class.
+
+### OData Query Endpoint Filter
+
+Minimal API filters allow developers to implement business logic that supports:
+- Running code before and after the endpoint handler.
+- Inspecting and modifying parameters provided during an endpoint handler invocation.
+- Intercepting the response behavior of an endpoint handler
+
+So, we can do the OData query using the minimal API filter. More details:
+1) We can do OData query validation before the endpoint handler. (It is powerful).
+2) We can apply the OData query on the data after the endpoint handler.
+
+Basically, that’s the same logic comparing to `EnableQueryAttribute`. But, minimal API has its own filter logic/pipeline.
+
+#### EndpointFilter vs EndpointFilterFactory
+There are two ways to add filter on route handler.
+
+1) `AddEndpointFilter`
+
+which registers a standard endpoint filter directly onto a route handler
+
+2) `AddEndpointFilterFactory`
+
+which allows you to create a filter dynamically based on the context of the endpoint using a factory function, enabling more flexible filter application based on the endpoint's details like its handler signature.
+
+So far, a standard endpoint filter is enough for OData query.
+
+#### IODataQueryEndpointFilter
+
+The below design is based on the standard endpoint filter.
+We create a new interface as below:
+```C#
+public interface IODataQueryEndpointFilter : IEndpointFilter
+{
+ ValueTask OnFilterExecutingAsync(ODataQueryFilterInvocationContext context);
+
+ ValueTask