DEV Community

Cover image for Lets make a simple API!
Cameron Young
Cameron Young

Posted on

Lets make a simple API!

We are going to create a simple REST API using a compiled language. In this example we will use asp.net core to create an API Server. We will cover the post, get, put and delete actions.

There are a lot of options to create an API. Python, JavaScript, Java and many others. In this example we will drink the asp.net kool aid. This ecosystem has great project generation and in the midwest it is the dominant choice for many companies.

Image of a vanilla flower

In this post, I'll walk through building "Store API" style functionality.

For those following along at home, all the code is in a GitHub repository. Each commit in the repo corresponds to a step in this post.

Step 1 - Generate the project

This assumes you have the .NET Core SDK or Visual Studio Community installed. If you have Visual Studio Community do the following:

Go to File > New > Project…

Select the Visual C# project category and then select ASP.NET Web Application (.NET Framework)

Name your project AspNetWebApiRest and click OK

Select the Empty project template and click OK (don’t check any boxes to add core references)

dotnet new webapi -o StoreApi
cd StoreApi
dotnet add package Microsoft.EntityFrameworkCore.InMemory
code -r ../StoreApi
Enter fullscreen mode Exit fullscreen mode

This generates a simple ASP.NET API website. We are using an InMemory database for speedy development. This would not be a proper set up for a live site, it is not complicated to add one later.

Step 2 - Build out the Domain Model

In this step, we build a few simple classes.

/Models/Products.cs

public class Products
{
   public int Id { get; set; }
   public string Sku { get; set; }
   public string Name { get; set; }
   public string Description { get; set; }
   public decimal Price { get; set; }
   public bool IsAvailable { get; set; }

   public int CategoryId { get; set; }

