CHAPTER 1
If you have been a developer for any amount of time, you will undoubtedly have heard the term API, which stands for application programming interface. For the purposes of this ebook, one way to think of these is small bits of code that allow other systems to access data and communicate. There are probably more systems out there that make use of APIs than you can shake a stick at.
When you use a website or a mobile application, these systems present data to the user in a way that is pleasing to the eye and looks all pretty on your device screen. Developers spend a lot of time making the UI of a system user-friendly. The user types a URL into the browser, and this opens a website that then displays data, videos, images, and other content formatted in a user-friendly manner. Essentially, APIs do the same thing. The only difference here is that the API does not care for formatting any of the data it returns. It is, therefore, mainly used for communication between systems.
A system can request information from an API, and the API will just return the raw data as well as other information that can be interpreted by the requesting system. This is all done without much input from a human. This means that whenever you open a mobile application (for example), chances are that the application is making use of an API call at some point in its lifecycle without you knowing it.
As a developer, another term that you’ve probably heard is API integration. This is something that APIs were designed to facilitate. If you need to take your system and integrate it with another system, chances are you’re going to make use of the other system’s APIs. Think of Shopify. Just Google the term “Shopify API,” and you will find the online Shopify API reference documentation. If you wanted to create an application that could read data from a specific Shopify store, you would integrate your application with several of the Shopify APIs to enable the transfer of data between your system and the Shopify system.
This is my first port of call whenever I need to integrate between two systems. Searching for the API reference documentation is an essential part of understanding the system API that you are integrating with. And you better believe that not all API documentation is created equal. Some documentation needs a little more research to make sense of.
Be that as it may, the importance of APIs cannot be underestimated. Postman released the 2021 State of the API Report, which you can find here.
The data was compiled by surveying 28,000 developers and combining that with what was observed on the Postman platform. There were seven key findings published in this report, which are as follows:
The term that jumps out to me is API-first. This means that developers are inclined to design and define APIs and API schemas before beginning with development. This is sometimes called bottom-up design. This makes it obvious that APIs are regarded as an essential component in many developed systems. The chances that you as a developer will be exposed to an API in some shape or form during your career is very good.
In a blog post, Postman categorizes APIs by who has access to them. These are:
Internal APIs are private APIs that are used by a team, company, or organization, while external APIs are APIs that are publicly accessible for anyone to use. Partner APIs are APIs that are private and only shared with specific integration partners outside of the organization.
There are also several API architectures available. Some of these include:
The word REST should jump out at you here. REST stands for REpresentational State Transfer. We will be taking a closer look at REST in the next section, but REST APIs are probably one of the most common APIs used today.
Unfortunately, there are some challenges when creating and consuming APIs. I alluded to the lack of proper documentation earlier in this section, and this is a very real problem. The documentation created by the API providers is often listed as the number one obstacle in helping consumers understand how to implement the API.
Another challenge is a lack of knowledge. Simply consuming an API is vastly different from actually developing an API. In this book, I will attempt to elucidate both.
You can find the GitHub repository for the code in this book here. Let us start by having a closer look at REST and what it means to use a REST API.
As mentioned in the previous section, REST stands for REpresentational State Transfer, and it is a software architectural style that developers use when developing APIs. The REST pattern provides a simple, uniform interface that can be used to access data (including media and other digital resources) via web URLs. The concepts of REST are basically:
There are a lot of opinions as to what REST is and isn’t. The basic idea, however, was summarized as follows in a Postman blog:
REST helps you better organize your digital resources and the operations you can perform against them. It’s about establishing a shared language to describe your digital capabilities and enable wider collaboration around their web usage.
You will notice that the summary states the organization of digital resources. This is a key concept in understanding REST.
When talking about REST, we are referring to a URL that points to a resource. Resources are a representation of the objects in your system, such as products, purchase orders, invoices, or employees. In other words, resources are the things in your system that you might want to insert, update, delete, or read.
Some developers think of resources as entities, but it is important to note that resources and entities are not always the same thing. They could be, but resources can also denote one or more entities.
Therefore, a resource can be an employee (for example), but it can also be a sales order along with its associated sales order lines and customer information. Resources can therefore be a collection of entities or only a single entity. At the end of the day, you need some data from an API, and the API will combine all the related information across several entities (or a single entity) and return to you a single representation of this data for use in your system.
The API we will be creating will be concerned with book information for my library of books. It will contain basic book information such as ISBN, title, and description. You can make this book information as verbose as you want to, but for this project, I have kept the information about a book in my library simple.
Our focus is creating an API, but it would be incomplete for me to not go through the initial creation and setup of the project. To this end, I will show you how to get a basic ASP.NET Core Web API project created that targets .NET 5.0. I will also show you how to set up a basic data service using Entity Framework so that we can have some data to test our API with.
For this example project, I will be using Visual Studio 2019 Version 16.9.4, but you can also use the Community (free) edition of Visual Studio.

