Using QuickChart API in .NET Core MVC.
If you've ever been in a situation that you got asked to provide a simple visualization of data in your application then look no further. QuickChart is an open-source API that uses the famous library Chart.Js. This service is lightweight and easy to use.
Today, I will walk you through how to generate charts using QuickChart.io API.
LET’S GET STARTED!!
1-Creating our Entities and repository.
For the purpose of this demonstration, I will be using VideoGame and Consoles as our Entities where Many videogames relate to one console.
The data will be in memory and we will mock the database repository pattern.
a-Adding the entities
- VideoGame
public class VideoGame{public int ID { get; set; }public Rating Rating { get; set; }public string Title { get; set; }public Developers Developer { get; set; }public GameConsole Console { get; set; }}
- GameConsole
public class GameConsole{public GameConsole(){VideoGames = new List<VideoGame>();}public int ID { get; set; }public string ConsoleName { get; set; }public List<VideoGame> VideoGames { get; set; }}
- Developers Enum
public enum Developers{ Activison, EA, Ubisoft}
- Rating Enum
public enum Rating{ Good, Bad, Great}
b-Adding our Respoitories
- IBaseRepo
public interface IBaseRepo<T>{int Save(T item);int Delete(T item);IQueryable<T> GetIqeryAble();}
- IVideoGameRepo
public interface IVideoGameRepo : IBaseRepo<VideoGame>{}
- IGameConsoleRepo
public interface IGameConsoleRepo : IBaseRepo<GameConsole>{}
- VideoGameRepo
public class VideoGameRepo : IVideoGameRepo{private List<VideoGame> _videoGameList;public VideoGameRepo(){_videoGameList = new List<VideoGame>();Save(new VideoGame{ ID = 0,Rating= Rating.Bad,Title ="Call of duty Bo2",Developer = Developers.Activison });Save(new VideoGame{ ID = 0,Rating= Rating.Great, Title ="Call of duty Bo3", Developer = Developers.Activison });Save(new VideoGame{ ID = 0,Rating= Rating.Good, Title ="Call of duty Bo4", Developer = Developers.Activison });Save(new VideoGame{ ID = 0,Rating= Rating.Good, Title ="Call of duty Bo5", Developer = Developers.Activison });Save(new VideoGame{ ID = 0,Rating= Rating.Great, Title ="Assasins creed 1", Developer = Developers.Ubisoft });Save(new VideoGame{ ID = 0,Rating= Rating.Bad, Title = "Assasins creed 2", Developer = Developers.Ubisoft });Save(new VideoGame{ ID = 0,Rating= Rating.Bad, Title = "Assasins creed 3",Developer = Developers.Ubisoft });Save(new VideoGame{ ID = 0,Rating= Rating.Great, Title = "Assasins creed Brotherhood", Developer = Developers.Ubisoft });Save(new VideoGame{ ID = 0,Rating= Rating.Good,Title ="Battlefield 3", Developer = Developers.EA });Save(new VideoGame{ ID = 0,Rating= Rating.Bad,Title = "Battlefield 4", Developer = Developers.EA });Save(new VideoGame{ ID = 0,Rating= Rating.Good, Title = "Battlefield 5", Developer = Developers.EA });}public int Delete(VideoGame item){var foundGame = _videoGameList.FirstOrDefault(i => i.ID == item.ID);if (foundGame == null){return 0;}else{_videoGameList.Remove(foundGame);return 1;}}public IQueryable<VideoGame> GetIqeryAble(){var queryAble = _videoGameList.AsQueryable();return queryAble;}public int Save(VideoGame item){if(item.ID == 0 && _videoGameList.Count == 0){item.ID = 1;_videoGameList.Add(item);return 1;}else if(item.ID > 0){var foundUpdatedGame = _videoGameList.FirstOrDefault(i => i.ID == item.ID);if (foundUpdatedGame != null){foundUpdatedGame = item;return 1;}else{return 0;}}else{var maxNum = _videoGameList.Max(v => v.ID);item.ID = maxNum + 1;_videoGameList.Add(item);return 1;}}}
- GameConsoleRepo
public class GameConsoleRepo : IGameConsoleRepo{public List<GameConsole> _gameConsoleList;public GameConsoleRepo(){_gameConsoleList = new List<GameConsole>();Save(new GameConsole{ ID = 0, ConsoleName = "PlayStation 5",VideoGames = new List<VideoGame>{ new VideoGame { ID = 1,Rating= Rating.Bad,Title ="Call of duty Bo2"},new VideoGame {ID = 2,Rating= Rating.Great,Title ="Call of duty Bo3" },new VideoGame {ID = 3,Rating= Rating.Good,Title ="Call of duty Bo4" } }});Save(new GameConsole{ ID = 0, ConsoleName = "PlayStation 4",VideoGames = new List<VideoGame>{ new VideoGame { ID = 4,Rating= Rating.Good,Title ="Call of duty Bo5"},new VideoGame {ID = 5,Rating= Rating.Great,Title ="Assasins creed 1"},new VideoGame {ID = 6,Rating= Rating.Bad,Title = "Assasins creed 2" } }});Save(new GameConsole{ ID = 0, ConsoleName = "Xbox One",VideoGames = new List<VideoGame>{ new VideoGame {ID = 7,Rating= Rating.Bad,Title = "Assasins creed 3"},new VideoGame {ID = 8,Rating= Rating.Great,Title = "Assasins creed Brotherhood"},new VideoGame {ID = 9,Rating= Rating.Good,Title ="Battlefield 3" } }});Save(new GameConsole{ ID = 0, ConsoleName = "Xbox Series X",VideoGames = new List<VideoGame>{ new VideoGame { ID = 0,Rating= Rating.Bad,Title = "Battlefield 4" },new VideoGame {ID = 0,Rating= Rating.Good,Title = "Battlefield 5" }}});}public int Delete(GameConsole item){var foundGame = _gameConsoleList.FirstOrDefault(i => i.ID == item.ID);if (foundGame == null){return 0;}else{_gameConsoleList.Remove(foundGame);return 1;}}public IQueryable<GameConsole> GetIqeryAble(){var queryAble = _gameConsoleList.AsQueryable();return queryAble;}public int Save(GameConsole item){if (item.ID == 0 && _gameConsoleList.Count == 0){item.ID = 1;_gameConsoleList.Add(item);return 1;}else if (item.ID > 0){var foundUpdatedGame = _gameConsoleList.FirstOrDefault(i => i.ID == item.ID);if (foundUpdatedGame != null){foundUpdatedGame = item;return 1;}else{return 0;}}else{var maxNum = _gameConsoleList.Max(v => v.ID);item.ID = maxNum + 1;_gameConsoleList.Add(item);return 1;}}}
- I have created a Generic interface that has most of the common methods that VideoGame and GameConsole repos will need and, I have implemented the corresponding interfaces to their respective concrete classes.
2-Adding Charts related models
a-Adding the Chart Types
- ChartBase
public abstract class ChartBase{public string Type { get; set; }public DataModel Data { get; set; }}
- DoughnoutChart
public class DoughnutChart : ChartBase{public DoughnutChart(){Type = "doughnut";}public object Options{get{return new{Plugins = new{datalabels = new{display = true,backgroundColor = "#ccc",borderRadius = 10,font = new{color = "red",size = 12}}}};}}}
- PieChart
public class PieChart : ChartBase{public PieChart(){Type = "pie";}public object Options{get{return new{Plugins = new{datalabels = new{display = true,align = "bottom",backgroundColor = "#ccc",borderRadius = 6,font = new{color = "red",size = 12}}}};}}}
I have created a base class and had other charts derive from it. the Options property is required when we convert the object to JSON string and POST it to the API endpoint.
b-Adding our API specific models
- DataModel
public class DataModel{ public List<string> Labels { get; set; } public List<DataSetModel> Datasets { get; set; }}
- DataSetModel
public class DataSetModel{ public string Label { get; set; } public List<int> Data { get; set; }}
- ClientResponseModel
public class ClientResponseModel{ public bool success { get; set; } public string Url { get; set; }}
C-Adding Generic Client class
- IBaseClient
public interface IBaseClient<T>{Task<ClientResponseModel> GetChartResult(T item);}
- BaseClient
public class BaseClient<T> : HttpClient, IBaseClient<T> where T : class{private string BasePath;private const string MEDIA_TYPE = "application/json";public BaseClient(string baseAddress, string basePath){BaseAddress = new Uri(baseAddress);BasePath = basePath;}public async Task<ClientResponseModel> GetChartResult(T item){try{SetupHeaders();var objectToSerialize = GetChartObject(item);var serializedJson = GetSerializedObject(objectToSerialize);var bodyContent = GetBodyContent(serializedJson);var response = await PostAsync(BasePath, bodyContent);if (response.IsSuccessStatusCode){var responseString = await response.Content.ReadAsStringAsync();var responseModel = JsonConvert.DeserializeObject<ClientResponseModel>(responseString);return responseModel;}else{throw new Exception(response.StatusCode.ToString());}}catch (Exception ex){throw new Exception($"Something went wrong {ex.Message}");}}#region Request Helpersprotected virtual void SetupHeaders(){DefaultRequestHeaders.Clear();//Define request data formatDefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(MEDIA_TYPE));}protected override void Dispose(bool disposing){base.Dispose(disposing);}protected virtual string GetSerializedObject(object obj){var serializerSettings = new JsonSerializerSettings();serializerSettings.ContractResolver =new CamelCasePropertyNamesContractResolver();var serializedJson = JsonConvert.SerializeObject(obj, serializerSettings);return serializedJson;}protected virtual StringContent GetBodyContent(string serializedJson){var bodyContent = new StringContent(serializedJson, Encoding.UTF8, "application/json");return bodyContent;}private object GetChartObject(object item, int width = 400, int height = 250){var objToSerialize = new{Width = width,Height = height,Chart = item,};return objToSerialize;}#endregion}
- Adding JSON properties to appsettings.json

- The client class will establish the connection to the API endpoint and retrieve the corresponding response from the server.
3-Creating the ChartService.
- IChartService
public interface IChartService{//Chart methodsChartBase GetVideoGameRatings();ChartBase GetVideoGameDeveloper();ChartBase GetConsoleGames();//Chart generation methodsTask<ClientResponseModel> GetVideoGameRatingChart();Task<ClientResponseModel> GetVideoGameDeveloperChart();Task<ClientResponseModel> GetConsoleGamesChart();//Chart GeneratorChartBase GetChartInstance(string type, List<string> labels, List<int> data);}
- ChartService
public class ChartService : IChartService{private readonly IBaseClient<ChartBase> _client;private readonly IVideoGameRepo _gameRepo;private readonly IGameConsoleRepo _consoleRepo;private readonly ChartFactory _chartFactory;public ChartService(IBaseClient<ChartBase> client,IVideoGameRepo gameRepo,IGameConsoleRepo consoleRepo,ChartFactory chartFactory){_client = client;_gameRepo = gameRepo;_consoleRepo = consoleRepo;_chartFactory = chartFactory;}#region Charts Methodspublic ChartBase GetVideoGameDeveloper(){var gamesList = _gameRepo.GetIqeryAble();var developerGroup = gamesList.GroupBy(i => i.Developer).OrderBy(i => i.Key);var developerList = developerGroup.Select(m => m.Count()).ToList();var chart = GetChartInstance("Pie",new List<string>(Enum.GetNames(typeof(Developers)).ToList()),new List<int>(developerList));return chart;}public ChartBase GetVideoGameRatings(){var gamesList = _gameRepo.GetIqeryAble();var ratingsGroup = gamesList.GroupBy(i => i.Rating).OrderBy(i => i.Key);var ratingsList = ratingsGroup.Select(m => m.Count()).ToList();var chart = GetChartInstance("Pie",new List<string>(Enum.GetNames(typeof(Rating)).ToList()),new List<int>(ratingsList));return chart;}public ChartBase GetConsoleGames(){var consoleList = _consoleRepo.GetIqeryAble();consoleList = consoleList.OrderBy(i => i.ID);var consoleName = consoleList.Select(c => c.ConsoleName);var consoleGames = consoleList.Select(c => c.VideoGames.Count);var chart = GetChartInstance("Doughnut", new List<string>(consoleName),new List<int>(consoleGames));return chart;}#endregion#region Charts Cinversionspublic async Task<ClientResponseModel> GetConsoleGamesChart(){var consoleGamesChart = GetConsoleGames();var response = await _client.GetChartResult(consoleGamesChart);return response;}public async Task<ClientResponseModel> GetVideoGameDeveloperChart(){var gameDevelopers = GetVideoGameDeveloper();var response = await _client.GetChartResult(gameDevelopers);return response;}public async Task<ClientResponseModel> GetVideoGameRatingChart(){var videoGamesRating = GetVideoGameRatings();var response = await _client.GetChartResult(videoGamesRating);return response;}#endregionpublic ChartBase GetChartInstance(string type, List<string> labels, List<int> data){var chartInstance = _chartFactory.GetChartInstance(type);chartInstance.Data = new DataModel{Labels = new List<string>(labels),Datasets = new List<DataSetModel>{ new DataSetModel{Data = new List<int>(data)}}};return chartInstance;}}
- ChartFactory
public class ChartFactory{public ChartBase GetChartInstance(string userSelection){if (userSelection == "Pie")return new PieChart();return new DoughnutChart();}}
- Creating a ViewModel for presentation
ChartViewModel
public class ChartViewModel{public ChartViewModel(){Charts = new List<ClientResponseModel>();}public List<ClientResponseModel> Charts { get; set; }}
4- Specifying the Dependencies
- Startup.cs
public void ConfigureServices(IServiceCollection services){var chartClientBaseAddress = Configuration.GetSection("chartClientBaseAddress").Value;var chartClientBasePath = Configuration.GetSection("chartClientBasePath").Value;services.AddScoped<IBaseClient<ChartBase>>(c => new BaseClient<ChartBase>(chartClientBaseAddress, chartClientBasePath));services.AddScoped<IChartService, ChartService.Helper.ChartService>();services.AddScoped<ChartViewModel>();services.AddSingleton<IGameConsoleRepo, GameConsoleRepo>();services.AddSingleton<IVideoGameRepo, VideoGameRepo>();services.AddSingleton<ChartFactory>();services.AddControllersWithViews();}
5-Wiring things up.
- HomeController
public class HomeController : Controller{private readonly ILogger<HomeController> _logger;private readonly IChartService _chartService;public HomeController(ILogger<HomeController> logger,IChartService chartService){_logger = logger;_chartService = chartService;}public async Task<IActionResult> Index(){var consoleGamesChart = _chartService.GetConsoleGamesChart();var videoGameDeveloperChart = _chartService.GetVideoGameDeveloperChart();var videoGameRatingChart = _chartService.GetVideoGameRatingChart();await Task.WhenAll(consoleGamesChart,videoGameDeveloperChart, videoGameRatingChart);var viewModel = new ChartViewModel{Charts = new List<ClientResponseModel> { consoleGamesChart.Result,videoGameDeveloperChart.Result, videoGameRatingChart.Result }};return View(viewModel);}public IActionResult Privacy(){return View();}[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]public IActionResult Error(){return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });}}
- Index.cshtml
@model ChartViewModel@{ViewData["Title"] = "Home Page";}<div class="text-center"><h1 class="display-4 text-info">Generate Chart Images using QuickChart API</h1><p class="text-dark display-4">Samples Generated</p><div class="row">@foreach (var image in Model.Charts){<div class="col-4"><div class="card"><img class="card-img-top" src="@image.Url" /></div></div>}</div></div>
- Final Result

Conclusion
In this demonstration I haven’t expanded on all the chart types there are in QuickChart. If you check the solution on Github(link). the project is built in a way you can expand more on it and apply different charts to the data you'd like to represent.
Quick Chart Docs
Git Hub Repo
LinkedIn