   /// <summary>
   /// This is to stop a circular reference
   /// </summary>
   [JsonIgnore]
   public virtual Category Category { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

/Models/User.cs

public class User
{
    public int Id { get; set; }
    public string Email { get; set; }
    public virtual List<Order> Orders { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

/Models/Category.cs

public class Category
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual List<Product> Products { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

/Models/Order.cs

public class Order
{
    public int Id { get; set; }
    public DateTime OrderDate { get; set; }
    public int UserId { get; set; }

    /// <summary>
    /// This is to stop a circular reference
    /// </summary>
    [JsonIgnore]
    public virtual User User { get; set; }
    public virtual List<Product> Products { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

Step 3 - Build out Entity Framework Core

We will install two nuget packages to get the ball rolling.

Install-Package Microsoft.EntityFrameworkCore.InMemory -Version 3.1.7

And

Install-Package Microsoft.EntityFrameworkCore -Version 3.1.7
Enter fullscreen mode Exit fullscreen mode

In this section we will first create the ShopContext class. Here we will add entity framework core into the project and seed the database.

/Models/ShopContext.cs

public class ShopContext : DbContext
{
    public ShopContext(DbContextOptions<ShopContext> options) : base(options)
    { }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Category>().HasMany(c => c.Products).WithOne(a => a.Category).HasForeignKey(a => a.CategoryId);
        modelBuilder.Entity<Order>().HasMany(o => o.Products);
        modelBuilder.Entity<Order>().HasOne(o => o.User);
        modelBuilder.Entity<User>().HasMany(u => u.Orders).WithOne(o => o.User).HasForeignKey(o => o.UserId);

        // modelBuilder.Seed();
    }

        public DbSet<Product> Products { get; set; }
        public DbSet<Category> Categories { get; set; }
        public DbSet<Order> Orders { get; set; }
        public DbSet<User> Users { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

Let us explore what is going on in this class. We will use the documentation to configure the DbContext. After that we had to tell Entity Framework the relation between those model classes. We are using the Fluent API in Entity Framework Core for our mappings mappings. You can dive deeper into how to use them here but going into detail about that concept is outside the scope of this writing.

We will also add an extension method to seed the database with values

/Models/ModelBuilderExtensions.cs

This file is too long to give a preview for so just copy it using the link above.

With all of that the project is now set up for us to start working on some crud operations. So let's get to it.

Step 4 - Build out the GET

Here we are going to create the GetAllProducts and GetProduct(int id) methods. As the name implies the get all products will all of the products. The get product will use the product id to pull back that specific product.

public class ProductsController : ControllerBase
{
    private readonly ShopContext _context;

    public ProductsController(ShopContext context)
    {
        _context = context;

        _context.Database.EnsureCreated();
    }

    [HttpGet]
    public IActionResult GetAllProducts()
    {
        return Ok(_context.Products.ToArray());
    }

    [HttpGet("{id}")]
    public IActionResult GetProduct(int id)
    {
        var product = _context.Products.Find(id);
        return Ok(product);
    }
}
Enter fullscreen mode Exit fullscreen mode

We have already created our Context class. The context object is how we interact with the runtime database. Using the Find and ToArray call from the context object are the backbone of our methods.

That’s it for the GET calls!!!

Step 5 - Build out the POST

Here we will need some extra software to implement this. I use Insomnia but there is nothing wrong with anything else. We will use the following json to test our new code.

{
    "name": "Old Skater Jeans",
    "sku": "AWMGSYYJ",
    "price": 68.00,
    "isAavailable": true,
    "categoryId": 1
}
Enter fullscreen mode Exit fullscreen mode

Using the api client of your choice we will put that json in the body of your PUT call. We will add the following method to now receive PUTs.

[HttpPost]
public ActionResult<Product> PostProduct([FromBody] Product product)
{
    _context.Products.Add(product);
    _context.SaveChanges();
        return CreatedAtAction (
            "GetProduct",
            new { id = product.Id },
            product
        );
}
Enter fullscreen mode Exit fullscreen mode

With this in our controller we can accept a call and we can test this and see the update in the system.
In this method we are using EF core and CreatedAtAction call. The EF is just an add for the moment and the CreatedAtAction returns a created (201) response with a location header. In another post we will add to this but for now this is a simple example.

Step 6 - Build out the PUT

In this section we will work on the update portion of the api. We will use similar json but with some changes.

{
    "name": "Super Old Skater Jeans",
    "sku": "AWMGSYYZZ",
    "price": 168.00,
    "isAavailable": true,
    "categoryId": 1
}
Enter fullscreen mode Exit fullscreen mode

There are just a few updates to the name, sku and price. We will have to use the correct path to trigger a put, https://localhost:{your port number}/products/{the id of the product you want to update}. Now just so we are on the same page there should not be curly braces in your actual url. That's just for us.

We will add the new put method now.

[HttpPut("{id}")]
public IActionResult PutProduct([FromRoute] int id, [FromBody] Product product)
{
    if (id != product.Id)
        {
            return BadRequest();
        }
    _context.Entry(product).State = EntityState.Modified;

    try
        {
            _context.SaveChanges();
        }

    catch (DbUpdateConcurrencyException)
        {
            if (_context.Products.Find(id) == null)
            {
                return NotFound();
            }
                throw;
        }
            return NoContent();
}
Enter fullscreen mode Exit fullscreen mode

Here we are working with many of the same objects, nothing new or fancy. We are using BadRequest, NotFound, and NoContent. Using EntityState we have a lot of options but it is too much to go over in this small section. From the documentation it is "Modified: the entity is being tracked by the context and exists in the database, and some or all of its property values have been modified". That should be enough to keep us going.

Step 7 - Build out the DELETE

Ok we are at the home stretch. The delete will act the same way as our put method. Except in our tool we will select delete instead of put to see some action.

[HttpDelete("{id}")]
public async Task<ActionResult<Product>> DeleteProduct(int id)
{
    var product = await _context.Products.FindAsync(id);
    if (product == null)
    {
        return NotFound();
    }

    _context.Products.Remove(product);
    await _context.SaveChangesAsync();
    return product;
}
Enter fullscreen mode Exit fullscreen mode

This is it for our basic api!! We will add some more changes to this project in a later post. Some of the things we will cover will be adding a full database to this, paging, filtering, searching and sorting and more. Thank you for reading!!!!

And we're done. You can see all the changes in order by looking at the commits in order.

Or just see the final result at https://github.com/cmmsolutions/SimpleStoreAPI.

Top comments (0)