# api

## API Styleguide

A summary of our API request and response conventions.

## Routes

### Methods

Use methods. Don't put the method into the route name.

Here are some other conventions we have at The Orchard.

#### HEAD

We create `HEAD` methods to return a yes/no response. A yes corresponds to HTTP status code 200.

For example, to check if a subaccount belongs to a vendor, we can issue a call to `ows-account` microservice:

```text
HEAD /vendor/<vendor_id>/subaccount/<subaccount_id>
```

If the vendor is the owner of the subaccount, it will return 200. Else, it will return a 400-level error.

#### POST

In general, use JSON for the `POST` request body.

In general, you should never use `POST` to upload a file directly to a microservice. Instead, microservices should only deal with references to files. Please reference [this issue](https://github.com/theorchard/ows-grass/issues/188) for larger discussion.

A potential set-up might be:

1. Frontend application is configured with IAM credentials that provide it write access to a path on an S3 bucket. Alternatively, it requests IAM credentials to write a specific file to a specific S3 bucket.
2. S3 Bucket has lifecycle policies configured. Any files written to the bucket will expire after n time.
3. User tries to upload a file using the frontend application.
4. Frontend application uploads the file to the path that its IAM credentials allowed for it.
5. Frontend application makes `POST` call to a microservice with reference to the file. The reference might be a signed/expiring link to the S3 object or a short-living STS token to the S3 object.
6. Microservice uses the reference to the file to act.

The benefits include:

* Because file size may be unpredictable, our applications \(Grass proxy, microservices\) are not tied up uploading a file, and then moving the file to a permanent location.
* Consider that our microservices are running on docker containers on EC2 instances. What happens when temporary files take up all the disk space?
* S3 is distributed and not tied to a specific region. This means we can reduce latency for the file upload itself.

### Naming

When naming a route,

* use nouns
* keep it lowercase.
* hyphenate \(`-`\) when the noun consists of multiple words.

When describing a resource, always use plural. This is applicable for HTTP methods.

DO:

```text
GET /vendors/<vendor_id>
POST /products
PUT /pricing-tiers/<pricing_tier_id>
```

DON'T:

```text
GET /vendor/post
/getvendor/<vendor_id>
POST /product
PUT /pricing_tier/<pricing_tier_id>
POST /make/sandwich
HEAD /Vendor/<vendor_id>/Sme
```

### Parameters

Route parameters should be used when specifying an immutable unique identifier.

If you notice that you have many route parameters and there is no obvious hierarchy for ordering the route parameters, seriously reconsider using query parameters.. This will make your API very non-intuitive and hard for clients.

DO:

```text
GET /vendors/<vendor_id>/projects
```

DON'T:

```text
GET /pluralized-noun1/<noun1_id>/this-could-have-gone-first/<some_id>
```

Query parameters should be used for filtering scenarios. When specifying query parameter names that consist of multiple words, use underscore. When filtering on multiple values use a comma separated list, don't use repeated key/value pairs.

DO:

```text
GET /products?product_type=music,video
```

DON'T:

```text
GET /products?productType=music,video
GET /products?product-type=music,video
GET /products?product_type=music&product_type=video
```

#### Route Parameters vs Query Parameters

DO:

```text
GET /users/<user_id>
GET /users?email=<email>                # email should be mutable whereas user_id shouldn't be
GET /users/<user_id>?email=<email>      # verify that email matches for the provided user_id
```

DON'T:

```text
GET /users?user_id=<user_id>
GET /users/<email>
```

You can also think of it like this:

* `/<entity>/<identifier>`: Always returns either the item or 404 \(if the identifier does not exist\).
* `/<entity>?<query>`: Always returns a paginated list of items, even if it only contains one item.

`<identifier>` should be a unique reference to the thing the endpoint returns.

DO:

```text
GET /playlist/<id>  # returns one playlist
GET /track/<id>     # returns one track
GET /isrc/<isrc>    # returns info about one isrc
```

DON'T:

```text
GET /playlist/<isrc> # returns all playlists which include <isrc>
```

## JSON Responses

### JSON Objects

In general, we follow these guidelines for naming object keys:

* use underscores in the object key names, instead of camel-casing.
* stick to lowercase

DO:

```javascript
"address": {
    "street": "23 E 4th Street",
    "city": "New York",
    "state_or_province": "NY",
    "country": "USA"
}
```

And DON'T:

```javascript
"address": {
    "street": "23 E 4th Street",
    "city": "New York",
    "stateOrProvince": "NY",
    "country": "USA"
}

"Address": {
    "Street": "23 E 4th Street",
    "City": "New York",
    "stateOrProvince": "NY",
    "Country": "USA"
}
```

### Single Item

For API endpoints which are intended to provide a single item response, DO:

```javascript
{
    "subaccount_id": 1,
    "vendor_id": 12,
    "subaccount_name": "Fred’s Records",
    "description": "Fred is a schnauzer who loves pop music.",
    "address": {
        "street": "23 E 4th Street",
        "city": "New York",
        "state_or_province": "NY",
        "country": "USA"
    }
}
```

### Lists

When returning a response containing a list of items, DO:

```javascript
{
    "items": [
        {
            "subaccount_id": 1,
            "vendor_id": 12,
            "subaccount_name": "Fred’s Records",
            "description": "Fred is a schnauzer who loves pop music."
        }
    ],
    "pagination": {
        "type": "standard",
        "offset": 0,
        "limit": 50,
        "total_records": 1
    }
}
```

Always use the key `items` to indicate the list. Thus, DON'T:

```javascript
{
    "subaccounts": [
        {
            "subaccount_id": 1,
            "vendor_id": 12,
            "subaccount_name": "Fred’s Records",
            "description": "Fred is a schnauzer who loves pop music."
        }
    ],
    "pagination": {
        "type": "standard",
        "offset": 0,
        "limit": 50,
        "total_records": 1
    }
}
```

If the API endpoint is intended to provide list behavior \(e.g. `/subaccounts`\), but there is only one item in the list, do not mutate the response. Thus, DON'T:

```javascript
{
    "subaccount_id": 1,
    "vendor_id": 12,
    "subaccount_name": "Fred’s Records",
    "description": "Fred is a schnauzer who loves pop music."
}
```

### Pagination

In general, if a list of items is being returned, provide pagination information. This should end up looking like:

```javascript
"pagination": {
    "type": "standard",
    "offset": 0,
    "limit": 50,
    "total_records": 1
}
```

or

```javascript
"pagination": {
    "type": "token",
    "token": "nextToken"
}
```

