2
votes

I'm using ServiceStack for creating my first API. In my service the user can enter new orders and retrieve those he has already executed. Each order has a very complex structure made up of various fields and lists of objects (some of which consist of 10 or 100 items, other consiste of thousands of records which therefore require paging).

The question is: what and how much data to send to the client.

Hypothetically, when the client requests the list of last executed orders I should provide a list of orders with few fields.

When the client instead requests the detail of the order I could give him more information.

In the REST world I saw 2 techniques:

[view] parameter

/orders?view=list

/orders?view=detail

which can be "list | detail" which provides a more succinct or more detailed version of the requested object.

[expand] parameter

/orders?expand=customer

/orders?expand=customer,address,phones,address

that allows specifying which fields to retrieve

In this way, whether the user requests a list of objects or the detail he can specify the amount of information he want to receive.

This solution is adopted by Stripe.com that provide also a c# client.

https://github.com/stripe/stripe-dotnet/blob/master/src/Stripe.net/Entities/ExpandableField.cs

How can these solutions be implemented with ServiceStack and in particular with the fact that the same api can be used both via http and via ServiceStack.Client?

Can it be a good idea to create two types of DTOs?

OrderDTO (containing few fields)

OrderDetailDTO (containing all the fields)

1

1 Answers

1
votes

What you choose is dependent on your use-case, generally you'd return summary info when viewing all orders:

/orders

And full order details when viewing a single order:

/orders/1

If you want to implement your ?expand customization you'd just add it as another property on your Request DTO then use it to test which additional info you should populate on your Response DTO, e.g:

[Route("/orders")]
public class QueryOrders : IReturn<QueryOrdersResponse>
{
    public string[] Expand { get; set; }
}

public object Any(QueryOrders request)
{
    var expand = new HashSet<string>(request.Expand ?? new string[0],
       StringComparer.OrdinalIgnoreCase);

    if (expand.Contains(nameof(Order.Customer)))
    {
       // populate Response DTO with additional info...
    }
}