2

I have a question cause I've been in this situation before and I want to know best practice.

Lets say you want these three endpoints.

host:port/api/movies/published/2012 returns all from '2012' sorted by published_date (M13, M1) host:port/api/movies/published/2012/8 returns all from '2012-08' sorted by published_date (M1) host:port/api/movies/published/2012/8/15 returns all from '2012-08-15' sorted by published_date (M1)

Would you have done a generic endpoint like this or what is best practice in these situations? How would you solve it?

[HttpGet("published/{year:int}/{month:int?}/{day:int?}/")]
public void GetMoviesSortedByPublishedDateAsync(int year, int month = 0, int day = 0)
{

    if (month == 0)
    {
        var date = new DateTime(year, 1, 1);
        await _cosmosDbService.GetMoviesByYear(date.Year);
    }
    else if(day == 0)
    {
        var date = new DateTime(year, month, 1);
        await _cosmosDbService.GetMoviesByYearAndMonth(date.Year, date.Month);
    }
    else
    {
        var date = new DateTime(year, month, day);
        await _cosmosDbService.GetMoviesByPublishedDate(date);
    }
}
Andy
  • 12,859
  • 5
  • 41
  • 56
KENUBBE
  • 79
  • 1
  • 5
  • 12
  • Your code looks like a decent enough approach, to be honest. is it not working? – zaitsman Aug 17 '20 at 23:46
  • Why do you need all three? The first should give everything. So you solution is giving duplicates and triplicates. You get everything from first query. Then you get duplicates for everything in August. Then triplicates for everything on August 15. – jdweng Aug 17 '20 at 23:49
  • I mean yeah it should work but is there any better and more efficient way to solve it? – KENUBBE Aug 17 '20 at 23:49

3 Answers3

2

What you have may work, but it does have some possible downsides including the fact that building parameters into the URL path ends up creating an unlimited number of possible paths. If you wanted to analyze or filter your traffic logs or otherwise do additional request processing, parsing out these path combinations could lead to some pain.

It seems like a better practice to make the date a parameter in the query string, as is more idiomatic to REST URLs. It could be a date prefix in ISO 8601 format, which is naturally sortable. Then, your query could leverage the native STARTSWITH in Cosmos DB to efficiently filter and sort on a publishedDate property.

It might look something like this:

[HttpGet("{published}")]
public void GetMoviesSortedByPublishedDateAsync(string published)
{
    if(!string.IsNullOrEmpty(published) // Validate better than this :)
    {
        // Queries container where published date STARTSWITH specified value
        await _cosmosDbService.GetMoviesByPublishPrefix(published);
    }
}

Possible values could be any valid date prefix with optional 2 digit day/month:

2020 => GET https://app.com/movies?published=2020
2020-03 => GET https://app.com/movies?published=2020-03
2020-03-13 => GET https://app.com/movies?published=2020-03-01
Noah Stahl
  • 6,905
  • 5
  • 25
  • 36
  • Hi, I ended up using this solution but it only works if I use published=2020-03 but if the user send it like published=2020-3 without the zero. Then STARTSWITH wont return the object. – KENUBBE Aug 18 '20 at 11:58
  • Yes, you'd need to ensure the input is left padded if needed: https://stackoverflow.com/questions/3459610/pad-with-leading-zeros – Noah Stahl Aug 18 '20 at 14:19
1

Would you have done a generic endpoint like this or what is best practice in these situations?

I think the decision should be made based on how the API is likely to be used. It is very likely that the consumer of this API will have a similar filtering option (by year, by month, by date) - in which case a single endpoint is nicer to work with.

As far as best practices, here's an interesting take, supporting your approach - take a look at the Allow filtering, sorting, and pagination section.

CoolBots
  • 4,770
  • 2
  • 16
  • 30
0

Why not simplify it like this :

[HttpGet("published/{year:int}/{month:int?}/{day:int?}/")]
public void GetMoviesSortedByPublishedDateAsync(int year, int month = 1, int day = 1)
{
    var date = new DateTime(year, month, day);
    await _cosmosDbService.GetMoviesByPublishedDate(date);
}

your search can be specific, and it doesn't really matter if they don't supply the month, since you'll search from the start of the given year anyways (same for day)

You end up with less code, and it's simpler as well.

Noctis
  • 11,507
  • 3
  • 43
  • 82
  • 1
    What if they provide day and year? – mjwills Aug 18 '20 at 01:19
  • @mjwills fair question, but, how do you call it on the api ? `http://.../year/(null?)/2` :shrug: is that possible ? – Noctis Aug 18 '20 at 01:26
  • I think this entirely depends on how `GetMoviesByPublishedDate` is implemented. From the question, I thought this method would only return the data for that specific date. So with this solution, I'd expect a request only providing the year 2012 to return only the data for 2102-01-01, not all of the data for 2012 – devNull Aug 18 '20 at 01:34
  • @devNull yeah ..english is funny ...`return all from` can suggest either from the given date, or from that date forward – Noctis Aug 18 '20 at 03:18