Design principles behind the Lucca API.
Make sure your OpenAPI specs is v3.x.
Install stoplight/spectral
Install Lucca API guidelines through npm
Lint your oas file with the spectral ruleset of this project
Prefer
HTTP header.
type
of the collection representation.
GET requests are used to read either a single or a collection of resource(s) representation(s).
GET
requests MUST be safe (i.e. they must not change the server state) and thus idempotent.GET
request SHOULD be cacheable.GET
requests for individual resources SHOULD return a 404 if the resource does not exist or it is not accessible to the authenticated user.GET
requests for collection resources MAY return either 200 (if the collection is empty) or 404 (if the collection is missing or inaccessible).GET
requests SHOULD NOT return a 202, and therefore should not be asynchronous.GET
requests MUST NOT have a request body payload. Use query parameters, and in the worst case, a POST
request. Do not use headers for this.GET
requests on collection resources SHOULD provide sufficient filter and pagination mechanisms.PUT requests are used to update - or create if it does not already exist - entire resources.
PUT
requests MUST have an idempotent behavior.PUT
requests SHOULD NOT usually be supported on collection resources. Otherwise, please refer to the “batch requests” part of this document.PUT
requests SHOULD return a 201 (without a Location
header as the resource was created on the URL targeted by the PUT
request) whenever it translates
to the creation of a new resource ; and a 200 whenever it updates an existing one. A 202 MAY be returned to indicate asynchronous processing.PUT
requests SHOULD return the representation of the updated/created resource whenever it contains server-generated properties.PUT
requests SHOULD be used for resource creation whenever the client has the control over the URI (and thus, usually, the identifier).PUT
requests SHOULD support If-Match
and If-None-Match
headers in order to given clients a better control over concurrency and avoid lost updates.POST requests are usually used to create single resources on a collection resource endpoint, but may also be used to execute commands on single resources endpoint.
POST
requests do not require to be idempotent, but MAY be implemented in an idempotent way.POST
requests MAY support batch creations through sending a collection (i.e. an object that list the representations of the items to create). Please refer to the
“batch requests” part of this document to learn more.POST
requests, when intended for creation and successful, MUST return a 201 as well as the URI of the created resource(s) in the Location
HTTP header.POST
requests MAY return a 202 when asynchronous, but SHOULD in this case return a Location
header for the client to be able to track the execution.PATCH method extends HTTP to update parts of the resource representation
PATCH
requests give clients the ability to update a partial representation of the resource. The syntax and semantics of the patch document is not defined
in RFC-5789 and must be described in the API specification by using one (or several) specific media types:application/json
. Send a subset of the object representation. Replaces the values of any listed property. Completely replaces all items of an array property.
Beware of the lack of control (and standard) such an implementation presents.application/merge-patch+json
. JSON Merge Patch standard, which is a standardized way of achieving
what’s described above.application/json-patch+json
. JSON Patch standard, which gives proper support for granular array manipulations.
It supports updating an array item identified via its index, but not via some of its properties.PATCH
method can prove quite tricky, we recommend handling updates through the PUT
method, which forces clients to send the complete
representation of resource, but gives better control (idempotency and no side-effects).PATCH
requests SHOULD support If-Match
and If-None-Match
headers in order to given clients a better control over concurrency and avoid lost updates.DELETE requests are used to delete resources.
DELETE
requests MUST be idempotent.DELETE
requests MAY be supported on collection resources, in order to support batch deletes. In this case, DELETE
requests on collection endpoints SHOULD
support query parameters in order to filter on the items of the collection to delete.DELETE
request MUST return a 204 when successful. Any subsequent GET
request MUST return a 404.DELETE
request MUST NOT have any request body. When needed, please implement the corresponding feature through a POST request.DELETE
requests SHOULD support If-Match
and If-None-Match
headers in order to given clients a better control over concurrency and avoid lost updates.HEAD requests are used to retrieve the header information of single resources and resource collections.
GET
, but only returns the headers (i.e. no response body).As a result, like a GET
request, any HEAD
request MUST be safe, idempotent, and SHOULD be cacheable.HEAD
requests SHOULD be supported in order to give clients the ability to efficiently lookup whether large resources or collection resources have been
updated in conjunction with the If-Match
and If-None-Match
headers.Glossary: safe, idempotent, cacheable
Idempotency-Key
HTTP header (c.f. RFC).Code | Methods | MUST document? | RFC | Description |
---|---|---|---|---|
200 OK | all | ✅ | rfc9110 | General success response. Prefer a more specific success code when possible. |
201 Created | POST, PUT | ✅ | rfc9110 | Returned on successful resource creation, even if response body is empty. Used along the Location header when the created resource URI is indeterminate for clients. |
202 Accepted | POST, PUT, PATCH, DELETE | ✅ | rfc9110 | The request was successful and will be processed asynchronously. Only applicable to methods which change the state on the server side. |
204 No Content | PUT, PATCH, DELETE | ✅ | rfc9110 | Returned in place of a 200 if no response body is returned. Only applicable to methods which change the state of the resource. |
304 Not Modified | GET, HEAD | ✅ | rfc9110 | Returned whenever a conditional GET or HEAD request would have resulted in 200 response if the condition evaluated to false Usually: the resource has not been modified since the version passed via request headers If-Modified-Since or If-None-Match. For PUT, PATCH or DELETE requests, use 412 instead. |
400 Bad Request | all | ✅ | rfc9110 | Unspecific client error indicating that the server cannot process the request due to something that is perceived to be a client error (e.g. malformed request syntax, invalid request). Should also be delivered in case of input body fails business logic / semantic validation (instead of using 422). |
401 Unauthorized | all | ❌ | rfc9110 | Actually “Unauthenticated”. The credentials are missing or not valid for the target resource. For an API, this usually means that the provided token or cookie is not valid. As this can happen for almost every endpoint, APIs should normally not document this. |
403 Forbidden | all | ❌ | rfc9110 | The user is not authorized to change this resource. For an API, this can mean that the request’s token was valid, but was missing a scope for this endpoint. Or that some object-specific authorization failed. We recommend only documenting the second case. |
404 Not Found | all | ❌ | rfc9110 | The target resource was not found, either because it does not exist, or because the user cannot access it. This will be returned by most (not documented) paths on most APIs. For a PUT endpoint that does not support creation (only updates), then this might be returned if the resource does not exist. Apart from these special cases, this does not need to be documented. You should not return a 404 on a DELETE request on a resource that does not exist (or was already deleted), but a 204. You may return a 404 on write requests whenever the resource references another resource that is not accessible or does not exist. |
405 Method Not Allowed | all | ✅ | rfc9110 | The request method is not supported for this resource. Using this response code on a documented endpoint only makes sense if it depends on some internal resource state whether a specific method is allowed. Do not use it unless you have such a special use case, but then make sure to document it, making it clear why a resource might not support a method. |
406 Not Acceptable | all | ❌ | rfc9110 | Resource only supports generating content with content-types that are not listed in the Accept header sent in the request. |
409 Conflict | POST, PUT, PATCH, DELETE | ✅ | rfc9110 | The request cannot be completed due to conflict with the current state of the target resource. For example, you may get a 409response when updating a resource that is older than the existing one on the server, resulting in a version control conflict. If this is used, it MUST be documented. For successful robust creation of resources (PUT or POST) you should always return 200 or 204 and not 409, even if the resource exists already. If any If-* conditional headers cause a conflict, you should use 412 and not 409. Only applicable to methods which change something. |
410 Gone | all | ❌ | rfc9110 | The resource does not exist any longer (but did exist in the past), and will most likely not exist in the future. This can be used e.g. when accessing a resource that has intentionally been deleted. This normally does not need to be documented, unless there is a specific need to distinguish this case from the normal 404 |
411 Length Required | POST, PUT, PATCH | ✅ | rfc9110 | The server requires a Content-Length header for this request. This is normally only relevant for large media uploads. The corresponding header parameter should be marked as required. If used, it MUST to be documented (and explained). Only applicable for methods with a request body. |
412 Precondition Failed | PUT, PATCH, DELETE | ❌ | rfc9110 | Returned for conditional requests (If-Match and If-None-Match) if the condition failed. Used for optimistic locking. Normally only applicable to methods that change something. For HEAD or GET requests, use 304 instead. |
415 Unsupported Media Type | POST, PUT, PATCH | ❌ | rfc9110 | The client did not provide a supported Content-Type for the request body. Only applicable to methods with a request body. |
423 Locked | PUT, PATCH, DELETE | ✅ | rfc4918 | Used for pessimistic locking. |
428 Precondition Required | all | ❌ | rfc6585 | Server requires the request to be conditional (If-Match and If-None-Match headers). Instead of documenting this response status, the required headers should be documented and marked as required. |
429 Too Many Requests | all | ❌ | rfc6585 | The client is not abiding by the rate limits in place and has sent too many requests. |
500 Internal Server Error | all | ❌ | rfc9110 | A generic error indication for an unexpected server execution problem. |
501 Not Implemented | all | ❌ | rfc9110 | Server cannot fulfill the request (usually implies future availability, e.g. new feature). |
503 Service Unavailable | all | ❌ | rfc9110 | Service is temporarily down. Ideally, also return a Retry-After header giving clients the time to wait before retrying. |
application/problem+json
.
x-provider
OpenAPI extension is used to indicate which application offers concrete implementation for the given endpoint.
It makes it possible to bundle an application specification, with only their own subset of operations.
application/json
, application/problem+json
, application/hal+json
, etc…
You should avoid using custom media types like application/com.luccasoftware.leave+json
. Custom media
types beginning with x bring no advantage compared to the standard media type for JSON, and make automated
processing more difficult.
type
property (if the root
resource is polymorphic, not a sub-object).
Property name | Type | Semantics | |
---|---|---|---|
id | string | null | Unique identifier of the resource in its collection. |
type | string<enum> | Name of the type of the resource. Should be equal to the name of its JSON schema in the spec. Used as the discriminator of a polymorphic resource. | |
url | string<uri> | Absolute URL to the resource. | |
totalCount | integer<int32> | null | Counts all items of a collection (i.e. across all pages) that match the query parameters. |
items | array (not nullable, but may be empty) | Lists the representations of all ressources in the collection page. | |
embedded | object | Lists representations of related resources. | |
links | object | Lists relationships with other resources which are not structural (i.e. not already present as reference properties of the target resource). |
Type | Prefix | Suffix |
---|---|---|
string<date> | N/A | On |
string<date-time> | N/A | At |
boolean | “is”, ”has“, “can”… | N/A |
canDo
instead of cantDo
) in order to avoid double negations ; and prefix them with is
, can
, or has
whenever it adds value.
enum
or x-extensible-enum
) need to consistently use the upper-snake case format, e.g. VALUE or YET_ANOTHER_VALUE. This is in order to better discriminate them from other properties.
DateTimeOffset
, then clients MUST send the offset. Otherwise, return a Problem.Date(Only)
, then clients MUST not send a time component (nor an offset, even though it’s valid). Otherwise, return a Problem.DateTime
and a DateTimeOffset
. Otherwise, return a Problem.Type | Standard | Description |
---|---|---|
Country | https://en.wikipedia.org/wiki/ISO_3166-1_alpha-3 | Three letters country code. |
Language | https://www.rfc-editor.org/rfc/rfc4646.html | Language tag. |
Currency | https://en.wikipedia.org/wiki/ISO_4217 | Three letters currency code. |
PUT
request.
[]
.
true
and false
. If the content has a meaningful null value, then we recommend replacing the boolean with enumeration of named values or statuses (e.g. “yes”, “no”, “undefined”).
nullable: true
; oas3: type: ['null', …]
) or non required (through the required property of an object).
A non-required property may be absent from the body. A nullable property may be null.
In other words, we could make a distinction between the two cases. But we decided against.
You MUST consider that an absent property is equivalent to declaring it null
.
null
, we recommend avoiding elastic schema definitions. Always serializing all properties makes de-serializing easier in many languages and libraries.
Money
object.
$ref
to this schema in your OpenAPI spec.
WorkEventDuration
object.
This gives you the ability to distinguish between two units: days and hours. Events in “days” unit have a duration defined as a fraction of a day of work (0.5 days might be 3.5 hours for an employee working 7h/day, and 4 hours for an employee working 8h/day).
Having a common definition grants us the ability to aggregate all work-related events of an employee and make calculations.
$ref
to this schema in your OpenAPI spec.
You SHOULD only support write requests on the iso
property.
Name | Type | Semantics |
---|---|---|
page | string | Cursor to the page. |
limit | integer<int32> | Number of items per page. |
include | Array<string<‘embedded’ | ‘links’ | ‘totalItems’>> | Control over the inclusion of embedded resource, the total count of a collection items and/or links to related resources. |
sort | Array<string<enum>> | Control over the sorting of a collection’s items. |
id
property by default.
{propName}.between
named query parameterParameter name | Parameter value type | Example | Description |
---|---|---|---|
page | <string | int<int32>> | ?page=2 | The identifier of the page to retrieve. |
limit | int<int32> | ?limit=100 | The page size. Subject to default and max values. |
embedded
response property
(at the root object level).
The embedded
property MUST be a JSON object whose keys are the name of the resources types
, and its values
are a JSON object whose keys are the embedded resources ids
and values the related representation.
embedded
property MAY be partial, i.e.
only serialize a subset of the related resource properties.
Api-Version
HTTP header (both requests and responses)Api-Version
HTTP header required, or make it possible for clients to “lock” a specific version (e.g. API key based).
Deprecation
and Sunset
HTTP headers to convey an API version lifecycleDeprecation
HTTP header, refer to the draft-ietf-httpapi-deprecation-header-03.
For the Sunset
HTTP header, refer to RFC 8594.
Sunset
HTTP response header.
Cache-Control
header to Cache-Control: no-store
(rather than no-cache
).
As a reminder:
no-cache
: responses may be cached but should be validated before use.no-store
: responses may not ever be cached.embedded
and links
).job-position.created
) and stay clear of business-events
(e.g. employee.promoted
) as well as free-form events (e.g. employee-10-years-anniversay
).
calendar-event
.
updatedAttributes
on *.updated
topics*.updated
events.
When implementing this feature, you SHOULD serialize a JSON object whose
keys are the updated attributes and whose values are their previous value.
Lucca-Signature
HTTP header.
This offers the following guarantees: