The second endpoint – REST APIs

The code of the second endpoint that allows a client to create an entity looks like this:

app.MapPost(
    “/”,
    (CreateDto input) => new CreatedDto(
        Random.Shared.Next(int.MaxValue),
        input.Name
    )
);
public record class CreateDto(string Name);
public record class CreatedDto(int Id, string Name);

Here’s the API contract we can extract from the preceding code:

Contract segmentValue
HTTP MethodPOST
URI/
InputAn instance of the CreateDto class.
OutputAn instance of the CreatedDto class.

Sending the following HTTP request (you can use the CreateEntity.http file) results in the output that follows:

POST / HTTP/1.1
Content-Type: application/json
Host: localhost:7000
Accept: application/json
Connection: keep-alive
Content-Length: 28
 
{
    “name”: “Jane Doe”
}

The trimmed-down response is:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Sat, 03 Jun 2023 17:59:25 GMT
Server: Kestrel
Alt-Svc: h3=”:7000″; ma=86400
Transfer-Encoding: chunked
 
{“id”:1624444431,”name”:”Jane Doe”}

As we can see from the preceding request, the client sent a serialized instance of the CreateDto class, set the name to Jane Doe, and received that same entity back but with a numeric id property (an instance of the CreatedDto class).The OpenAPI specs of our endpoint look like the following:

“/”: {
  “post”: {
    “requestBody”: {
      “content”: {
        “application/json”: {
          “schema”: {
            “$ref”: “#/components/schemas/CreateDto”
          }
        }
      },
      “required”: true
    },
    “responses”: {
      “200”: {
        “description”: “Success”,
        “content”: {
          “application/json”: {
            “schema”: {
              “$ref”: “#/components/schemas/CreatedDto”
            }
          }
        }
      }
    }
  }
}

The input and output schemas are:

“CreateDto”: {
  “type”: “object”,
  “properties”: {
    “name”: {
      “type”: “string”,
      “nullable”: true
    }
  },
  “additionalProperties”: false
},
“CreatedDto”: {
  “type”: “object”,
  “properties”: {
    “id”: {
      “type”: “integer”,
      “format”: “int32”
    },
    “name”: {
      “type”: “string”,
      “nullable”: true
    }
  },
  “additionalProperties”: false
},

Similar to the first endpoint, SwaggerGen translates our C# classes into OpenAPI specs. Let’s wrap this up.

Wrapping up

Some ASP.NET Core templates come with SwaggerGen preconfigured. It also comes with the Swagger UI that lets you visually explore the API contract from your application and even query it. NSwag is another tool that offers similar features. Plenty of online documentation shows how to take advantage of those tools.Besides exploring tooling, we defined that an API contract is fundamental and promotes robustness and reliability. Each endpoint has the following pieces as part of the overall API contract:

A single URI can lead to multiple endpoints by combining different HTTP methods and inputs. For example, GET /api/entities may return a list of entities, while POST /api/entities may create a new entity. Using the entity’s name in its plural form is a convention used by many.

We explore data transfer objects next to add more clarity to that pattern.

You may also like