Constructing a New Public API

This yr, I launched into an thrilling journey to construct a brand new public API system for certainly one of my shoppers. The purpose was to create a system permitting them to promote entry to their priceless dataset to exterior events. This mission wasn’t nearly opening up new income streams; it was about innovation and increasing the worth we may provide to the shoppers sector.

APIs play an important function in fashionable software program techniques, facilitating the circulate of important information throughout varied parts and platforms. By enabling third events to construct merchandise on high of our infrastructure, we had been set to considerably improve the platform’s worth and attain.

However our imaginative and prescient prolonged past exterior use. We aimed to create an API that will additionally function an inside normal for information entry throughout the platform. This dual-purpose method promised to streamline operations and set a brand new benchmark for effectivity.

Constructing the Infrastructure

Beginning such a mission may be daunting, however I’ve realized that the act of starting is commonly probably the most essential milestone. Upon getting one thing tangible, it turns into a lot simpler to iterate and enhance. Nevertheless, earlier than diving into coding, I knew that designing the proper infrastructure from the beginning could be half the battle. The previous adage “measure twice, reduce as soon as” got here to thoughts.

My focus was on three key areas:

  1. Applied sciences: Selecting the best tech stack
  2. Construction: Guaranteeing maintainability by means of correct mission group
  3. Safety: Implementing sturdy measures to guard the primary platform

Applied sciences

Provided that the primary platform runs on .NET 5/6+, I made a decision to leverage this know-how as the inspiration for the brand new API system. This resolution instantly descoped a big quantity of effort – a crucial consideration for a solo developer.

I additionally wished to include Swagger with its OpenAPI specification, instruments I might had optimistic experiences with prior to now. These two key applied sciences fashioned the bottom upon which I constructed the remainder of the stack.

The ultimate tech stack included:

  • .NET
  • Swagger + OpenAPI specification
  • Microsoft SQL Server
  • Docker
  • Microsoft entity framework

Final tech stack

Venture Construction

For the mission construction, I opted to create a brand new space throughout the present answer quite than a separate .sln file. This method maintains a cohesive view of your entire codebase, which is especially useful for smaller groups.

I began by defining the routes for the preliminary model of the API:

/api/v2/campuses
/api/v2/campuses/[id]
/api/v2/programs
/api/v2/programs/[id]
/api/v2/intakes
/api/v2/intakes/[id]
/api/v2/suppliers
/api/v2/suppliers/[id]

Primarily based on these routes, I created the next listing construction:

PublicApi/
├── Controllers/
│   ├── CampusesController.cs
│   ├── CoursesController.cs
│   ├── IntakesController.cs
│   ├── ProviderController.cs
│   └── ScholarshipController.cs
├── Fashions/
│   ├── Campus.cs
│   ├── Course.cs
│   ├── Consumption.cs
│   ├── Supplier.cs
│   └── Scholarship.cs
└── Providers/
    (initially empty, to be populated as wanted)

I started by defining the fashions that formed the API’s response payloads. With these in place, I may then arrange primary GET endpoints for each record and particular person useful resource retrieval, initially returning dummy information.

Here is a primary instance of one of many domain-driven endpoints:

utilizing System.Collections.Generic;

namespace PublicApi.Fashions

    public class Course
    
        public string Title  get; set; 
        public string Id  get; set; 
        public string ProviderId  get; set; 
        public string LevelOfStudy  get; set; 
        public Checklist AreasOfStudy  get; set; 
        public Checklist SubjectsOfStudy  get; set; 
        public string Particulars  get; set; 
        public string EntryRequirements  get; set; 
        public string Length  get; set; 
        public bool Lively  get; set; 
        public lengthy LastUpdated  get; set; 
        public bool InternationalFlag  get; set; 
    

> GetCourses()

// Dummy data for illustration
var courses = new List

new Course

Name=”Computer Science”,
Id = ‘CS101’,
ProviderId = ‘UNIV001’,
LevelOfStudy = ‘Undergraduate’,
AreasOfStudy = new List ‘Technology’, ‘Mathematics’ ,
SubjectsOfStudy = new List ‘Programming’, ‘Algorithms’, ‘Data Structures’ ,
Details=”A comprehensive course covering the fundamentals of computer science.”,
EntryRequirements=”High school diploma with strong mathematics background”,
Duration = ‘4 years’,
Active = true,
LastUpdated = 1630444800,
InternationalFlag = true
,
new Course

