Lesson Weekend

In this lesson, we'll create our first custom API endpoints. We'll no longer use the WeatherForecastController.cs file that the CLI created for us. It can be deleted or kept for future reference.

We'll start by creating an AnimalsController.cs with two new methods. One will return all the animals in our application while the other will create a new animal.

using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using CretaceousPark.Models;

namespace CretaceousPark.Controllers
  public class AnimalsController : ControllerBase
    private readonly CretaceousParkContext _db;

    public AnimalsController(CretaceousParkContext db)
      _db = db;

    // GET api/animals
    public async Task<ActionResult<IEnumerable<Animal>>> Get()
      return await _db.Animals.ToListAsync();

    // POST api/animals
    public async Task<ActionResult<Animal>> Post(Animal animal)
      await _db.SaveChangesAsync();

      return CreatedAtAction("Post", new { id = animal.AnimalId }, animal);

Note that our code looks very similar to code we might use when building a web application. The key difference is that we aren't returning a view. There are a few key differences that we'll reiterate here:

  • Our GET route needs to return an ActionResult of type <IEnumerable<Animal>>. In our web applications, we didn't need to specify a type because we were always returning a view.

  • Our POST route utilizes the function CreatedAtAction. This is so that it can end up returning the Animal object to the user, as well as update the status code to 201, for "Created", rather than the default 200 OK.

If we run our application and make an API call with Postman to localhost:5000/api/animals, there won't be any results and we'll receive an empty array.

Let's use our new POST method to change that. Let's send a POST request to http://localhost:5000/api/animals.

We'll need to include a Body in our POST request. If we look at our POST route, it is expecting an object of type Animal. We can do this by passing an object literal in the body of our API call:

    "species": "Tyrannosaurus Rex",
    "name": "Elizabeth",
    "age": 8,
    "gender": "Female"

When we make the API call, we'll get a 201 Created status, which means the call was successful.

If we make a GET request to http://localhost:5000/api/animals, our new Animal will be returned in the response. Note that .NET automatically converts the properties to JSON for us.

Now that we've posted some valid information, let's look at how we might get a specific animal's information from the API. Let's add a new route to our controller.

// GET: api/Animals/5
public async Task<ActionResult<Animal>> GetAnimal(int id)
    var animal = await _db.Animals.FindAsync(id);

    if (animal == null)
        return NotFound();

    return animal;

Note the HttpGet accepts an argument here. We include {id} in the data annotation, and restart the server. Now we can pass in the AnimalId as a URL parameter like this:


If we do the above GET request, it will return an animal that has an AnimalId of 1. Go ahead and test this out in Postman to confirm that it's functioning as expected.

At this point, we can do some tweaking of the POST request for best practices. In its return value, we will change the first argument. This first argument affects the value of the Location in the response header -- we'll change it to the result of our GetAnimal route. Upon creation, the result contains a link to where that newly-created object can be found with a GET request:

return CreatedAtAction(nameof(GetAnimal), new { id = animal.AnimalId }, animal);

Repository Reference

Follow the link below to view how a sample version of the project should look at this point. Note that this is a link to a specific commit in the repository.

Example GitHub Repo for Cretaceous Park

Lesson 5 of 22
Last updated April 6, 2022