feat: initial implementation of metadata aggregator
- gRPC service with MusicBrainz provider - PostgreSQL schema with migrations - Service layer with database-first caching - Repository pattern for data access - YAML configuration support - Research documentation for 17 music metadata projects
This commit is contained in:
@@ -0,0 +1,902 @@
|
||||
# GraphBrainz API Reference
|
||||
|
||||
## Endpoint Configuration
|
||||
|
||||
| Parameter | Environment Variable | Default |
|
||||
|-----------|---------------------|---------|
|
||||
| Path | GRAPHBRAINZ_PATH | / |
|
||||
| Port | PORT | 3000 |
|
||||
| CORS Origin | GRAPHBRAINZ_CORS_ORIGIN | false |
|
||||
| GraphiQL | GRAPHBRAINZ_GRAPHIQL | true (development) |
|
||||
|
||||
## Query Types
|
||||
|
||||
GraphBrainz exposes four primary query entry points:
|
||||
|
||||
### 1. Lookup Queries
|
||||
|
||||
Direct entity retrieval by MusicBrainz ID (MBID).
|
||||
|
||||
```graphql
|
||||
type Query {
|
||||
lookup: LookupQuery
|
||||
}
|
||||
|
||||
type LookupQuery {
|
||||
area(mbid: String!): Area
|
||||
artist(mbid: String!): Artist
|
||||
collection(mbid: String!): Collection
|
||||
event(mbid: String!): Event
|
||||
instrument(mbid: String!): Instrument
|
||||
label(mbid: String!): Label
|
||||
place(mbid: String!): Place
|
||||
recording(mbid: String!): Recording
|
||||
release(mbid: String!): Release
|
||||
releaseGroup(mbid: String!): ReleaseGroup
|
||||
series(mbid: String!): Series
|
||||
url(mbid: String!): URL
|
||||
work(mbid: String!): Work
|
||||
}
|
||||
```
|
||||
|
||||
**Example**:
|
||||
```graphql
|
||||
{
|
||||
lookup {
|
||||
artist(mbid: "5b11f4ce-a62d-471e-81fc-a69a8278c7da") {
|
||||
name
|
||||
type
|
||||
country
|
||||
lifeSpan {
|
||||
begin
|
||||
end
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Browse Queries
|
||||
|
||||
Retrieve entities linked to a parent entity with cursor-based pagination.
|
||||
|
||||
```graphql
|
||||
type Query {
|
||||
browse: BrowseQuery
|
||||
}
|
||||
|
||||
type BrowseQuery {
|
||||
areas(
|
||||
collection: String
|
||||
first: Int
|
||||
after: String
|
||||
): AreaConnection
|
||||
|
||||
artists(
|
||||
area: String
|
||||
collection: String
|
||||
recording: String
|
||||
release: String
|
||||
releaseGroup: String
|
||||
work: String
|
||||
first: Int
|
||||
after: String
|
||||
): ArtistConnection
|
||||
|
||||
collections(
|
||||
area: String
|
||||
artist: String
|
||||
editor: String
|
||||
event: String
|
||||
label: String
|
||||
place: String
|
||||
recording: String
|
||||
release: String
|
||||
releaseGroup: String
|
||||
work: String
|
||||
first: Int
|
||||
after: String
|
||||
): CollectionConnection
|
||||
|
||||
events(
|
||||
area: String
|
||||
artist: String
|
||||
collection: String
|
||||
place: String
|
||||
first: Int
|
||||
after: String
|
||||
): EventConnection
|
||||
|
||||
labels(
|
||||
area: String
|
||||
collection: String
|
||||
release: String
|
||||
first: Int
|
||||
after: String
|
||||
): LabelConnection
|
||||
|
||||
places(
|
||||
area: String
|
||||
collection: String
|
||||
first: Int
|
||||
after: String
|
||||
): PlaceConnection
|
||||
|
||||
recordings(
|
||||
artist: String
|
||||
collection: String
|
||||
release: String
|
||||
first: Int
|
||||
after: String
|
||||
): RecordingConnection
|
||||
|
||||
releases(
|
||||
area: String
|
||||
artist: String
|
||||
collection: String
|
||||
label: String
|
||||
recording: String
|
||||
releaseGroup: String
|
||||
track: String
|
||||
trackArtist: String
|
||||
first: Int
|
||||
after: String
|
||||
): ReleaseConnection
|
||||
|
||||
releaseGroups(
|
||||
artist: String
|
||||
collection: String
|
||||
release: String
|
||||
first: Int
|
||||
after: String
|
||||
): ReleaseGroupConnection
|
||||
}
|
||||
```
|
||||
|
||||
**Example**:
|
||||
```graphql
|
||||
{
|
||||
browse {
|
||||
releases(
|
||||
artist: "5b11f4ce-a62d-471e-81fc-a69a8278c7da"
|
||||
first: 10
|
||||
) {
|
||||
edges {
|
||||
node {
|
||||
title
|
||||
date
|
||||
status
|
||||
}
|
||||
}
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
endCursor
|
||||
}
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Search Queries
|
||||
|
||||
Lucene-based full-text search across entity types.
|
||||
|
||||
```graphql
|
||||
type Query {
|
||||
search: SearchQuery
|
||||
}
|
||||
|
||||
type SearchQuery {
|
||||
areas(query: String!, first: Int, after: String): AreaConnection
|
||||
artists(query: String!, first: Int, after: String): ArtistConnection
|
||||
events(query: String!, first: Int, after: String): EventConnection
|
||||
instruments(query: String!, first: Int, after: String): InstrumentConnection
|
||||
labels(query: String!, first: Int, after: String): LabelConnection
|
||||
places(query: String!, first: Int, after: String): PlaceConnection
|
||||
recordings(query: String!, first: Int, after: String): RecordingConnection
|
||||
releases(query: String!, first: Int, after: String): ReleaseConnection
|
||||
releaseGroups(query: String!, first: Int, after: String): ReleaseGroupConnection
|
||||
works(query: String!, first: Int, after: String): WorkConnection
|
||||
}
|
||||
```
|
||||
|
||||
**Lucene Query Syntax**:
|
||||
- `artist:"Radiohead"` - Exact phrase match
|
||||
- `artist:Radiohead AND country:GB` - Boolean operators
|
||||
- `artist:Radio*` - Wildcard search
|
||||
- `begin:[1990 TO 2000]` - Range queries
|
||||
- `tag:rock^2 tag:alternative` - Boosting
|
||||
|
||||
**Example**:
|
||||
```graphql
|
||||
{
|
||||
search {
|
||||
artists(query: "artist:Radiohead AND country:GB", first: 5) {
|
||||
edges {
|
||||
node {
|
||||
name
|
||||
country
|
||||
type
|
||||
score
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Node Query (Relay)
|
||||
|
||||
Global object identification via Relay-compliant node interface.
|
||||
|
||||
```graphql
|
||||
type Query {
|
||||
node(id: ID!): Node
|
||||
}
|
||||
|
||||
interface Node {
|
||||
id: ID!
|
||||
}
|
||||
```
|
||||
|
||||
**Example**:
|
||||
```graphql
|
||||
{
|
||||
node(id: "QXJ0aXN0OjViMTFmNGNlLWE2MmQtNDcxZS04MWZjLWE2OWE4Mjc4YzdkYQ==") {
|
||||
... on Artist {
|
||||
name
|
||||
country
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Entity Types
|
||||
|
||||
### Artist
|
||||
|
||||
```graphql
|
||||
type Artist implements Node {
|
||||
id: ID!
|
||||
mbid: MBID!
|
||||
name: String
|
||||
sortName: String
|
||||
disambiguation: String
|
||||
type: String
|
||||
typeID: MBID
|
||||
country: String
|
||||
area: Area
|
||||
beginArea: Area
|
||||
endArea: Area
|
||||
lifeSpan: LifeSpan
|
||||
gender: String
|
||||
genderID: MBID
|
||||
ipis: [IPI]
|
||||
isnis: [ISNI]
|
||||
aliases: [Alias]
|
||||
recordings: RecordingConnection
|
||||
releases: ReleaseConnection
|
||||
releaseGroups: ReleaseGroupConnection
|
||||
works: WorkConnection
|
||||
relationships: RelationshipConnection
|
||||
collections: CollectionConnection
|
||||
tags: TagConnection
|
||||
|
||||
# Extension fields
|
||||
fanArt: FanArtImages
|
||||
mediaWikiImages: [MediaWikiImage]
|
||||
theAudioDB: TheAudioDBArtist
|
||||
}
|
||||
```
|
||||
|
||||
### Release
|
||||
|
||||
```graphql
|
||||
type Release implements Node {
|
||||
id: ID!
|
||||
mbid: MBID!
|
||||
title: String
|
||||
disambiguation: String
|
||||
asin: String
|
||||
status: String
|
||||
statusID: MBID
|
||||
packaging: String
|
||||
packagingID: MBID
|
||||
quality: String
|
||||
date: Date
|
||||
country: String
|
||||
barcode: String
|
||||
artists: [Artist]
|
||||
artistCredit: [ArtistCredit]
|
||||
labels: [ReleaseLabel]
|
||||
media: [Medium]
|
||||
releaseGroup: ReleaseGroup
|
||||
relationships: RelationshipConnection
|
||||
collections: CollectionConnection
|
||||
tags: TagConnection
|
||||
|
||||
# Extension fields
|
||||
coverArtArchive: CoverArtArchiveRelease
|
||||
}
|
||||
```
|
||||
|
||||
### Recording
|
||||
|
||||
```graphql
|
||||
type Recording implements Node {
|
||||
id: ID!
|
||||
mbid: MBID!
|
||||
title: String
|
||||
disambiguation: String
|
||||
length: Duration
|
||||
video: Boolean
|
||||
isrcs: [ISRC]
|
||||
artists: [Artist]
|
||||
artistCredit: [ArtistCredit]
|
||||
releases: ReleaseConnection
|
||||
relationships: RelationshipConnection
|
||||
collections: CollectionConnection
|
||||
tags: TagConnection
|
||||
}
|
||||
```
|
||||
|
||||
### ReleaseGroup
|
||||
|
||||
```graphql
|
||||
type ReleaseGroup implements Node {
|
||||
id: ID!
|
||||
mbid: MBID!
|
||||
title: String
|
||||
disambiguation: String
|
||||
type: String
|
||||
typeID: MBID
|
||||
primaryType: String
|
||||
primaryTypeID: MBID
|
||||
secondaryTypes: [String]
|
||||
secondaryTypeIDs: [MBID]
|
||||
firstReleaseDate: Date
|
||||
artists: [Artist]
|
||||
artistCredit: [ArtistCredit]
|
||||
releases: ReleaseConnection
|
||||
relationships: RelationshipConnection
|
||||
collections: CollectionConnection
|
||||
tags: TagConnection
|
||||
}
|
||||
```
|
||||
|
||||
### Area
|
||||
|
||||
```graphql
|
||||
type Area implements Node {
|
||||
id: ID!
|
||||
mbid: MBID!
|
||||
name: String
|
||||
sortName: String
|
||||
disambiguation: String
|
||||
type: String
|
||||
typeID: MBID
|
||||
iso31661Codes: [String]
|
||||
iso31662Codes: [String]
|
||||
iso31663Codes: [String]
|
||||
lifeSpan: LifeSpan
|
||||
aliases: [Alias]
|
||||
relationships: RelationshipConnection
|
||||
collections: CollectionConnection
|
||||
tags: TagConnection
|
||||
}
|
||||
```
|
||||
|
||||
### Label
|
||||
|
||||
```graphql
|
||||
type Label implements Node {
|
||||
id: ID!
|
||||
mbid: MBID!
|
||||
name: String
|
||||
sortName: String
|
||||
disambiguation: String
|
||||
type: String
|
||||
typeID: MBID
|
||||
labelCode: Int
|
||||
ipis: [IPI]
|
||||
area: Area
|
||||
lifeSpan: LifeSpan
|
||||
aliases: [Alias]
|
||||
releases: ReleaseConnection
|
||||
relationships: RelationshipConnection
|
||||
collections: CollectionConnection
|
||||
tags: TagConnection
|
||||
}
|
||||
```
|
||||
|
||||
### Work
|
||||
|
||||
```graphql
|
||||
type Work implements Node {
|
||||
id: ID!
|
||||
mbid: MBID!
|
||||
title: String
|
||||
disambiguation: String
|
||||
type: String
|
||||
typeID: MBID
|
||||
language: String
|
||||
languages: [String]
|
||||
iswcs: [ISWC]
|
||||
artists: [Artist]
|
||||
relationships: RelationshipConnection
|
||||
collections: CollectionConnection
|
||||
tags: TagConnection
|
||||
}
|
||||
```
|
||||
|
||||
### Event
|
||||
|
||||
```graphql
|
||||
type Event implements Node {
|
||||
id: ID!
|
||||
mbid: MBID!
|
||||
name: String
|
||||
disambiguation: String
|
||||
type: String
|
||||
typeID: MBID
|
||||
time: String
|
||||
cancelled: Boolean
|
||||
setlist: String
|
||||
lifeSpan: LifeSpan
|
||||
aliases: [Alias]
|
||||
relationships: RelationshipConnection
|
||||
collections: CollectionConnection
|
||||
tags: TagConnection
|
||||
}
|
||||
```
|
||||
|
||||
### Place
|
||||
|
||||
```graphql
|
||||
type Place implements Node {
|
||||
id: ID!
|
||||
mbid: MBID!
|
||||
name: String
|
||||
disambiguation: String
|
||||
type: String
|
||||
typeID: MBID
|
||||
address: String
|
||||
area: Area
|
||||
coordinates: Coordinates
|
||||
lifeSpan: LifeSpan
|
||||
aliases: [Alias]
|
||||
relationships: RelationshipConnection
|
||||
collections: CollectionConnection
|
||||
tags: TagConnection
|
||||
}
|
||||
```
|
||||
|
||||
### Instrument
|
||||
|
||||
```graphql
|
||||
type Instrument implements Node {
|
||||
id: ID!
|
||||
mbid: MBID!
|
||||
name: String
|
||||
disambiguation: String
|
||||
type: String
|
||||
typeID: MBID
|
||||
description: String
|
||||
aliases: [Alias]
|
||||
relationships: RelationshipConnection
|
||||
collections: CollectionConnection
|
||||
tags: TagConnection
|
||||
}
|
||||
```
|
||||
|
||||
### Series
|
||||
|
||||
```graphql
|
||||
type Series implements Node {
|
||||
id: ID!
|
||||
mbid: MBID!
|
||||
name: String
|
||||
disambiguation: String
|
||||
type: String
|
||||
typeID: MBID
|
||||
aliases: [Alias]
|
||||
relationships: RelationshipConnection
|
||||
collections: CollectionConnection
|
||||
tags: TagConnection
|
||||
}
|
||||
```
|
||||
|
||||
### Collection
|
||||
|
||||
```graphql
|
||||
type Collection implements Node {
|
||||
id: ID!
|
||||
mbid: MBID!
|
||||
name: String
|
||||
editor: String
|
||||
type: String
|
||||
typeID: MBID
|
||||
entityType: String
|
||||
areas: AreaConnection
|
||||
artists: ArtistConnection
|
||||
events: EventConnection
|
||||
instruments: InstrumentConnection
|
||||
labels: LabelConnection
|
||||
places: PlaceConnection
|
||||
recordings: RecordingConnection
|
||||
releases: ReleaseConnection
|
||||
releaseGroups: ReleaseGroupConnection
|
||||
series: SeriesConnection
|
||||
works: WorkConnection
|
||||
}
|
||||
```
|
||||
|
||||
## Relay Connection Types
|
||||
|
||||
All list fields return Relay-compliant connection types:
|
||||
|
||||
```graphql
|
||||
type ArtistConnection {
|
||||
edges: [ArtistEdge]
|
||||
nodes: [Artist]
|
||||
pageInfo: PageInfo!
|
||||
totalCount: Int
|
||||
}
|
||||
|
||||
type ArtistEdge {
|
||||
node: Artist
|
||||
cursor: String!
|
||||
score: Int # Only present in search results
|
||||
}
|
||||
|
||||
type PageInfo {
|
||||
hasNextPage: Boolean!
|
||||
hasPreviousPage: Boolean!
|
||||
startCursor: String
|
||||
endCursor: String
|
||||
}
|
||||
```
|
||||
|
||||
### Pagination
|
||||
|
||||
- `first: Int` - Number of items to return
|
||||
- `after: String` - Cursor for pagination
|
||||
|
||||
**Example**:
|
||||
```graphql
|
||||
{
|
||||
browse {
|
||||
releases(artist: "...", first: 10) {
|
||||
edges {
|
||||
node { title }
|
||||
cursor
|
||||
}
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
endCursor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Next page
|
||||
{
|
||||
browse {
|
||||
releases(artist: "...", first: 10, after: "Y3Vyc29yOjEw") {
|
||||
edges {
|
||||
node { title }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Nodes Shortcut
|
||||
|
||||
Access nodes directly without edges:
|
||||
|
||||
```graphql
|
||||
{
|
||||
browse {
|
||||
releases(artist: "...", first: 10) {
|
||||
nodes {
|
||||
title
|
||||
date
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Extension Fields
|
||||
|
||||
### Cover Art Archive
|
||||
|
||||
Added to `Release` type:
|
||||
|
||||
```graphql
|
||||
type Release {
|
||||
coverArtArchive: CoverArtArchiveRelease
|
||||
}
|
||||
|
||||
type CoverArtArchiveRelease {
|
||||
front: Boolean
|
||||
back: Boolean
|
||||
artwork: Boolean
|
||||
count: Int
|
||||
release: String
|
||||
images: [CoverArtArchiveImage]
|
||||
}
|
||||
|
||||
type CoverArtArchiveImage {
|
||||
fileID: String
|
||||
image: String
|
||||
thumbnails: CoverArtArchiveThumbnails
|
||||
front: Boolean
|
||||
back: Boolean
|
||||
types: [String]
|
||||
edit: Int
|
||||
approved: Boolean
|
||||
comment: String
|
||||
}
|
||||
|
||||
type CoverArtArchiveThumbnails {
|
||||
small: String
|
||||
large: String
|
||||
}
|
||||
```
|
||||
|
||||
**Example**:
|
||||
```graphql
|
||||
{
|
||||
lookup {
|
||||
release(mbid: "...") {
|
||||
title
|
||||
coverArtArchive {
|
||||
front
|
||||
images {
|
||||
image
|
||||
thumbnails {
|
||||
large
|
||||
}
|
||||
types
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### fanart.tv
|
||||
|
||||
Added to `Artist` type:
|
||||
|
||||
```graphql
|
||||
type Artist {
|
||||
fanArt: FanArtImages
|
||||
}
|
||||
|
||||
type FanArtImages {
|
||||
backgrounds: [FanArtImage]
|
||||
banners: [FanArtImage]
|
||||
logos: [FanArtLabelImage]
|
||||
logosHD: [FanArtLabelImage]
|
||||
thumbnails: [FanArtImage]
|
||||
}
|
||||
|
||||
type FanArtImage {
|
||||
imageID: String
|
||||
url: String
|
||||
likes: Int
|
||||
}
|
||||
|
||||
type FanArtLabelImage {
|
||||
imageID: String
|
||||
url: String
|
||||
likes: Int
|
||||
color: String
|
||||
}
|
||||
```
|
||||
|
||||
**Configuration**: Requires `FANART_API_KEY` environment variable.
|
||||
|
||||
**Example**:
|
||||
```graphql
|
||||
{
|
||||
lookup {
|
||||
artist(mbid: "...") {
|
||||
name
|
||||
fanArt {
|
||||
backgrounds {
|
||||
url
|
||||
likes
|
||||
}
|
||||
logosHD {
|
||||
url
|
||||
color
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### MediaWiki
|
||||
|
||||
Added to `Artist` type:
|
||||
|
||||
```graphql
|
||||
type Artist {
|
||||
mediaWikiImages: [MediaWikiImage]
|
||||
}
|
||||
|
||||
type MediaWikiImage {
|
||||
url: String
|
||||
descriptionURL: String
|
||||
title: String
|
||||
user: String
|
||||
size: Int
|
||||
width: Int
|
||||
height: Int
|
||||
canonicalTitle: String
|
||||
objectName: String
|
||||
descriptionShortURL: String
|
||||
metadata: [MediaWikiImageMetadata]
|
||||
}
|
||||
|
||||
type MediaWikiImageMetadata {
|
||||
name: String
|
||||
value: String
|
||||
}
|
||||
```
|
||||
|
||||
**Example**:
|
||||
```graphql
|
||||
{
|
||||
lookup {
|
||||
artist(mbid: "...") {
|
||||
name
|
||||
mediaWikiImages {
|
||||
url
|
||||
width
|
||||
height
|
||||
metadata {
|
||||
name
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### TheAudioDB
|
||||
|
||||
Added to `Artist` type:
|
||||
|
||||
```graphql
|
||||
type Artist {
|
||||
theAudioDB: TheAudioDBArtist
|
||||
}
|
||||
|
||||
type TheAudioDBArtist {
|
||||
artistID: String
|
||||
biography: String
|
||||
biographyEN: String
|
||||
memberCount: Int
|
||||
banner: String
|
||||
logo: String
|
||||
thumbnail: String
|
||||
fanArt: [TheAudioDBImage]
|
||||
}
|
||||
|
||||
type TheAudioDBImage {
|
||||
url: String
|
||||
}
|
||||
```
|
||||
|
||||
**Configuration**: Requires `THEAUDIODB_API_KEY` environment variable.
|
||||
|
||||
**Example**:
|
||||
```graphql
|
||||
{
|
||||
lookup {
|
||||
artist(mbid: "...") {
|
||||
name
|
||||
theAudioDB {
|
||||
biographyEN
|
||||
logo
|
||||
fanArt {
|
||||
url
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Scalar Types
|
||||
|
||||
```graphql
|
||||
scalar MBID # MusicBrainz ID (UUID format)
|
||||
scalar Date # ISO 8601 date (YYYY-MM-DD)
|
||||
scalar Duration # Milliseconds (integer)
|
||||
scalar IPI # Interested Parties Information code
|
||||
scalar ISNI # International Standard Name Identifier
|
||||
scalar ISRC # International Standard Recording Code
|
||||
scalar ISWC # International Standard Musical Work Code
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
Core GraphBrainz API requires no authentication. Extensions may require API keys:
|
||||
|
||||
| Extension | Environment Variable | Required |
|
||||
|-----------|---------------------|----------|
|
||||
| fanart.tv | FANART_API_KEY | Yes |
|
||||
| TheAudioDB | THEAUDIODB_API_KEY | Yes |
|
||||
| Cover Art Archive | - | No |
|
||||
| MediaWiki | - | No |
|
||||
|
||||
## CORS Configuration
|
||||
|
||||
Enable CORS via environment variable:
|
||||
|
||||
```bash
|
||||
GRAPHBRAINZ_CORS_ORIGIN="https://example.com"
|
||||
# or
|
||||
GRAPHBRAINZ_CORS_ORIGIN="*"
|
||||
```
|
||||
|
||||
Default: `false` (CORS disabled)
|
||||
|
||||
## GraphiQL Interface
|
||||
|
||||
Interactive GraphQL IDE enabled by default in development mode.
|
||||
|
||||
**Configuration**:
|
||||
```bash
|
||||
GRAPHBRAINZ_GRAPHIQL=true # Enable
|
||||
GRAPHBRAINZ_GRAPHIQL=false # Disable
|
||||
```
|
||||
|
||||
Access at configured path (default: http://localhost:3000/)
|
||||
|
||||
## Rate Limits
|
||||
|
||||
GraphBrainz enforces MusicBrainz API rate limits:
|
||||
|
||||
- **MusicBrainz**: 5 requests per 5.5 seconds
|
||||
- **Extensions**: 10 requests per second (default)
|
||||
|
||||
Rate limit errors return HTTP 429 with retry-after header.
|
||||
|
||||
## Error Handling
|
||||
|
||||
GraphQL errors follow standard format:
|
||||
|
||||
```json
|
||||
{
|
||||
"errors": [
|
||||
{
|
||||
"message": "Artist not found",
|
||||
"locations": [{ "line": 2, "column": 3 }],
|
||||
"path": ["lookup", "artist"],
|
||||
"extensions": {
|
||||
"code": "NOT_FOUND",
|
||||
"mbid": "invalid-mbid"
|
||||
}
|
||||
}
|
||||
],
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
Error codes:
|
||||
|
||||
- `NOT_FOUND` - Entity not found
|
||||
- `INVALID_MBID` - Invalid MusicBrainz ID format
|
||||
- `RATE_LIMIT` - Rate limit exceeded
|
||||
- `NETWORK_ERROR` - Upstream API error
|
||||
- `VALIDATION_ERROR` - Invalid query parameters
|
||||
Reference in New Issue
Block a user