Figure 1: Creating a new project
Start by creating a new ASP.NET Core Web API application using the available project templates from the Create a new project screen in Visual Studio. As you can see from Figure 1, I will be creating a C# application.
On the next screen (not shown), you can choose a name for your project and solution. You can call this what you like, but I am creating a book repository API, so I’m calling the solution BookRepositoryAPI and the project BookRepository.

Figure 2: Choosing the target framework
On the next screen (Figure 2), you can choose your target framework; the one we will be using in this project is the .NET Framework 5.0. I will not be adding any authentication. You can click Create to create your project.

Figure 3: The boilerplate code
Visual Studio will now create a basic web API project with some boilerplate code (WeatherForecast) as shown in Figure 3.

Figure 4: Uncheck Launch browser
Since I am creating an API, there is no user interface to debug. For this reason, I am going to uncheck the Launch browser option in the project properties as shown in Figure 4. To get to this option, right-click your BookRepository project in the Solution Explorer window and select Properties from the context menu. On the BookRepository properties page, select the Debug tab, and you will see the option to launch a browser. Uncheck this.
A little further down on the properties page, you will see the web server settings, as shown in Figure 5.

Figure 5: My web server settings
Pay special attention to the URL next to the Enable SSL checkbox. Your port will most likely be different than the one in Figure 5, which is my URL. Copy this URL, save these changes, and run your API project.
Because you have set the project not to launch a web browser, you will just see that Visual Studio is running. If you right-click IIS Express in your taskbar, you will see all the currently running sites.

Figure 6: Running applications in IIS Express
BookRepository will be one of the running sites, as seen in Figure 6.

Figure 7: URLs for running applications in IIS Express
After clicking the BookRepository site in the running applications screen (Figure 6), you will see the URLs that can be used to access the API. Note that there are the HTTP and HTTPS URLs there that were listed on the project properties page in Figure 5.
With your application still running, open a browser and enter the URL https://localhost:44371/WeatherForecast into the address bar, keeping in mind to replace the port with the port listed on your property page.

Figure 8: Calling the API in the browser
As you can see in Figure 8, the default API boilerplate code returned some JSON with weather-related data. Congratulations, you have just done a GET request on the API you created. It is the most common HTTP method to use. There are others and we will have a look at these in later sections of this book. For now, you have just made a request to the server and told it that you want the information on the WeatherForecast resource. The server dutifully responded by returning you JSON containing the WeatherForecast data.
The goal of our API is to access data against a real database. For this reason, we will be adding a data project called BookRepository.Data to the solution. Right-click your BookRepositoryAPI solution and add a C# class library project.

Figure 9: Targeting .NET 5.0
As you can see in Figure 9, the class library targets .NET 5.0.

Figure 10: Adding Entity Framework Core
Once the BookRepository.Data project has been added, we need to install the NuGet package Microsoft.EntityFrameworkCore to the data project, as seen in Figure 10.