Name=”Business Administration”,
Id = ‘BA201’,
ProviderId = ‘UNIV001’,
LevelOfStudy = ‘Graduate’,
AreasOfStudy = new List ‘Business’, ‘Management’ ,
SubjectsOfStudy = new List ‘Finance’, ‘Marketing’, ‘Operations’ ,
Details=”An MBA program designed for aspiring business leaders.”,
EntryRequirements=”Bachelor”s degree and 2 years of work experience’,
Duration = ‘2 years’,
Active = true,
LastUpdated = 1641024000,
InternationalFlag = true

;

return Ok(courses);

[HttpGet(‘id’)]
public ActionResult GetCourse(string id)

// Dummy information for illustration
var course = new Course

Title=”Information Science”,
Id = id,
ProviderId = ‘UNIV001’,
LevelOfStudy = ‘Graduate’,
AreasOfStudy = new Checklist ‘Expertise’, ‘Statistics’ ,
SubjectsOfStudy = new Checklist ‘Machine Studying’, ‘Large Information’, ‘Statistical Evaluation’ ,
Particulars=”A complicated course in information science and analytics.”,
EntryRequirements=”Bachelor”s diploma in a quantitative subject’,
Length = ‘2 years’,
Lively = true,
LastUpdated = 1651363200,
InternationalFlag = true
;

return Okay(course);

}
}” data-lang=”textual content/x-csharp”>

utilizing Microsoft.AspNetCore.Mvc;
utilizing System.Collections.Generic;
utilizing PublicApi.Fashions;

namespace PublicApi.Controllers
{
    [Area("PublicApi")]
    [ApiController]
    [Route("api/v2/[controller]")]
    [ApiExplorerSettings(GroupName = "PublicApi")]
    public class CoursesController : ControllerBase
    {
        [HttpGet]
        public ActionResult> GetCourses()
        
            // Dummy information for illustration
            var programs = new Checklist
            
                new Course 
                 
                    Title = "Laptop Science",
                    Id = "CS101",
                    ProviderId = "UNIV001",
                    LevelOfStudy = "Undergraduate",
                    AreasOfStudy = new Checklist  "Expertise", "Arithmetic" ,
                    SubjectsOfStudy = new Checklist  "Programming", "Algorithms", "Information Constructions" ,
                    Particulars = "A complete course masking the basics of laptop science.",
                    EntryRequirements = "Highschool diploma with sturdy arithmetic background",
                    Length = "4 years",
                    Lively = true,
                    LastUpdated = 1630444800,
                    InternationalFlag = true
                ,
                new Course 
                 
                    Title = "Enterprise Administration",
                    Id = "BA201",
                    ProviderId = "UNIV001",
                    LevelOfStudy = "Graduate",
                    AreasOfStudy = new Checklist  "Enterprise", "Administration" ,
                    SubjectsOfStudy = new Checklist  "Finance", "Advertising", "Operations" ,
                    Particulars = "An MBA program designed for aspiring enterprise leaders.",
                    EntryRequirements = "Bachelor's diploma and a couple of years of labor expertise",
                    Length = "2 years",
                    Lively = true,
                    LastUpdated = 1641024000,
                    InternationalFlag = true
                
            ;

            return Okay(programs);
        

        [HttpGet("id")]
        public ActionResult GetCourse(string id)
        
            // Dummy information for illustration
            var course = new Course
            
                Title = "Information Science",
                Id = id,
                ProviderId = "UNIV001",
                LevelOfStudy = "Graduate",
                AreasOfStudy = new Checklist  "Expertise", "Statistics" ,
                SubjectsOfStudy = new Checklist  "Machine Studying", "Large Information", "Statistical Evaluation" ,
                Particulars = "A complicated course in information science and analytics.",
                EntryRequirements = "Bachelor's diploma in a quantitative subject",
                Length = "2 years",
                Lively = true,
                LastUpdated = 1651363200,
                InternationalFlag = true
            ;

            return Okay(course);
        
    }
}

This setup allowed me to carry out my first take a look at utilizing a Bruno consumer, and voila! I acquired my first response. Now we’re getting someplace

Safety

Earlier than delivery something, implementing safety measures was essential. I targeted on two important considerations:

  1. Unauthorized entry: I carried out a rough-cut API key authentication technique and added our new API pages to the robots.txt file to forestall search engine indexing. This may make it troublesome to stumble throughout our API unintentionally, and in the event that they did, they would not be capable of entry our system and not using a legitimate API key.
  2. Safety in opposition to DoS: To mitigate the chance of database learn operation overload (intentional or unintentional), I carried out API key price limiting together with IP price limits. I set a wise restrict of 120 requests per minute (2 requests per second) to take care of an affordable SLA whereas defending the system from unintentional request floods.

Making It a Product

Whereas I had a primary API in manufacturing, reworking it right into a priceless product required a number of further steps:

