diff --git a/server/Controllers/v1/QuotesController.cs b/server/Controllers/v1/QuotesController.cs
new file mode 100644
index 0000000..fe35739
--- /dev/null
+++ b/server/Controllers/v1/QuotesController.cs
@@ -0,0 +1,55 @@
+using Microsoft.AspNetCore.Mvc;
+using server.Models;
+using server.Persistence.Repositories;
+
+namespace server.Controllers.v1
+{
+ [ApiController]
+ [ApiVersion("1.0")]
+ [Route("/api/quotes")]
+ [Produces("application/json")]
+ public class QuotesController : ControllerBase
+ {
+ private readonly IQuoteRepository _quoteRepository;
+
+ public QuotesController(IQuoteRepository quoteRepository)
+ {
+ _quoteRepository = quoteRepository;
+ }
+
+ ///
+ /// Gets a collection of quotes.
+ ///
+ /// Filter parameters passed from query string.
+ /// Returns the collection of quotes requested.
+ /// Not a valid request.
+ /// Failed to get quotes.
+ /// Returns a collection of quotes.
+ [MapToApiVersion("1.0")]
+ [HttpGet]
+ [ProducesResponseType(typeof(List), StatusCodes.Status200OK)]
+ [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
+ [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status500InternalServerError)]
+ public async Task>> GetQuotesAsync([FromQuery] QuoteFilter? filter)
+ {
+ try
+ {
+ var quotes = await _quoteRepository.GetQuotesAsync(filter);
+ return Ok(quotes);
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(e);
+ return Problem(detail: "Failed to get quote", statusCode: 500);
+ }
+ }
+
+ // TODO
+ [MapToApiVersion("1.0")]
+ [HttpGet]
+ public async Task> GetQuoteByIdAsync(int id)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/server/Models/QuoteFilter.cs b/server/Models/QuoteFilter.cs
new file mode 100644
index 0000000..1abedf3
--- /dev/null
+++ b/server/Models/QuoteFilter.cs
@@ -0,0 +1,10 @@
+namespace server.Models
+{
+ public class QuoteFilter
+ {
+ ///
+ /// Use to filter quote by narrator.
+ ///
+ public string? Narrator { get; set; } = null;
+ }
+}
diff --git a/server/Persistence/Repositories/IQuoteRepository.cs b/server/Persistence/Repositories/IQuoteRepository.cs
new file mode 100644
index 0000000..daee7d7
--- /dev/null
+++ b/server/Persistence/Repositories/IQuoteRepository.cs
@@ -0,0 +1,10 @@
+using server.Models;
+
+namespace server.Persistence.Repositories
+{
+ public interface IQuoteRepository
+ {
+ Task> GetQuotesAsync(QuoteFilter filter);
+ Task GetQuoteByIdAsync(int id);
+ }
+}
diff --git a/server/Persistence/Repositories/QuoteRepository.cs b/server/Persistence/Repositories/QuoteRepository.cs
new file mode 100644
index 0000000..44454b8
--- /dev/null
+++ b/server/Persistence/Repositories/QuoteRepository.cs
@@ -0,0 +1,32 @@
+using server.Models;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace server.Persistence.Repositories
+{
+ public class QuoteRepository : IQuoteRepository
+ {
+ private readonly IDbContext _context;
+
+ public QuoteRepository(IDbContext context)
+ {
+ _context = context;
+ }
+
+ // TODO: Implement GetQuotesAsync() method.
+ public Task> GetQuotesAsync(QuoteFilter filter)
+ {
+ throw new NotImplementedException();
+ }
+
+ // TODO: Implement GetQuoteByIdAsync() method.
+ public Task GetQuoteByIdAsync(int id)
+ {
+ throw new NotImplementedException();
+ }
+
+ }
+}
diff --git a/server/Program.cs b/server/Program.cs
index b804123..ea489bc 100644
--- a/server/Program.cs
+++ b/server/Program.cs
@@ -56,6 +56,8 @@ builder.Services.AddScoped();
builder.Services.AddScoped();
+builder.Services.AddScoped();
+
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();