Figure 11: Install the latest stable EF Core NuGet package
Click Install to add Microsoft.EntityFrameworkCore to the data project. When all is done, your solution will look like Figure 12. Expanding the Dependencies under the BookRepository.Data project, you will see that Microsoft.EntityFrameworkCore (5.0.11) is listed as a dependency.
Entity Framework Core (EF Core) serves as an O/RM that allows developers to:
When you use EF Core, data access is provided using a model. This comprises entity classes and a context object and represents a database session, allowing you to query and save data.

Figure 12: EF Core added to the data project references
It would not, however, make much sense if we didn’t add any entities to work with. Let’s do that next.
Earlier in this chapter, we stated that resources could contain several entities. Now, we will create some of these entities for our BookRepository project. Our project will be dealing with books, but so far we have nothing that defines exactly what a book is. This is the job of an entity. It tells our application what a book entity will look like.
As before (Figure 9), go ahead and add another class library project called BookRepository.Core to your solution. Again, select .NET 5.0 as the target framework and create the project. You can delete the default Class1.cs class or rename it if you like. What you need to end up with is a class called Book.cs.
You can see this entity in Code Listing 1. It simply contains properties that describe a book in our repository. For now, we will just add some basic information about a book, such as the ISBN and title.
Code Listing 1: The Book entity
|
{ public class Book { public int Id { get; set; } public string ISBN { get; set; } public string Title { get; set; } public string Description { get; set; } public string Publisher { get; set; } } } |
When you have added your Book entity, your solution will look like Figure 13.

Figure 13: The Book entity
Let us swing back to our data project and flesh this out a bit by adding an interface for our book data.
We need to add an interface for the book data so that we can tell our API what methods need to be implemented in the data service we want to create. Once the data service implements the IBookData interface, it will be up to the data service to decide exactly how to provide the implementation of the interface.
Inside the BookRepository.Data project, add an interface called IBookData. You can see the code for the interface in Code Listing 2.
Code Listing 2: The IBookData interface
|
using System.Collections.Generic; namespace BookRepository.Data { public interface IBookData { IEnumerable<Book> ListBooks(); Book GetBook(int Id); Book UpdateBook(Book bookData); Book AddBook(Book newBook); int Save(); } } |
You can see that you will have to reference the BookRepository.Core project to use the Book entity in your BookRepository.Data project. All this interface does is tell the data service that it must implement some logic to return a list of books, to get a specific book, to update a book, to add a book, and to save a book. By using an interface, we can decouple the data access service by allowing it to implement the IBookData interface. No matter what our data service looks like, as long as it implements the interface, it will be able to be injected into our services collection.
Note: Our interface will change when we start creating the API, specifically to include async methods. For now, I just want to get the basics in and create the DbContext.
The next thing we need to do is implement a DbContext.
A DbContext instance represents a session with the database. It allows us to save and query entity instances. Delete the existing Class1.cs file in the BookRepository.Data project and add a new class called BookRepoDbContext.cs. You can see the code for the BookRepoDbContext.cs class in Code Listing 3.
The API will work with Book entities; therefore, the BookRepoDbContext simply has a property of type DbSet<Book>. This tells Entity Framework that I want to query, add, delete, and update books. You will see that I also had to add references to the BookRepository.Core project and to Microsoft.EntityFrameworkCore.
I do agree that the name DbSet is not particularly clear about its purpose. If you ever doubt what the purpose of a class is in .NET, you can view the metadata by clicking it and pressing F12. The code comments here are your friend in helping you understand what the particular class does.
Code Listing 3: The BookRepoDbContext
|
using Microsoft.EntityFrameworkCore; namespace BookRepository.Data { public class BookRepoDbContext : DbContext { public DbSet<Book> Books { get; set; } } } |
The property on the BookRepoDbContext will allow us to work with our database. We will be using LocalDB as our database, and it is installed when you install Visual Studio. To check if LocalDB is installed, run sqllocaldb info in the command prompt.

