- 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
15 KiB
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).
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:
{
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.
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:
{
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.
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 matchartist:Radiohead AND country:GB- Boolean operatorsartist:Radio*- Wildcard searchbegin:[1990 TO 2000]- Range queriestag:rock^2 tag:alternative- Boosting
Example:
{
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.
type Query {
node(id: ID!): Node
}
interface Node {
id: ID!
}
Example:
{
node(id: "QXJ0aXN0OjViMTFmNGNlLWE2MmQtNDcxZS04MWZjLWE2OWE4Mjc4YzdkYQ==") {
... on Artist {
name
country
}
}
}
Entity Types
Artist
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
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
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
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
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
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
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
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
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
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
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
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:
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 returnafter: String- Cursor for pagination
Example:
{
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:
{
browse {
releases(artist: "...", first: 10) {
nodes {
title
date
}
}
}
}
Extension Fields
Cover Art Archive
Added to Release type:
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:
{
lookup {
release(mbid: "...") {
title
coverArtArchive {
front
images {
image
thumbnails {
large
}
types
}
}
}
}
}
fanart.tv
Added to Artist type:
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:
{
lookup {
artist(mbid: "...") {
name
fanArt {
backgrounds {
url
likes
}
logosHD {
url
color
}
}
}
}
}
MediaWiki
Added to Artist type:
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:
{
lookup {
artist(mbid: "...") {
name
mediaWikiImages {
url
width
height
metadata {
name
value
}
}
}
}
}
TheAudioDB
Added to Artist type:
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:
{
lookup {
artist(mbid: "...") {
name
theAudioDB {
biographyEN
logo
fanArt {
url
}
}
}
}
}
Scalar Types
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:
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:
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:
{
"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 foundINVALID_MBID- Invalid MusicBrainz ID formatRATE_LIMIT- Rate limit exceededNETWORK_ERROR- Upstream API errorVALIDATION_ERROR- Invalid query parameters