- GraphQL 및 REST API
- 프론트엔드
- 인스턴스 변수
- 엔티티
- 문서화
- 메서드 및 매개변수 설명
- 중단 변경사항
- 중단 변경 사항이란
- 중단 변경 사항이 아닌 것
- 실험적, 베타 및 일반적으로 사용 가능한 기능
- 선언된 매개변수
- 배열 타입
- HTTP 상태 헬퍼 사용하기
- HTTP 동사 선택하기
- GitLab Rails 코드베이스에서 API 경로 헬퍼 사용하기
- 사용자 정의 검증기
- 내부 API
- N+1 문제 피하기
- 테스트
- 변경 로그 항목 포함
API 스타일 가이드
이 스타일 가이드는 API 개발에 대한 모범 사례를 권장합니다.
GraphQL 및 REST API
우리는 고객에게 두 가지 유형의 API를 제공합니다:
두 API를 병렬로 지원하는 기술적 부담을 줄이기 위해, 가능한 한 구현을 공유해야 합니다.
예를 들어, 같은 서비스를 공유할 수 있습니다.
프론트엔드
프론트엔드 개발 시 어떤 API를 사용할지에 대한 세부 정보는 프론트엔드 가이드를 참조하세요.
인스턴스 변수
인스턴스 변수를 사용하지 마세요. 필요가 없습니다 (Rails 뷰에서처럼 접근할 필요가 없습니다). 로컬 변수면 충분합니다.
엔티티
항상 엔드포인트의 페이로드를 전달하기 위해 Entity를 사용하세요.
문서화
모든 새로운 API 엔드포인트 또는 업데이트된 API 엔드포인트는 문서와 함께 제공되어야 하며, 내부적이거나 기능 플래그 뒤에 있지 않은 경우에 해당합니다.
문서는 같은 병합 요청에 포함되어야 하거나, 엄격하게 필요하다면 원래 병합 요청과 동일한 마일스톤의 후속 요청에서 제공되어야 합니다.
Markdown 및 OpenAPI 정의 파일에서 API 리소스를 문서화하는 세부 정보는 문서화 스타일 가이드 RESTful API 페이지를 참조하세요.
메서드 및 매개변수 설명
모든 메서드는 Grape DSL을 사용하여 설명해야 합니다.
(좋은 예시는 environments.rb
에서 확인할 수 있습니다):
-
desc
는 메서드 요약을 위한 것입니다. 추가 세부사항을 위한 블록을 전달해야 합니다.- 엔드포인트가 추가된 GitLab 버전. 기능 플래그 뒤에 있다면 대신 언급합니다: 이 기능은 :feature_flag_symbol 기능 플래그로 차단됩니다.
- 엔드포인트가 Deprecated 되었는지 및 그렇다면 예정된 제거 날짜
-
params
는 메서드 매개변수를 위한 것입니다. 이는 설명, 매개변수 검증 및 코에르전 역할을 합니다.
좋은 예시는 다음과 같습니다:
desc '모든 방송 메시지 가져오기' do
detail '이 기능은 GitLab 8.12에서 도입되었습니다.'
success Entities::System::BroadcastMessage
end
params do
optional :page, type: Integer, desc: '현재 페이지 번호'
optional :per_page, type: Integer, desc: '페이지당 메시지 수'
end
get do
messages = System::BroadcastMessage.all
present paginate(messages), with: Entities::System::BroadcastMessage
end
중단 변경사항
REST API v4에 대한 중단 변경사항을 만들어서는 안 되며, 주요 GitLab 릴리즈에서도 마찬가지입니다.
우리의 REST API는 GitLab 버전 관리와 독립적으로 자체 버전 관리를 유지합니다.
현재 REST API 버전은 4
입니다. REST API에 대해 의미론적 버전 관리를 따를 것을 약속합니다.
이는 주요 버전 변경(대부분 5
)이 있기 전에는 중단 변경을 할 수 없음을 의미합니다.
버전 5
가 계획되어 있지 않으므로 드문 예외를 허용합니다.
중단 변경 대신 하위 호환성 수용
종종 하위 호환성은 API에서 변경된 기능을 구형 API 스키마에 계속 적응시켜 수용할 수 있습니다.
예를 들어, 우리의 REST API는 work_in_progress
와 draft
필드를 모두 노출합니다.
예외
예외는 다음의 경우에만 적용됩니다:
- 기능이 주요 GitLab 릴리스에서 제거되어야 합니다.
- 하위 호환성을 유지할 수 없습니다
어떤 형태로든. - 기능이 이전에 실험적 또는 베타로 표시된 경우.
이 예외는 드물어야 합니다.
이 예외에서도 필드나 인수를 제거하는 것보다 항상 다음과 같이 해야 합니다:
- 필드에 대해 빈 응답을 반환합니다 (예:
"null"
또는[]
). - 인수를 no-op로 전환합니다.
중단 변경 사항이란
중단 변경 사항의 몇 가지 예는 다음과 같습니다:
- 필드, 인수 또는 열거형 값의 제거 또는 이름 변경. JSON 응답에서 필드는 모든 JSON 키입니다.
- 엔드포인트 제거.
- 새로운 리디렉션 추가 (모든 클라이언트가 리디렉션을 따르지는 않음).
- 응답의 콘텐츠 유형 변경.
- 응답의 필드 유형 변경. JSON 응답에서 이는
Number
,String
,Boolean
,Array
또는Object
유형의 변경을 의미합니다. - 새로운 필수 인수를 추가.
- 인증, 인가 또는 기타 헤더 요구사항 변경.
-
아무 상태 코드 변경
500
이외.
중단 변경 사항이 아닌 것
중단 변경 사항이 아닌 몇 가지 예는 다음과 같습니다:
- 엔드포인트, 비필수 인수, 필드 또는 열거형 값을 추가하는 모든 추가 변경 사항.
- 오류 메시지 변경.
-
500
상태 코드를 지원되는 상태 코드로 변경 (이것은 버그 수정입니다). - 응답에서 반환되는 필드의 순서 변경.
실험적, 베타 및 일반적으로 사용 가능한 기능
API 요소를 실험적 및 베타 기능으로 추가할 수 있습니다. 이는 추가적인 변경이어야 하며, 그렇지 않으면
중단 변경 사항으로 분류됩니다.
실험 또는 베타로 표시된 API 요소는 하위 호환성 보장 정책에서 면제되며,
사전 통지 없이 언제든지 변경되거나 제거될 수 있습니다.
실험 상태일 때:
- 기본적으로 꺼져 있는 기능 플래그를 사용합니다.
- 플래그가 꺼져 있을 때:
- 추가된 엔드포인트는
404 Not Found
를 반환해야 합니다. - 추가된 인수는 무시되어야 합니다.
- 추가된 필드는 노출되지 않아야 합니다.
- 추가된 엔드포인트는
- API 문서는 실험 상태를 문서화해야 하며, 기능 플래그는 문서화되어야 합니다.
-
OpenAPI 문서는 변경 사항을 설명해서는 안 됩니다 (예: the
hidden
option을 사용하여).
베타 상태일 때:
- 기본적으로 켠 기능 플래그를 사용합니다.
- API 문서는 베타 상태를 문서화해야 하며, 기능 플래그는 문서화되어야 합니다.
- OpenAPI 문서는 변경 사항을 설명해서는 안 됩니다.
기능이 일반적으로 사용 가능해질 때:
- 기능 플래그를 제거합니다.
- API 문서에서 실험 또는 베타 상태를 제거합니다.
- 변경 사항을 프로그래밍적으로 검색 가능하게 만들기 위해 OpenAPI 문서를 추가합니다.
선언된 매개변수
Grape는 params
블록에 선언된 매개변수만 접근할 수 있습니다.
허용되지 않은 매개변수는 필터링됩니다.
– https://github.com/ruby-grape/grape#declared
부모 네임스페이스에서 매개변수 제외하기
기본적으로 declared(params)
는 모든 부모 네임스페이스에서 정의된 매개변수를 포함합니다.
– https://github.com/ruby-grape/grape#include-parent-namespaces
대부분의 경우 부모 네임스페이스에서 매개변수를 제외해야 합니다:
declared(params, include_parent_namespaces: false)
declared(params)
사용 시기
매개변수 해시를 메서드 호출의 인수로 전달할 때는 항상 declared(params)
를 사용해야 합니다.
예를 들어:
# 나쁜 예
User.create(params) # 사용자가 `admin=1`을 제출했다고 상상해 보세요... :)
# 좋은 예
User.create(declared(params, include_parent_namespaces: false).to_h)
참고:
declared(params)
는 Hashie::Mash
객체를 반환하며, 여기서 .to_h
를 호출해야 합니다.
하지만 단일 요소에 접근할 때는 params[key]
를 직접 사용할 수 있습니다.
예를 들어:
# 좋은 예
Model.create(foo: params[:foo])
배열 타입
Grape v1.3+부터, 배열 타입은 coerce_with
블록으로 정의되어야 하며, 그렇지 않으면 API 요청에서 문자열이 전달될 때 유효성 검사가 실패합니다.
자세한 내용은 Grape 업그레이드 문서를 참조하세요.
nil 입력 자동 변환
Grape v1.3.3 이전에는 nil
값을 가진 배열 매개변수가 자동으로 빈 배열로 변환되었습니다.
그러나 v1.3.3에서의 이 풀 리퀘스트로 인해 더 이상 그렇지 않습니다. 예를 들어, 선택적 매개변수를 가진 PUT /test
요청을 정의한다고 가정해 보겠습니다:
optional :user_ids, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: '이 규칙에 대한 사용자 아이디'
보통 PUT /test?user_ids
요청은 Grape가 { user_ids: nil }
의 params
를 전달하게 합니다.
이는 빈 배열을 기대하고 nil
입력을 제대로 처리하지 않는 엔드포인트에서 오류를 발생시킬 수 있습니다. 이전 동작을 유지하기 위해, 모든 API 호출의 before
블록에서 사용할 수 있는 도우미 메서드 coerce_nil_params_to_array!
가 있습니다:
before do
coerce_nil_params_to_array!
end
이 변경으로 PUT /test?user_ids
요청은 Grape가 { user_ids: [] }
의 params
를 전달하게 합니다.
Grape 트래커에 열린 문제가 이 작업을 더 쉽게 만들기 위해 있습니다.
HTTP 상태 헬퍼 사용하기
200이 아닌 HTTP 응답의 경우, 올바른 동작을 보장하기 위해 lib/api/helpers.rb
에 제공된 헬퍼를 사용하세요(예: not_found!
또는 no_content!
).
이들은 Grape 내에서 throw
되며 엔드포인트의 실행을 중단합니다.
DELETE
요청의 경우, 일반적으로 성공 시 기본적으로 204 No Content
응답을 반환하거나 주어진 If-Unmodified-Since
헤더가 범위를 벗어날 경우 412 Precondition Failed
응답을 반환하는 destroy_conditionally!
헬퍼를 사용하는 것이 좋습니다.
이 헬퍼는 전달된 리소스에 대해 #destroy
를 호출하지만 블록을 전달하여 사용자 지정 삭제 방법을 구현할 수도 있습니다.
HTTP 동사 선택하기
새로운 API 경로를 정의할 때는 올바른 HTTP 요청 방법을 사용하세요.
PATCH
와 PUT
결정하기
Rails 애플리케이션에서 PATCH
와 PUT
요청 방법 모두 컨트롤러의 update
메서드로 라우팅됩니다. GitLab API를 작성하는 데 사용하는 Grape 프레임워크에서는 업데이트를 수행하는 엔드포인트에 대해 PATCH
또는 PUT
HTTP 동사를 명시적으로 설정해야 합니다.
엔드포인트가 주어진 리소스의 모든 속성을 업데이트하는 경우, PUT
요청 방법을 사용하세요. 엔드포인트가 주어진 리소스의 일부 속성을 업데이트하는 경우, PATCH
요청 방법을 사용하세요.
PATCH
에 대한 좋은 예시는: PATCH /projects/:id/protected_branches/:name
PUT
에 대한 좋은 예시는: PUT /projects/:id/merge_requests/:merge_request_iid/approve
대개 좋은 PUT
엔드포인트는 id와 동사(위의 예에서는 “approve”)만 포함합니다.
또는 단일 값만 포함하고 키/값 쌍을 나타냅니다.
Rails 블로그에서는 업데이트를 수행하는 웹 API 엔드포인트에 대해 PATCH
가 일반적으로 가장 적합한 동사인 이유를 상세히 설명합니다.
GitLab Rails 코드베이스에서 API 경로 헬퍼 사용하기
GitLab을 상대 URL로 설치할 수 있기 때문에, Grape에 의해 생성된 API 경로 헬퍼를 사용할 때 이를 고려해야 합니다. 사용되는 모든 API 경로 헬퍼는 expose_path
헬퍼 호출로 래핑되어야 합니다.
예를 들어:
- endpoint = expose_path(api_v4_projects_issues_related_merge_requests_path(id: @project.id, issue_iid: @issue.iid))
사용자 정의 검증기
API 요청에서 일부 매개변수를 검증하기 위해, 이를 더 앞서 보내기 전에 검증합니다(예: Gitaly). 지금까지 추가한 사용자 정의 검증기는 다음과 같으며 사용 방법은 이와 같습니다. 새로운 사용자 정의 검증기를 추가하는 방법에 대한 가이드를 작성하기도 했습니다.
사용자 정의 검증기 사용하기
-
FilePath
:GitLab에서는 파일 경로를 탐색해야 하는 다양한 기능을 지원합니다.
FilePath
검증기는 다양한 경우에 대해 매개변수 값을 검증합니다. 주로, 경로가 상대인지 확인하고File::Separator
를 사용하여../../
상대 탐색이 포함되어 있는지와 경로가 절대 경로인지(예:/etc/passwd/
)를 확인합니다. 기본적으로 절대 경로는 허용되지 않습니다. 그러나 다음과 같은 방법으로 허용되는 절대 경로에 대한 허용 목록을 선택적으로 전달할 수 있습니다:requires :file_path, type: String, file_path: { allowlist: ['/foo/bar/', '/home/foo/', '/app/home'] }
-
Git SHA
:Git SHA
검증기는 Git SHA 매개변수가 유효한 SHA인지 확인합니다. 이는commit.rb
파일에 언급된 정규 표현식을 사용하여 확인합니다. -
Absence
:Absence
검증기는 특정 매개변수가 주어진 매개변수 해시에서 결여되어 있는지 확인합니다. -
IntegerNoneAny
:IntegerNoneAny
검증기는 주어진 매개변수의 값이Integer
,None
, 또는Any
중 하나인지 확인합니다. 요청을 진행하기 위해 언급된 값 중 하나만 허용합니다. -
ArrayNoneAny
:ArrayNoneAny
검증기는 주어진 매개변수의 값이Array
,None
, 또는Any
중 하나인지 확인합니다. 요청을 진행하기 위해 언급된 값 중 하나만 허용합니다. -
EmailOrEmailList
:EmailOrEmailList
검증기는 문자열 또는 문자열 목록의 값이 유효한 이메일 주소만 포함되는지 확인합니다. 모든 유효한 이메일 주소가 포함된 목록만 요청을 진행할 수 있습니다.
새로운 커스텀 검증기 추가하기
커스텀 검증기는 매개변수를 플랫폼에 전송하기 전에 검증하는 훌륭한 방법입니다.
잘못된 매개변수를 처음에 식별하면 서버와 플랫폼 간의 왕복을 줄일 수 있습니다.
커스텀 검증기를 추가해야 하는 경우, validators
디렉토리에 각자의 파일로 추가됩니다.
API를 추가하기 위해 Grape를 사용하기 때문에, 검증기 클래스에서 Grape::Validations::Validators::Base
클래스를 상속합니다.
이제, 당신이 해야 할 일은 validate_param!
메서드를 정의하는 것입니다. 이 메서드는 두 개의 매개변수: 검증할 param
이름과 params
해시를 받습니다.
메서드 본문은 매개변수 값을 검증하는 작업을 수행하고 호출 메서드에 적절한 오류 메시지를 반환합니다.
마지막으로, 아래와 같이 검증기를 등록합니다:
Grape::Validations.register_validator(<validator name as symbol>, ::API::Helpers::CustomValidators::<YourCustomValidatorClassName>)
검증기를 추가한 후에는 validators
디렉토리에 각자의 파일로 rspec
를 추가하는 것을 잊지 마세요.
내부 API
내부 API는 내부 사용을 위해 문서화되어 있습니다.
다양한 컴포넌트가 어떤 엔드포인트를 사용하고 있는지 알 수 있도록 항상 최신 상태로 유지하세요.
N+1 문제 피하기
API 엔드포인트에서 레코드 컬렉션을 반환할 때 일반적으로 발생하는 N+1 문제를 피하기 위해, 이ager loading(즉시 로딩)을 사용해야 합니다.
API 내에서 이를 수행하는 표준 방법은 모델이 API에서 반환되는 연관 관계와 데이터를 미리 로드하는 with_api_entity_associations
라는 스코프를 구현하는 것입니다.
이 스코프의 예는 Issue
모델에서 볼 수 있습니다.
같은 모델이 API에 여러 개의 엔티티(예: UserBasic
, User
및 UserPublic
)가 있을 경우, 이 스코프를 적용하는 데 있어 자 discretion를 사용해야 합니다.
가장 기본적인 엔티티에 대해 최적화하고, 후속 엔티티가 그 스코프를 기반으로 발전할 수 있습니다.
with_api_entity_associations
스코프는 자동으로 데이터를 미리 로드합니다.
todo API에서 반환될 때 Todo
_targets_에 대해.
미리 로드에 대한 더 많은 맥락과 논의는 이 스코프를 도입한 이 병합 요청을 확인하세요.
테스트로 검증하기
API 엔드포인트가 컬렉션을 반환할 때, API 엔드포인트가 N+1 문제가 없도록 항상 테스트를 추가하세요.
이를 위해 ActiveRecord::QueryRecorder
를 사용할 수 있습니다.
예시:
def make_api_request
get api('/foo', personal_access_token: pat)
end
it 'avoids N+1 queries', :request_store do
# 우선, 엔드포인트가 단일 레코드를 반환할 때 몇 개의 PostgreSQL 쿼리를 기록합니다.
create_record
control = ActiveRecord::QueryRecorder.new { make_api_request }
# 이제 두 번째 레코드를 생성하고 API가 이전보다 더 많은 쿼리를 실행하지 않는지 확인합니다.
create_record
expect { make_api_request }.not_to exceed_query_limit(control)
end
테스트
새로운 API 엔드포인트에 대한 테스트를 작성할 때는 /spec/fixtures/api/schemas
에 위치한 스키마 피크스쳐를 사용하는 것이 좋습니다.
응답이 주어진 스키마와 일치하는지 expect
할 수 있습니다:
expect(response).to match_response_schema('merge_requests')
테스트에서 N+1 성능 검증도 참조하세요.
변경 로그 항목 포함
모든 클라이언트에 대한 변경 사항 은 반드시 변경 로그 항목을 포함해야 합니다.
이것은 내부 API를 포함하지 않습니다.