Figure 14: Check if LocalDB is installed
The API will use the built-in LocalDB database that is listed in the output displayed in Figure 14. To see more info about the MSSQLLocalDB instance, run the command sqllocaldb info mssqllocaldb from the command prompt, as you can see in Figure 15.

Figure 15: Info regarding MSSQLLocalDB
You can also view the LocalDB instance in Visual Studio by going to View > SQL Server Object Explorer. This instance may contain several databases, and it is this instance that we need to create a connection to. This we need to specify in the appsettings.json file that should be in your BookRepository project.
Code Listing 4: The appsettings.json file
|
"Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "AllowedHosts": "*", "ConnectionStrings": { "BookConn": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=BookRepo;Integrated Security=True" } } |
The code for the appsettings.json file is presented in Code Listing 4. All I have done is add a new section called ConnectionStrings that contains a key and value pair for the various database connections we want to use. This section can contain more than one database connection, hence the section ConnectionStrings being plural. The key for our database connection is called BookConn, and the value is the connection string to the database called BookRepo (a database that does not exist yet).
Next, we need a way to tell the DbContext about this connection to the database we would like to use. We will do this in the ConfigureServices method of the Startup.cs class. Modify the ConfigureServices method as illustrated in Code Listing 5.
Code Listing 5: The ConfigureServices method
This registers the DbContext as a service in the IServiceCollection.
The UseSqlServer method tells the Entity Framework about the DbContext being used in the API.
Tip: You will probably need to add the Microsoft.EntityFrameworkCore and Microsoft.EntityFrameworkCore.SqlServer NuGet packages to the BookRepository project.
We also specify that DbContext pooling should be used, which allows for increased throughput. This is because instances of DbContext are reused instead of having new instances created for each request.
Note: Pay attention to the fact that the key being used for our connection in the appsettings.json file has to be an exact match to the string being passed to the GetConnectionString method seen in Code Listing 5.
Now that we have registered the DbContext as a service in the IServiceCollection, we need to change the BookRepoDbContext class slightly to tell it about the connection string we specified along with any other options specified with the DbContextOptionsBuilder in the ConfigureServices method.
Code Listing 6: The modified BookRepoDbContext class
|
using Microsoft.EntityFrameworkCore; namespace BookRepository.Data { public class BookRepoDbContext : DbContext { public BookRepoDbContext(DbContextOptions<BookRepoDbContext> dbContextOptns) : base(dbContextOptns) { } public DbSet<Book> Books { get; set; } } } |
Swing back to the BookRepoDbContext class and add a constructor that takes DbContextOptions as a parameter (seen in Code Listing 6). We are now finally ready to use database migrations to create the database we specified in the connection string specified in the appsettings.json file (seen in Code Listing 4).
For this portion of the process, we will be using the dotnet tool. The dotnet tool should be already installed; you can check by typing the command dotnet --info at the command prompt. If you do not have the dotnet tool installed, you can install it by running the command dotnet tool install –global dotnet-ef.
You can find more information on installing the dotnet tool by visiting this website.
We also need to make sure that we have the Microsoft.EntityFrameworkCore.Design NuGet package added to our BookRepository and BookRepository.Data projects. After adding the NuGet package, your solution should now look as illustrated in Figure 16.

Figure 16: The BookRepositoryAPI solution with the installed NuGet packages
It is extremely important that the NuGet package versions of Entity Framework Core match between the BookRepository and BookRepository.Data projects. My versions are 5.0.11.
Before adding and running any migrations, I always run the command dotnet ef dbcontext info, also specifying the startup project. This way I can catch any missing NuGet packages (you might miss the addition of the Design NuGet package, for example).
Open a command prompt and change the directory to the BookRepository.Data project folder. Now run the following command: dotnet ef dbcontext info -s ..\BookRepository\BookRepository.csproj. The output should look as illustrated in Figure 17.

