Skip to content

Latest commit

 

History

History
89 lines (69 loc) · 7.93 KB

File metadata and controls

89 lines (69 loc) · 7.93 KB
title Designing a New API
sidebar_order 2

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.

URL Patterns

The API's URL is what developers use to call the endpoint so it’s important that it is meaningful and clear.

Don't Exceed 3-level Nesting

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/

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 project
  • teams/{team_identifier}/projects/ refers to the projects a specific team has been added to

Naming Guidance

  • 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 or dateCreated.
  • For sorting and filtering, stick with the common param names: sortBy (e.g. sortBy=-dateCreated), orderBy (either asc or desc), groupBy, limit
  • Path params should be snake_case

Functionality

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
  • 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

Batch vs. Single Resource Methods

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 example TeamProjectsEndpoint.
  • Batch Get (Batch Create, Batch Update, Batch Delete): Use get on the {root-parent}{resource}Endpoint. The difference between Batch and List is that batch usually includes a list of ids 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 case OrganizationProjectsEndpoint.

Response Object

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.

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.