A GraphQL schema, Express server, and middleware for querying the MusicBrainz API. It features an extensible schema to add integration with Discogs, Spotify, Last.fm, fanart.tv, and more!
Try out the live demo! 💡 Use the “Docs” sidebar, the schema, or the types docs to help construct your query.
Did this project bring you joy? Want to support updates? Check out my GitHub Sponsors page.
Alternatively…
Install with npm:
npm install graphbrainz --save
Install with Yarn:
yarn add graphbrainz
GraphBrainz is written and distributed as native ECMAScript modules (ESM) and requires a compatible version of Node.js
This package can be used both as a standalone GraphQL server and as Express middleware supplying a GraphQL endpoint.
Run the included graphbrainz
executable to start the server. The server is
configured using environment variables.
$ graphbrainz
Listening on port 3000.
Development mode features like JSON pretty printing and the GraphiQL
interface will be enabled unless the server is run with NODE_ENV=production
.
Note that if you are not running the standalone server within another Node
project, you may wish to install this package globally so that the graphbrainz
script is globally available:
npm install -g graphbrainz
If you have an existing Express server and want to add this GraphQL service as an endpoint, or you just want more customization, use the middleware.
import express from 'express';
import { middleware as graphbrainz } from 'graphbrainz';
const app = express();
// Use the default options:
app.use('/graphbrainz', graphbrainz());
// or, pass some options:
app.use('/graphbrainz', graphbrainz({
client: new MusicBrainz({ ... }),
graphiql: true,
...
}));
app.listen(3000);
The graphbrainz
middleware function accepts the following options:
client
: A custom API client instance to use. See the client submodule for help with creating a custom instance. You probably only need to do this if you want to adjust the rate limit and retry behavior.- Any remaining options are passed along to the standard GraphQL middleware. See the express-graphql documentation for more information.
If you just want to make queries from your app without running a separate server
or exposing a GraphQL endpoint, use the GraphBrainz schema with a library like
GraphQL.js. You just need to create the context
object that the
GraphBrainz resolvers expect, like so:
import { graphql } from 'graphql';
import { MusicBrainz, createContext, baseSchema } from 'graphbrainz';
const client = new MusicBrainz();
const context = createContext({ client });
graphql(
schema,
`
{
lookup {
releaseGroup(mbid: "99599db8-0e36-4a93-b0e8-350e9d7502a9") {
title
}
}
}
`,
null,
context
)
.then((result) => {
const { releaseGroup } = result.data.lookup;
console.log(`The album title is “${releaseGroup.title}”.`);
})
.catch((err) => {
console.error(err);
});
MUSICBRAINZ_BASE_URL
: The base MusicBrainz API URL to use. Change this if you are running your own MusicBrainz mirror. Defaults tohttp://musicbrainz.org/ws/2/
.GRAPHBRAINZ_PATH
: The URL route at which to expose the GraphQL endpoint, if running the standalone server. Defaults to/
.GRAPHBRAINZ_CORS_ORIGIN
: The value of theorigin
option to pass to the CORS middleware. Valid values aretrue
to reflect the request origin, a specific origin string to allow,*
to allow all origins, andfalse
to disable CORS (the default).GRAPHBRAINZ_CACHE_SIZE
: The maximum number of REST API responses to cache. Increasing the cache size and TTL will greatly lower query execution time for complex queries involving frequently accessed entities. Defaults to8192
.GRAPHBRAINZ_CACHE_TTL
: The maximum age of REST API responses in the cache, in milliseconds. Responses older than this will be disposed of (and re-requested) the next time they are accessed. Defaults to86400000
(one day).GRAPHBRAINZ_GRAPHIQL
: Set this totrue
if you want to force the GraphiQL interface to be available even in production mode.GRAPHBRAINZ_EXTENSIONS
: A JSON array of module paths to load as extensions.PORT
: Port number to use, if running the standalone server.
When running the standalone server, dotenv is used to load these variables
from a .env
file, if one exists in the current working directory. This just
makes it more convenient to launch the server with certain settings. See the
dotenv package for more information.
The DEBUG
environment variable can be used to enable logging for all (or just
some) of this package’s submodules:
$ DEBUG=graphbrainz:* graphbrainz
See the debug package for more information.
Nirvana albums and each album’s singles (try it):
query NirvanaAlbumSingles {
lookup {
artist(mbid: "5b11f4ce-a62d-471e-81fc-a69a8278c7da") {
name
releaseGroups(type: ALBUM) {
edges {
node {
title
firstReleaseDate
relationships {
releaseGroups(type: "single from") {
edges {
node {
target {
... on ReleaseGroup {
title
firstReleaseDate
}
}
}
}
}
}
}
}
}
}
}
}
The first five labels with “Apple” in the name (try it):
query AppleLabels {
search {
labels(query: "Apple", first: 5) {
...labelResults
}
}
}
fragment labelResults on LabelConnection {
pageInfo {
endCursor
}
edges {
cursor
node {
mbid
name
type
area {
name
}
}
}
}
…and the next five, using the endCursor
from the previous result
(try it):
query AppleLabels {
search {
labels(query: "Apple", first: 5, after: "YXJyYXljb25uZWN0aW9uOjQ=") {
...labelResults
}
}
}
Who the members of the band on an Apple Records release married, and when (try it):
query AppleRecordsMarriages {
search {
labels(query: "Apple Records", first: 1) {
edges {
node {
name
disambiguation
country
releases(first: 1) {
edges {
node {
title
date
artists {
edges {
node {
name
...bandMembers
}
}
}
}
}
}
}
}
}
}
}
fragment bandMembers on Artist {
relationships {
artists(direction: "backward", type: "member of band") {
edges {
node {
type
target {
... on Artist {
name
...marriages
}
}
}
}
}
}
}
fragment marriages on Artist {
relationships {
artists(type: "married") {
edges {
node {
type
direction
begin
end
target {
... on Artist {
name
}
}
}
}
}
}
}
Images of Tom Petty provided by various extensions (try it):
query TomPettyImages {
lookup {
artist(mbid: "5ca3f318-d028-4151-ac73-78e2b2d6cdcc") {
name
mediaWikiImages {
url
objectName
descriptionHTML
licenseShortName
}
fanArt {
thumbnails {
url
likeCount
}
}
theAudioDB {
logo
biography
}
}
}
}
You can find more example queries in the schema tests.
What’s with the cumbersome edges
/node
nesting? Why first
/after
instead
of limit
/offset
? Why mbid
instead of id
?
You can thank Relay for that; these are properties of a Relay-compliant schema. The schema was originally designed to be more user-friendly, but in the end I decided that being compatible with Relay was a worthwhile feature. I agree, it’s ugly.
The GraphBrainz schema includes an extra nodes
field on every connection type.
If you only want the nodes and no other fields on edges
, you can use nodes
as a shortcut.
Don’t forget that you can also use GraphQL aliases to rename fields
to your liking. For example, the following query renames edges
, node
, and
mbid
to results
, releaseGroup
, and id
, respectively:
query ChristmasAlbums {
search {
releaseGroups(query: "Christmas") {
results: edges {
releaseGroup: node {
id: mbid
title
}
}
}
}
}
Why does my query take so long?
It’s likely that your query requires multiple round trips to the MusicBrainz REST API, which is subject to rate limiting. While the query resolver tries very hard to fetch only the data necessary, and with the smallest number of API requests, it is not 100% optimal (yet). Make sure you are only requesting the fields you need and a reasonable level of nested entities – unless you are willing to wait.
You can also set up a local MusicBrainz mirror and configure GraphBrainz to use that with no rate limiting.
The types document is the easiest to browse representation of the schema, or you can read the schema in GraphQL syntax.
The GraphBrainz schema can easily be extended to add integrations with third-party services. See the Extensions docs for more info.