Designing a New API
Django REST framework(DRF) is a powerful and flexible toolkit for building Web APIs. Sentry APIs are built using DRF. Here are some considerations to make when designing APIs at Sentry.
The API's URL is what developers use to call the endpoint so itโs important that it is meaningful and clear.
Nested resources format like api/0/organizations/\{org}/projects/
is recommended over api/0/projects/
for readability because it gives user an understanding of resource hierarchy. However, nesting can make URLs too long and hard to use. Sentry uses 3-level nesting as a hybrid solution.
Here are some possible urls for values with this resource hierarchy: organization -> project -> tag -> value
- ๐
/projects/\{organization_slug}/\{project_slug}/tags/\{tag_id}/values
- ๐
/organizations/\{organization_slug}/projects/\{project_slug}/tags/\{tag_id}/values/
- ๐
/values/
In the above example we flattened projects
. The table below shows the existing flattened collections which works out with our existing APIs.
First collection in URL | When to use | Parent | Identifier | Example |
---|---|---|---|---|
organizations | When the resource cannot be attached to any other collection below parent like Project | N/A - always comes as first collection | {organization_slug} | Create a New Team |
teams | When the resource is under a specific team in the hierarchy | organizations | {organization_slug}/ {team_slug} | Retreive Team Stats |
projects | When the resource is under a specific project in the hierarchy but not under an issue | organizations | {organization_slug}/ {project_slug} | Create a New Client Key |
issues | When the resource is under a specific issue in the hierarchy | projects | {issue_id} | List an Issue's Events |
sentry-app-installations | When the resource is mapped to a specific integration | organizations | {integration_slug} | Delete an External Issue |
Here are some additional examples:
- ๐
/organizations/\{organization_slug}/projects/
- ๐
/projects/\{organization_slug}/\{project_slug}/issues/
- ๐
/projects/
Note
Hierarchy here does not necessarily mean that one collection belongs to a parent collection. For example:
projects/\{project_identifier}/teams/
refers to the teams that have been added to specific projectteams/\{team_identifier}/projects/
refers to the projects a specific team has been added to
- Collection names should be lowercase and hyphenated, e.g.
commit-files
. - Collection names must be plural. Avoid using uncountable words because the user canโt know whether the GET returns one item or a list.
- Query params and body params should be
camelBacked
. eg.userId
ordateCreated
. - For sorting and filtering, stick with the common param names:
sortBy
(e.g.sortBy=-dateCreated
),orderBy
(eitherasc
ordesc
),groupBy
,limit
- Path params should be
snake_case
Each API should be stateless, have a clear purpose, and do one specific thing. To achieve that, stick with the standard methods listed below. If your API needs to be more complicated, work with owners-api on how to create it.
- ๐ An API that updates project settings: PATCH for updating a field or PUT for updating settings
- ๐ An API that creates a project, creates a team, and creates alerts for that team about that project
Functionality | HTTP Method | Response Object | Example |
---|---|---|---|
Create | POST | Serialized created resource | Create a Project |
Update | PUT or PATCH | Serialized updated resource | Update Project Settings |
Get | GET | Serialized single resource | Retrieve a Project |
Delete | DELETE | None | Delete a Project |
List | GET | List of multiple serialized resources | List All the Projects in an Organization |
Batch Get | GET | List of serialized resources | Get project details for specific project ids |
Batch Create | POST | List of serialized created resources | Create multiple projects with the same settings |
Batch Update | PUT | List of serialized updated resources | Update a list of issues |
Batch Delete | DELETE | None | Delete a list of issues |
Here are some examples of how to use standard methods to represent complex tasks:
- Get count of a resource
- Count is part of the
List
API and is provided in header X-Total-Count param
- Count is part of the
- Get latest of a resource
- Order and filtering should happen as part of list api query parameters. Hereโs a good read.
- ๐
/api/0/issues/\{issue_id}/events/latest/
- ๐
/api/0/issues/\{issue_id}/events?orderBy=-date,limit=1
,-
for descending
Here are some notes that can help you decide between similar methods. We use Get here as an example but the same applies to all the other methods in the parenthesis.
- Get (Update, Delete): Use get on the
\{resource}DetailsEndpoint
to retrieve a resource. For example,ProjectDetailsEndpoint
. - List (Create, Batch Create, Batch Update, Batch Delete): Use get on the
\{resource-parent}\{resource}Endpoint
to retreive all resources that belong to that parent. For exampleTeamProjectsEndpoint
. - Batch Get (Batch Create, Batch Update, Batch Delete): Use get on the
\{root-parent}\{resource}Endpoint
. The difference betweenBatch
andList
is that batch usually includes a list ofids
as query parameter and returns details about those ids. This list does not necessarily belong to one parent. For example, we can't retrieve two projects that belong to two different teams in the above example and in that case we use the get method in the root resource, in this caseOrganizationProjectsEndpoint
.
Each response object returned from an API should be a serialized version of the Django model associated with the resource. You can see all the existing serializers here.
Note
Some models might have different serializers based on use case. For example, Project
can be serialized into DetailedProjectSerializer
or ProjectSerializer
. Decide which one to use based on your use case and API scope but DO NOT RETURN CUSTOM OBJECTS like {slug: project_slug, platform: project_platform
}. We want the API responses to be uniform and useable in multiple automations without adding extra complication to the external developers' code.
Our documentation is open source and available on GitHub. Your contributions are welcome, whether fixing a typo (drat!) or suggesting an update ("yeah, this would be better").