Including Enterprise Logic

We wanted to get the routes wired up so they may begin returning one thing priceless to the consumer. I used Microsoft’s Entity Framework ORM to tug data from the SQL database and map them to the API response payloads. This course of concerned creating information entry layers and implementing the mandatory enterprise logic in every controller.

Creating Providers for Reusable Logic

To advertise code reuse and preserve a clear separation of considerations, I abstracted widespread enterprise logic into companies. By the tip of the mission, I had created a number of utility companies:

Providers/
├── AuthorityFormatter.cs
├── DateFormatCalculator.cs
├── DeliveryCalculator.cs
├── DurationCalculator.cs
├── HtmlHelper.cs
├── MacronRemover.cs
├── ProviderTypeFormatter.cs
├── RegionMapper.cs
├── StreetAddressFormatter.cs
├── StringFormatter.cs
└── SubjectTaxonomyMapper.cs

Implementing Pagination

Pagination was essential for permitting third events to navigate by means of data on the API stage effectively. I created a pagination mannequin and repair:

public class ApiPaginatedResponse

    public Checklist Objects  get; set; 
    public Pagination Pagination  get; set; 


public class Pagination

    public int Skip  get; set; 
    public int Restrict  get; set; 
    public int Depend  get; set; 
    public string NextPage  get; set; 
    public int TotalCount  get; set; 

Implementing Appropriate HTTP Response Codes

An typically missed however essential facet of API design is the right use of HTTP response codes. These codes present instant suggestions to API shoppers (and builders) in regards to the standing of their requests, making the API extra intuitive and simpler to work with.

I made certain to implement a variety of applicable standing codes in our API responses:

  • 200 OK: For profitable GET, PUT, or PATCH requests
  • 400 Dangerous Request: When the request is malformed or accommodates invalid parameters
  • 401 Unauthorized: When authentication is required however not supplied or is invalid
  • 403 Forbidden: When the authenticated consumer would not have permission to entry the requested useful resource
  • 404 Not Discovered: When the requested useful resource would not exist
  • 429 Too Many Requests: When the consumer has despatched too many requests in a given period of time (price limiting)
  • 500 Inside Server Error: For surprising server errors

Customizing the UI

To boost the consumer expertise and produce our model to the API, I custom-made the Swagger UI by changing the generic branding with our brand and making use of clear, constant styling.

I additionally added instance response payloads for varied endpoints: 

Swagger/
└───Examples
    ├───CampusExamples
    │       CampusListResponseExample.cs
    │       CampusResponseExample.cs
    │
    ├───CourseExamples
    │       CourseListResponseExample.cs
    │       CourseResponseExample.cs
    │
    ├───IntakeExamples
    │       IntakeListResponseExample.cs
    │       IntakeResponseExample.cs
    │
    ├───ProviderExamples
    │       ProviderListResponseExample.cs
    │       ProviderResponseExample.cs
    │
    ├───ScholarshipExamples
    │       ScholarshipListResponseExample.cs
    │       ScholarshipResponseExample.cs
    │
    └───StatusCodes
            400ResponseExample.cs
            401ResponseExample.cs
            404ResponseExample.cs
            429ResponseExample.cs

Getting list of campuses

Returning the list of campuses

Provisioning API Keys

Earlier than sharing the brand new Swagger documentation, I generated and distributed API keys to the third events who could be utilizing the brand new API. This course of concerned making a safe system for producing, storing, and managing these keys.

You need not overthink this. We assigned API keys to a singleton and added code feedback to establish the related customers. This easy system allowed for straightforward administration and potential future revocation of entry if wanted.

Classes Discovered

Constructing this mission was an thrilling problem that taught me a number of priceless classes:

  1. Begin small and iterate: Starting with a minimal viable product and bettering it repeatedly proved to be an efficient technique.
  2. Outline first, then construct: Having a transparent understanding of the specified responses tremendously simplified the method of writing enterprise logic.
  3. Do not stress the small stuff: Getting the service into customers’ fingers shortly for suggestions is essential, even when it is not 100% polished.
  4. Leverage present sources: Selecting to make use of languages and frameworks already in play resulted in a extra cohesive answer and simpler future upkeep.

Future Horizons

The event of this API has not solely expanded our choices however has additionally opened up new prospects for innovation within the training sector. As extra organizations acknowledge the ability of APIs in driving development and fostering ecosystems, tasks like it will turn into more and more very important.

Whether or not you are contemplating constructing an API to your personal mission or seeking to leverage present APIs in your group, keep in mind that the journey of a thousand miles begins with a single step. Begin small, deal with delivering worth, and do not be afraid to iterate and enhance as you go.