The Only Three Types in an API

Ray Epps
Exobase
Published in
3 min readAug 9, 2022
View, Model, and Storage Types
View, Model, and Storage Types

As a small child, one who tended to loose toys and jackets, I was thought this phrase:

Everything with a place and everything in its place

The idea: if everything has been assigned a location then everything can be put where it belongs. If an item does not have an assigned location it can’t be put away. A terrific lesson for children and engineers alike.

As engineers, we should think critically about our types, classes, layers, services, and the other devices we use to organize our software. Everything should have a dedicated purpose and home where it belongs. After 10+ years of working on APIs in disarray, this is how I organize my types.

The Only Three Types

There are only three types in an API. They are the view, model, and storage types. Of course you may have others but they should be isolated and abstracted into modules where the interface interacts strictly by model types.

As a request comes in we validate and parse it to produce arguments, often partial model types. When putting model objects into the database we convert them to storage types. When pulling from the database we map the storage type to a model type. When returning models to the client we transform them into view types.

Model Types

This is what you’re probably used to. Everything in your API is probably a model type right now. Model types are the shapes that the API expects to work with. They are the standard shape of data. Let’s assume your API is a public community commerce where users can post listings they’d like to sell.

Model Type

Storage Types

When you store a model you often modify it to enrich the object with properties that are helpful for the specific storage your using. For example, when using Mongo I take any foreign key properties and add a new property where this id is converted to Mongo’s ObjectId type. This makes Mongo’s indexes and queries faster.

Storage Type

In a SQL database, you might want to convert millisecond timestamps from your model into Date objects or flatten properties represented in the model as arrays or objects.

We call these storage types instead of database types because in modern applications we’re often storing different data in a variety of storage devices. Your storage types might cover timeseries data you put into Mongo, relational data you put in Postgres, payload data you put in SQS, event data you put in Dynamo, and cached data you put in Redis. Storage types cover all these modules. Each of the modules should accept and return model types.

View Types

These are the types surfaced by your API. Often times your model includes data you don’t want exposed to the client. Or maybe, you only want it exposed in different endpoints for specific authorization levels. View types clearly express the data that should be returned and if done properly will enforce correctness and disallow over-exposing data to the user.

View Types

There are only two differences between this type and our `Listing` model type.

  1. We’ve added a _view property. This is how we ensure that the `Listing` model can’t be unintentionally used in place of the `ListingView`. Because `ListingView` is really just a subset of properties from the `Listing` model, Typescript would allow the `Listing` model to be used if not for a unique property on the view type: `_view`.
  2. We’ve omitted the `reported` property. For our use case in the basic find listing endpoint we don’t want to expose how many times a listing has been reported.

Storage Mappers

To maintain this abstraction, any storage module needs to accept and return model types. However, internally, those model types should be mapped to storage types when storing and mapped back to model types when reading from storage.

Storage Mappers

This should live inside any database or storage module you have. This allows the business logic to focus on business logic things using the model types.

View Mappers

A small module with view mapping function handles converting the model type to the view type strictly and safely.

View Mappers

Endpoints

It all works together in an endpoint. A request enters, model types are created, storage modules are called with model types, they return model types, any business logic is done, and the endpoint returns a view type.

Find Listing Endpoint

--

--