Generic endpoint for DateTime or three different endpoints?

Viewed 290

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);
    }
}
3 Answers

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

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.

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.

Related