Figure 17: Running dbcontext info
It is important to specify the startup project using the -s option. This is because the BookRepository.Data project does not know about the Startup.cs class that contains the ConfigureServices method.
If this command produced the output as illustrated in Figure 17, we should be in a good position to add the migration to our data project.
Database migrations will enable us to keep the database in sync when a model in our project changes. EF Core will compare the current data model to a snapshot of the old model and figure out what has changed. It will then generate migration files and apply those migrations to the database and record the history in a table. This is nice because it allows you to see which migrations have been applied.
Keeping the command prompt pointed to the BookRepository.Data project, run dotnet ef migrations from the command line.
Code Listing 7: Available commands
From the output, you will notice that we have a few commands available to us. These are:
In our case, we want to add a new migration. From the command prompt, run the dotnet ef migrations add --help command.
Code Listing 8: The add command arguments and options
Usage: dotnet ef migrations add [arguments] [options] Arguments: <NAME> The name of the migration. Options: -o|--output-dir <PATH> The directory to put files in. Paths are relative to the project directory. Defaults to "Migrations". --json Show JSON output. Use with --prefix-output to parse programatically. -n|--namespace <NAMESPACE> The namespace to use. Matches the directory by default. -c|--context <DBCONTEXT> The DbContext to use. -p|--project <PROJECT> The project to use. Defaults to the current working directory. -s|--startup-project <PROJECT> The startup project to use. Defaults to the current working directory. --framework <FRAMEWORK> The target framework. Defaults to the first one in the project. --configuration <CONFIGURATION> The configuration to use. --runtime <RUNTIME_IDENTIFIER> The runtime to use. --msbuildprojectextensionspath <PATH> The MSBuild project extensions path. Defaults to "obj". --no-build Don't build the project. Intended to be used when the build is up-to-date. -h|--help Show help information -v|--verbose Show verbose output. --no-color Don't colorize output. --prefix-output Prefix output with level. |
From the output in Code Listing 8, you will notice that you can give the migration a name when adding it. I am simply going to call this migration Initial to show that it was the first one created. To add this migration, run the command listed in Code Listing 9 from the command prompt.
Code Listing 9: Adding the first migration
dotnet ef migrations add Initial -s ..\BookRepository\BookRepository.csproj |
Notice that we still need to specify the startup project by specifying the -s option after the migration name Initial.
Code Listing 10: Migration successfully added
Build started... Build succeeded. Done. To undo this action, use 'ef migrations remove' |
After the migration has been added, have a look at the BookRepository.Data project in Visual Studio.

Figure 18: Migrations added to BookRepository.Data project
A Migrations folder has been added to the BookRepository.Data project. You will also see the Initial migration created, called 20211107110645_Initial.cs.
We are now ready to create the database in the LocalDB instance.
Note: It was here that I found out that the BookRepository.Data project required NuGet packages called Microsoft.EntityFrameworkCore.Relational and Microsoft.EntityFrameworkCore.SqlServer. Just add these NuGet packages to the data project and you should be good to go.
From the command prompt, run the dotnet build command. If you get a successful build, go ahead and run the command as listed in Code Listing 11.
Code Listing 11: Create the database on LocalDB
When the command has been completed, you can open the SQL Server Object Explorer and expand the Databases folder under the MSSQLLocalDB instance to see the created database as illustrated in Figure 19.

Figure 19: The created BookRepo database
Expanding the Tables folder, you will see the __EFMigrationsHistory table as well as the Books table.

Figure 20: Viewing the Books table columns
Expanding the columns, I see that I have forgotten to add a column for Author. How do I add this column to my database? It is here that database migrations show their true worth.
Previously I saw that I had forgotten to add an Author column. This is a problem because it is information that I will need going forward. Luckily, updating the database is simple. It only requires that we update our Book class in the BookRepository.Core project, as seen in Code Listing 12.
Code Listing 12: The updated Book class
namespace BookRepository.Core { public class Book { public int Id { get; set; } public string ISBN { get; set; } public string Title { get; set; } public string Description { get; set; } public string Publisher { get; set; } public string Author { get; set; } } } |
All I have done is add a new property for Author. The next thing I need to do is add a new migration with this change. As mentioned earlier in this section, database migrations enable us to keep the database in sync when a model in our project changes. EF Core will compare the current data model to a snapshot of the old model and figure out what has changed.
Code Listing 13: Adding the new database migrations
dotnet ef migrations add BookModelUpdate1 -s ..\BookRepository\BookRepository.csproj |
Run the command in Code Listing 13 to create a new database migrations file called BookModelUpdate1, and then you will see that it has been added to the Migrations folder in the BookRepository.Data project, as seen in Figure 21.

