GraphQL Examples & SDKs¶
This guide provides client setup examples in cURL, JavaScript, and Python, along with 23 targeted query examples (recipes) to jumpstart your Nostalgia GraphQL API integration.
Client Setup¶
const API_KEY = 'nlk_YOUR_API_KEY';
const ENDPOINT = 'https://api.nostalgialab.org/';
async function gql(query, variables = {}) {
const res = await fetch(ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${API_KEY}`
},
body: JSON.stringify({ query, variables })
});
if (res.status === 403) throw new Error('Invalid or missing API key');
if (!res.ok) throw new Error(`HTTP error! Status: ${res.status}`);
const json = await res.json();
if (json.errors) throw new Error(json.errors[0].message);
return json.data;
}
import requests
API_KEY = 'nlk_YOUR_API_KEY'
ENDPOINT = 'https://api.nostalgialab.org/'
def gql(query, variables=None):
resp = requests.post(
ENDPOINT,
json={'query': query, 'variables': variables or {}},
headers={'Authorization': f'Bearer {API_KEY}'}
)
if resp.status_code == 403:
raise Exception('Invalid or missing API key')
resp.raise_for_status()
data = resp.json()
if 'errors' in data:
raise Exception(data['errors'][0]['message'])
return data['data']
Query Recipes¶
1. Get a Single Release by ID¶
{
release(id: 12345) {
releaseId
title
system
systemName
region
genre
genre2
developer
publisher
releaseDate
dateUs
dateJp
dateEu
score
players
online
discCount
series
seriesName
description
advancedDescription
}
}
2. List Releases by System¶
{
releases(system: "SNES", limit: 20) {
releaseId
title
score
developer
publisher
releaseDate
genre
}
}
Available system codes: NES SNES N64 NDS GBA GB GBC GEN SMS GG SAT PS1 PSP TGCD Amiga Atari2600 Atari5200 Atari7800 Lynx C64 Coleco NGP PC MD SEGACD VB WS
3. Filter by Genre¶
Genre is matched case-insensitively using ILIKE, so "rpg" and "Action RPG" both work.
{
releases(system: "SNES", genre: "RPG", limit: 25) {
releaseId
title
score
developer
genre
genre2
}
}
4. Filter by Region¶
Common region values: USA Japan Europe World Korea
5. Search by Title¶
Searches across all platforms. Uses ILIKE so partial matches work.
{
releases(search: "Your Search Term", limit: 20) {
releaseId
title
system
region
score
developer
}
}
6. Combine Multiple Filters¶
{
releases(
system: "GBA"
genre: "Action"
region: "USA"
limit: 15
offset: 0
) {
releaseId
title
score
genre
developer
publisher
}
}
7. Pagination¶
Max limit is 100. Use offset to step through large result sets.
JavaScript pagination loop:
async function getAllReleases(system) {
const results = [];
let offset = 0;
const limit = 100;
while (true) {
const data = await gql(`{
releases(system: "${system}", limit: ${limit}, offset: ${offset}) {
releaseId title score developer
}
}`);
results.push(...data.releases);
if (data.releases.length < limit) break;
offset += limit;
}
return results;
}
const allNes = await getAllReleases('NES');
console.log(`${allNes.length} NES games indexed`);
8. Get Box Art and Images¶
Each image type returns a presigned S3 URL valid for 15 minutes. null means not available for that release.
Get every image type at once using field aliases:
{
release(id: 12345) {
title
front: imageUrl(type: front)
back: imageUrl(type: back)
cart: imageUrl(type: cart)
main: imageUrl(type: main)
screenshot: imageUrl(type: screenshot)
screenshottitle: imageUrl(type: screenshottitle)
manual: imageUrl(type: manual)
wheel: imageUrl(type: wheel)
wheelcarbon: imageUrl(type: wheelcarbon)
wheelsteel: imageUrl(type: wheelsteel)
box3d: imageUrl(type: box3d)
boxtexture: imageUrl(type: boxtexture)
marquee: imageUrl(type: marquee)
marqueesmall: imageUrl(type: marqueesmall)
side: imageUrl(type: side)
video: imageUrl(type: video)
}
}
All ImageType values:
| Value | Description |
|---|---|
front |
Front box art |
back |
Back of box |
cart |
Cartridge or disc label |
main |
Primary image (best available) |
screenshot |
In-game screenshot |
screenshottitle |
Title screen screenshot |
texture |
Box texture for 3D rendering |
video |
Video thumbnail |
manual |
Game manual scan |
wheel |
Logo / wheel art (transparent PNG) |
wheelcarbon |
Wheel art on carbon background |
wheelsteel |
Wheel art on steel background |
marquee |
Arcade marquee banner |
marqueesmall |
Small marquee |
smallmarquee |
Alternative small marquee format |
box3d |
3D rendered box image |
boxtexture |
Raw box texture map |
side |
Side of box |
Presigned Image URL Lifespan
Presigned image and video URLs are valid for 15 minutes (900 seconds) from time of generation. Do not store or cache these URLs permanently; query the API dynamically to obtain fresh URLs.
9. Get Video URL¶
Returns a presigned URL valid for 15 minutes. null if no video exists for this release.
10. Get All Reviews for a Game¶
{
release(id: 12345) {
title
score
reviews {
id
title
body
rating
source
author
date
platform
url
type
createdAt
}
}
}
11. Get Regional Variants¶
Find all regional editions of the same game (USA, Japan, Europe releases):
{
release(id: 12345) {
title
region
otherRegions {
releaseId
title
region
dateUs
dateJp
dateEu
}
}
}
12. Get Other Platform Releases¶
Same game released on multiple consoles:
{
release(id: 12345) {
title
system
otherSystems {
releaseId
title
system
systemName
region
score
}
}
}
13. Get ROM Data for a Release¶
{
release(id: 12345) {
title
rom {
romId
filename
filenameNoExt
size
crc
md5
sha1
region
language
serial
dumpSource
}
}
}
14. Identify a ROM by MD5 Hash¶
Useful for matching a file you already have to its database entry:
{
roms(md5: "abc123def456abc123def456abc12345", limit: 5) {
romId
filename
size
crc
md5
sha1
releases {
releaseId
title
system
region
score
}
}
}
Also works with crc or sha1:
15. Look Up ROMs by Filename¶
{
roms(filename: "your-rom-filename", limit: 10) {
romId
filename
size
sha1
releases {
releaseId
title
system
region
}
}
}
16. Fetch a ROM by ID¶
{
rom(id: 9876) {
romId
filename
filenameNoExt
size
crc
md5
sha1
language
region
serial
parent
header
dumpSource
system {
name
shortName
manufacturer
}
releases {
releaseId
title
region
score
}
}
}
17. Get System Metadata and SQLite Download URL¶
The downloadUrl field returns a presigned S3 URL for the NostalgiaDB SQLite file for that system, valid for 1 hour:
{
releases(system: "SNES", limit: 1) {
system {
systemId
name
shortName
manufacturer
releaseYear
generation
mediaType
region
unitsSold
specs
description
downloadUrl
}
}
}
18. Complete Game Detail Query¶
Everything needed to render a full game page -- metadata, images, reviews, and cross-references:
{
release(id: 12345) {
releaseId
title
system
systemName
region
genre
genre2
developer
publisher
releaseDate
dateUs
dateJp
dateEu
score
players
online
discCount
description
advancedDescription
front: imageUrl(type: front)
back: imageUrl(type: back)
screenshot: imageUrl(type: screenshot)
manual: imageUrl(type: manual)
videoUrl
reviews {
rating
source
author
date
url
}
otherRegions {
releaseId
region
}
otherSystems {
releaseId
system
systemName
}
rom {
filename
size
md5
sha1
}
}
}
19. Find Games in a Series / Franchise¶
{
releases(search: "Game Series Name", system: "NES", limit: 20) {
releaseId
title
series
seriesName
releaseDate
score
developer
}
}
All games sharing the same series value belong to the same franchise.
20. Python: Identify a ROM File by Hash¶
import hashlib
def file_md5(path):
h = hashlib.md5()
with open(path, 'rb') as f:
for chunk in iter(lambda: f.read(8192), b''):
h.update(chunk)
return h.hexdigest()
def identify_rom(path):
md5 = file_md5(path)
result = gql(f'''{{
roms(md5: "{md5}", limit: 5) {{
romId filename size
releases {{
releaseId title system region score
}}
}}
}}''')
for rom in result['roms']:
print(f"File: {rom['filename']} ({rom['size']:,} bytes)")
for release in rom['releases']:
print(f" {release['title']} ({release['system']}, {release['region']}) Score: {release['score']}")
identify_rom('/path/to/your/rom.sfc')
21. Python: Download All Box Art for a System¶
import os, requests as req
def download_box_art(system, output_dir):
os.makedirs(output_dir, exist_ok=True)
offset, limit, count = 0, 50, 0
while True:
result = gql(f'''{{
releases(system: "{system}", limit: {limit}, offset: {offset}) {{
releaseId
title
imageUrl(type: front)
}}
}}''')
for release in result['releases']:
url = release.get('imageUrl')
if not url:
continue
path = os.path.join(output_dir, f"{release['releaseId']}.jpg")
if not os.path.exists(path):
img = req.get(url, timeout=30)
if img.status_code == 200:
open(path, 'wb').write(img.content)
count += 1
if len(result['releases']) < limit:
break
offset += limit
print(f'Downloaded {count} images for {system}')
download_box_art('SNES', './art/snes')
Presigned URL Expiration
Image URLs expire in 15 minutes. Download them immediately -- do not store the URL and download later.
22. JavaScript: Paginated Game Browser¶
async function getBrowsePage(system, page = 0) {
const limit = 24;
const data = await gql(`{
releases(system: "${system}", limit: ${limit}, offset: ${page * limit}) {
releaseId title score developer genre players
imageUrl(type: front)
}
}`);
return { games: data.releases, hasMore: data.releases.length === limit };
}
const { games } = await getBrowsePage('SNES', 0);
games.forEach(g => console.log(`${g.title} [${g.score}] - ${g.genre}`));
23. JavaScript: Build a Local Search Index¶
Fetch all releases for a system and build a fast client-side title lookup:
async function buildIndex(system) {
const all = [];
let offset = 0;
while (true) {
const data = await gql(`{
releases(system: "${system}", limit: 100, offset: ${offset}) {
releaseId title genre developer publisher score region
}
}`);
all.push(...data.releases);
if (data.releases.length < 100) break;
offset += 100;
}
const byTitle = {};
for (const r of all) {
const k = r.title.toLowerCase();
(byTitle[k] = byTitle[k] || []).push(r);
}
return { all, byTitle };
}
const index = await buildIndex('NES');
const hits = index.byTitle['your game title'] ?? [];
console.log(`Found ${index.all.length} NES games. Matched title: ${hits.length} version(s)`);