While CRUD may be the most common pattern in RESTful APIs, how to name things sure seems to be the most talked about.
Certainly, one can name API elements without following any pattern…. that will likely yield unfavorable results. As I noted in my “The Art of API Design” presentation at Nordic APIs Austin API Summit, you could get creative and start using musical notes or Greek letters to name things… but that will hinder the usability of the API. In other words, applying consistent naming patterns will increase the usability of your API… and that won’t hurt adoption.
Matthew Reinbold posted recently in Net API News about The Necessity of Naming in APIs:
“naming should convey the API producer's mental model to many consumers with minimal fuss” — Matthew Reinbold
“To lower the cognitive overhead required to integrate with your API, you must consider naming design issues. It is the #1 thing that an API producer can do to improve a design, always.” — Matthew Reinbold
Matthew lists serval factors that contribute to the long-held (and justifiable) belief that naming is hard, then recommends a book, Naming Things: The Hardest Problem in Software Engineering by Tom Benner.
So there is a lot of good advice on choosing domain names for your API. I’m going to step back and suggest naming patterns in APIs to improve the developer experience. Using consistent naming patterns (just as using any pattern consistently, when it is the right pattern) will help lessen the cognitive load when others try to adopt your API. But consistent naming will also help you in the design process—basically, a naming pattern serves as a preemptive design decision that removes the need to make lots of similar decisions in the rest of the API design process.
For example, here is a list of the important (OpenAPI) API elements that require a name from the API designer:
Path elements
static path elements
dynamic path elements - path parameters
Operation IDs
Requests and Response Headers
Query Parameters
Schemas
schema names
object property names
string enumerations
Response objects
Parameter objects
Security Schemes
Security scopes
With each name, you have a some important name attributes to consider:
style — do you use snake_case, camelCase, PascalCase, kebob-case
is the name case sensitive or case insensitive? Both OpenAPI and HTTP sort of make this decision for you - most names in the OpenAPI Specification are case sensitive. However, HTTP says that header names are case insensitive. The convention adopted in many revisions of the HTTP standards are to use Title-Kebab-Case for headers, such as
Content-Type
,Content-Length
,Retry-After
and so on.
Rather than defining a separate naming rule or pattern for each category, I have adopted a meta rule for API design: Unless otherwise indicated, use camelCase style for all names, with the exception of header names, which follow the Title-Kebab-Case style described above. (Every rule needs an exception, so that you can claim a small bit of edginess or sense of rebelliousness.) I prefer using camelCase because such names can be used as programming language identifiers in most programming languages without transformation rules, and OpenAPI also uses it throughout (for example operationId
, securitySchemes
, requestBodies
, etc.)
This does not cover all naming situations. Heres a few more naming patterns I’ve adopted:
URL Paths
I use plural names for the domain resource (entity) collections, such as (in our API Design Matters Chain Links sample application’s domain example):
/chains
,/chainLinks
,/authors
,/universes
,/characters
, etc. (Note how the compound-word resource term “chain link” becomeschainLink
via the camelCase style). Others prefer using kebab case for path elements (i.e.chain-links
). This is perfectly valid, although it deviates from the “one pattern to rule them all” that I prefer.This is a matter of preference and style, so choose one and use it consistently throughout your API.For path parameters which indicate the unique resource ID of an instance within a collection, I use a
<resourceName>Id
naming pattern:/chains/{chainId}
,/chainLinks/{chainLinkId}
,/authors/{authorId}
,/universes/{universeId}
,/characters/{characterId}, and so on.
Operation IDs
The operationId
assigned define in each operation object is often used in code generation as the name of a function or method to invoke the operation. Thus, I start all operationId names with direct present-tense verbs, such as listAuthors
, createUniverse
, getCharacter
etc. I use list
for operations that return collections of API resources and get
for operations which return instances within collections or other non-collection resources.
Model Schemas
The model schemas in APIs are the names of things and thus should use noun phrases, such as author,
character,
universe
, chain
, and chainLink
. For convenience, I’ll use a generic camelCase placeholder {resourceName}
for the name of an API resource. The case here is significant. For example, for the “chain link” domain resource for the Chain Links API, {resourceName}
is chainLink and {ResourceName}
is ChainLink.
new{ResourceName}
is the name of the model schema used to create a new instance in the create operation. Example: newChainLink{resourceName}Patch
is the name of the model schema used for the for a PATCH operation request body. Example:chainLinkPatch
{resourceName}Update is the name of the model schema for the
PUT
operation request body.
By applying these model schema naming patterns with the operation ID and path element patterns, larger aggregate patterns become evident.
To create a resource:
POST …/{pluralResourceName}
operationId: create{ResourceName}
request body model schema: {newResourceName}
response body model schema: {resourceName}
For example:
POST …/characters
operationId
: createCharacter
request body model schema: newCharacter
response body model schema: character
Property Names
In general the names of properties should follow your domain model and ubiquitous language. However, there are a number of commonly repeated properties which pop up, regardless of domain. Having “reserved” property names as part of your API pattern language can guide your consistent API design:
id
— the unique ID string for a resource, often assigned by the API service in the POST operation that creates a resource. Thisid
is used in the …/{pluralResourceName}/{resourceName}Id
resource path to access the instance resource within the collection, such as…/characters/{characterId}
or/authors/{authorId}
createdAt
is an RFC 3339date-time
when a resource was created.updatedAt
is the date-time the resource was last updated with aPUT
orPATCH
or other operation. In general, I suggest using{event}At
for date-time properties and{eventAt}On
for date properties.The property
name
is for resources which have names;label
for resources which have slightly longer word-phrase labels that are displayed in the user interface; anddescription
for resources which allow longer sentence-length or paragraph descriptions).
Meta Patterns
Name Everything
My preference is to provide names for all API elements, especially schemas — avoid using anonymous nested schemas (i.e. if a property of an object schema s
is defined by another object schema, do not define its properties in line. Instead, extract them to a named schema. This is important for code generation (software development kits or SDKs) for your API, as different vendors choose different “automatic” naming strategies for any anonymous/unnamed schemas, and this can diminish usability of the SDK and therefore the API. Such auto-generated names can often be overly long and cumbersome because they tend to be derived from the context.
This applies not only to schemas nested inside other object schemas or array item schemas, but to response object schemas, request body schemas, parameters, and subschema used schema composition with allOf
.
Naming the schemas also makes it much easier to reuse a schema later, and helps prevent code duplication and “API drift” — For example, if someone reading the API later sees the same object structure with the same four properties defined two or times, natural question arise:
Are these the same structures?
If not, why not?
Is the similarity accidental?
What is different about them?
Naming such schemas helps reveal your intent, which makes future maintenance and API evolution easier.
Use widely recognized domain names / your ubiquitous language
If you have used the ADDR process or domain driven design and created a ubiquitous language—especially one that is driven by external API consumers and feedback, that is a great starting point for the names in your API. I encourage you to review your API domain analysis and your API Domain’s ubiquitous language and choose names from that common language, provided they are clear and not based on jargon that is not really part of the domain.
Use Standard Names
When there is an existing internet standard, use the names defined by that standard. This begins with the HTTP standard (for example, for header names such as Content-Type
, Accept
, Accept-Language
, Retry-After
), media type names such as application/json
, application/pdf
, text/plain
, etc.
If you are using application/problem+json
(RFC 9457) representation for API problems/errors, use the property names it defines (type, status, title, detail, instance
). ( See Your API Has Problems. Deal With It.)
Few things will irritate a developer more than an API which does not follow existing standards.
Anti-Patterns
One thing that I’ve learned over more than a dozen years working in corporate software development is to avoid using brand names or marketing names in API element names. During my time working at one organization, I saw a single product renamed three separate times.
Avoid redundancy. When naming properties inside a model schema, avoid repeating the model name in the model schema’s property names. For example, use universe.id
, character.name
, and author.name
, not universe.universeId, character.characterName
or author.authorName.
Brand and marketing names (especially in software) change over time—it is a fact of software development. If you incorporate brand/marketing names into an API, then the API becomes semi-obsolete when the name changes.
Another anti-pattern is using jargon, especially if such jargon derives from internal implementation choices such as code names, legacy systems, or terms that have seeped in from other domains. If the term is not in common use by domain practitioners, with a clear meaning, that is a strong hint that it requires more thought.
Summary
I’ve presented a small catalog of reusable patterns for naming elements of APIs. I hope they work for you, or at least inspire you to create your own patterns to increase the consistency and predictability of your APIs.
Thanks for sharing. This makes me feel encouraged to make some conventions more explicit.
In the "Model Schema" I think the operation you wanted to write was PATCH but not PATH?