Figure 21: The BookModelUpdate1 database migration
All that remains to be done is to run the database migrations by executing the command in Code Listing 14 in the command prompt.
Code Listing 14: Updating the database
dotnet ef database update -s ..\BookRepository\BookRepository.csproj |
After this has been completed, refresh the Books table in the database and you will see that the Author column has been added, as shown in Figure 22.

Figure 22: The updated Books table
This is generally the process that you will follow when modifying and adding to the database when our models change in the project. All that remains for us to do now is to add a data access service.
Create a new class called SqlData in the BookRepository.Data project, and let it implement the IBookData interface. The complete code is found in Code Listing 15, so I will not go through this in any detail. Suffice to say that it is quite basic and all that we need to start building our API.
Code Listing 15: The SqlData data service class
using BookRepository.Core; using Microsoft.EntityFrameworkCore; using System.Collections.Generic; using System.Linq; namespace BookRepository.Data { public class SqlData : IBookData { private readonly BookRepoDbContext _database; public SqlData(BookRepoDbContext database) { _database = database; } public Book AddBook(Book newBook) { _ = _database.Add(newBook); return newBook; } public Book GetBook(int Id) { return _database.Books.Find(Id); } public IEnumerable<Book> ListBooks() { return _database.Books.OrderBy(b => b.Title); } public int Save() { return _database.SaveChanges(); } public Book UpdateBook(Book bookData) { var entity = _database.Books.Attach(bookData); entity.State = EntityState.Modified; return bookData; } } } |
Lastly, we need to register our data access service in the services collection. Switch to the BookRepository project and open the Startup.cs class.
Code Listing 16: Registering the data service
We are adding this service registration and telling our API that whenever something asks for an implementation of IBookData, give it the SqlData class, as seen in Code Listing 16.
Code Listing 17: The modified ConfigureServices method
This line of code is added to the ConfigureServices method of the Startup.cs class, as seen in Code Listing 17. This is all that we will need to wire up our project with a SQL database to work with. I will leave populating the Books table with data up to you. It is quite easy to do. Right-click on the table in SQL Server Object Explorer, select View Data, and start plugging in some data to work with.

Figure 23: The data in my Books table
Figure 23 shows the data that I have added to my Books table.
Throughout this book, I will be using Postman to test the API. Postman is a free tool that you can download here. Once you have installed Postman, you can start using it to call APIs. As a quick start, I will show you how to perform a simple GET operation on the official public API for the public-apis project.
First, have a look at the project page on GitHub.
The URL in Code Listing 18 is the public APIs URL. All that the GET will return is a list of all public APIs currently cataloged in the project.
Code Listing 18: Public APIs URL
https://api.publicapis.org/entries |
In Postman, make sure that you have selected a GET operation, and then paste the URL from Code Listing 18 into the URL field, as illustrated in Figure 24. Click Send to make the GET request.

Figure 24: The URL for the GET operation
Postman will make the API call and return the response to you from the GET request as shown in Figure 25.

Figure 25: The GET response JSON
As you can see, it simply returns a list of APIs in its catalog. The contents of the response here are of no concern to us (except for perhaps the axolotl API—everyone loves axolotls). What is important is understanding what Postman can do for us.
Postman gives us a way to test the workings of our API, which, as you can imagine, is going to be created without a UI. This book is not going to go in depth on the topic of using Postman; the examples will illustrate the most basic usage. However, I encourage you to get to know Postman better. It is an extremely powerful tool and knowing how to use it properly will prepare you for your future API development.