skinsrestorer-js
v1.0.0
Published
Read player skins, history and favourites from SkinsRestorer plugin database (MySQL/PostgreSQL)
Maintainers
Readme
skinsrestorer-js
Node.js library for reading player skins, history, and favourites from a SkinsRestorer plugin database. Supports MySQL and PostgreSQL.
📦 Installation
npm install skinsrestorer-jsInstall the driver for your database:
# MySQL
npm install mysql2
# PostgreSQL
npm install pg🚀 Quick start
import { SkinsRestorer } from "skinsrestorer-js";
const sr = new SkinsRestorer({
database: {
type: "mysql",
host: "localhost",
port: 3306,
user: "root",
password: "secret",
database: "minecraft",
},
tablePrefix: "sr_", // optional, default: 'sr_'
onlineMode: false, // optional, default: false
});
await sr.connect();
const skin = await sr.getByName("S0yoneyro");
if (skin) {
console.log(skin.skin); // Mojang CDN texture URL
console.log(skin.variant); // 'SLIM' | 'CLASSIC' | null
console.log(skin.type); // 'CUSTOM' | 'URL' | 'PLAYER' | null
console.log(skin.cape); // cape URL or null
// Download the skin as a PNG buffer
const png = await skin.fetchImage();
}
await sr.disconnect();History, favourites & custom skins
// Skin change history (paginated)
const history = await sr.getHistoryByName('S0yoneyro', { page: 1, pageSize: 5 });
for (const entry of history.data) {
console.log(entry.type, entry.source, new Date(entry.timestamp));
}
// Player's favourite skins
const favs = await sr.getFavouritesByName('S0yoneyro', { pageSize: 10 });
// Server-wide custom skins (uploaded by admins)
const custom = await sr.getCustomSkins({ search: 'cool' });
for (const skin of custom.data) {
console.log(skin.name, skin.displayName);
const png = await skin.fetchImage();
}Save skin image to file
import { writeFile } from 'fs/promises';
const skin = await sr.getByName('S0yoneyro');
if (skin) {
const png = await skin.fetchImage();
await writeFile('skin.png', png);
}⚙️ Constructor
new SkinsRestorer(options)
| Option | Type | Default | Description |
| ------------------- | ----------------------- | --------------- | --------------------------------------------------------- |
| database.type | 'mysql' \| 'postgres' | — | Database type |
| database.host | string | — | Host |
| database.port | number | 3306 / 5432 | Port |
| database.user | string | — | User |
| database.password | string | — | Password |
| database.database | string | — | Database name |
| tablePrefix | string | 'sr_' | SkinRestorer table prefix |
| onlineMode | boolean | false | Server online mode (see below) |
📖 Methods
Skin lookup
| Method | Returns | Description |
| ------------------------ | ----------------------------- | --------------------------------------------------------- |
| sr.connect() | Promise<void> | Open database connection |
| sr.disconnect() | Promise<void> | Close database connection |
| sr.getByName(nickname) | Promise<SkinResult \| null> | Get skin by player nickname |
| sr.getByUUID(uuid) | Promise<SkinResult \| null> | Get skin by UUID (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) |
History & favourites
All list methods accept an optional ListOptions and return a PaginatedResult<T>.
| Method | Returns |
| ----------------------------------------- | ---------------------------------- |
| sr.getHistoryByName(nickname, opts?) | PaginatedResult<SkinEntry> |
| sr.getHistoryByUUID(uuid, opts?) | PaginatedResult<SkinEntry> |
| sr.getFavouritesByName(nickname, opts?) | PaginatedResult<SkinEntry> |
| sr.getFavouritesByUUID(uuid, opts?) | PaginatedResult<SkinEntry> |
| sr.getCustomSkins(opts?) | PaginatedResult<CustomSkinEntry> |
ListOptions
| Field | Type | Default | Description |
| ---------- | -------- | ------- | -------------------------------- |
| page | number | 1 | Page number (1-based) |
| pageSize | number | 20 | Items per page |
| search | string | — | Filter by skin identifier / name |
PaginatedResult<T>
{
data: T[];
total: number;
page: number;
pageSize: number;
totalPages: number;
}📋 Response types
SkinResult
Returned by getByName / getByUUID.
| Field | Type | Description |
| --------------- | --------------------------------------- | ------------------------------------------------------------------- |
| skin | string \| null | Mojang CDN skin texture URL |
| variant | 'SLIM' \| 'CLASSIC' \| null | Arm model (Alex / Steve) |
| type | 'CUSTOM' \| 'URL' \| 'PLAYER' \| null | How the skin was assigned |
| source | string \| null | Original URL (for URL type) or source player UUID (for PLAYER type) |
| cape | string \| null | Cape texture URL |
| fetchImage() | Promise<Buffer> | Downloads the skin PNG from Mojang CDN |
| raw.value | string \| null | Base64 Mojang texture property |
| raw.signature | string \| null | Mojang RSA signature |
SkinEntry
Returned in history and favourites lists. Same fields as SkinResult plus:
| Field | Type | Description |
| ----------- | -------- | ------------------------------------- |
| timestamp | number | When this skin was applied (epoch ms) |
CustomSkinEntry
Returned in custom skins list.
| Field | Type | Description |
| --------------- | ----------------------------- | --------------------------- |
| skin | string \| null | Mojang CDN skin texture URL |
| variant | 'SLIM' \| 'CLASSIC' \| null | Arm model |
| name | string | Internal skin name |
| displayName | string \| null | Display name |
| cape | string \| null | Cape texture URL |
| fetchImage() | Promise<Buffer> | Downloads the skin PNG |
| raw.value | string \| null | Base64 texture property |
| raw.signature | string \| null | Mojang RSA signature |
🌐 Online vs offline mode
Minecraft servers store player data under different UUIDs depending on their mode:
- Offline mode (default) — UUID is derived from the nickname via MD5 (
OfflinePlayer:<nick>) - Online mode — UUID comes from Mojang authentication
Set onlineMode: true if your server runs in online mode. This changes how getByName resolves the UUID:
| Mode | getByName behaviour |
| --------- | ----------------------------------------------------------------------------------- |
| offline | Computes offline UUID from nickname, also checks Mojang UUID from cache as fallback |
| online | Looks up Mojang UUID from sr_cache table; returns null if not found |
🛠 Utilities
computeOfflineUUID(nickname)
Computes the offline-mode UUID for a Minecraft nickname. Equivalent to Java's UUID.nameUUIDFromBytes("OfflinePlayer:<nick>".getBytes()).
import { computeOfflineUUID } from "skinsrestorer-js";
computeOfflineUUID("S0yoneyro");
// '630e75a3-3c01-3ea8-aabe-a02ab0b10a30'parseTextureValue(base64)
Decodes a base64 Mojang texture property into a structured TextureData object.
import { parseTextureValue } from "skinsrestorer-js";
const data = parseTextureValue(skin.raw.value);
// { timestamp, profileId, profileName, textures: { SKIN?: { url, metadata? }, CAPE?: { url } } }🔍 Skin resolution flow
getByName(nickname)
│
├─ online mode → looks up Mojang UUID from sr_cache
├─ offline mode → computes offline UUID + checks sr_cache as fallback
│
└─ getByUUID(uuid)
│
├─ sr_players row not found → fallback to sr_player_skins by UUID
│
└─ sr_players row found
│
├─ skin_identifier is NULL → sr_player_skins by UUID
│
└─ based on skin_type:
├─ PLAYER → sr_player_skins (by referenced player UUID)
├─ CUSTOM → sr_custom_skins (by name)
└─ URL → sr_url_skins (by URL)🗄️ Database tables
The library reads the following SkinsRestorer tables (prefix is configurable):
| Table | Description |
| ---------------------- | --------------------------------------------------------------- |
| sr_players | Player → skin assignment (UUID, skin type, identifier, variant) |
| sr_player_skins | Cached Mojang texture data per player UUID |
| sr_custom_skins | Admin-uploaded custom skins |
| sr_url_skins | Skins fetched from URLs (via MineSkin) |
| sr_cache | Nickname → Mojang UUID cache |
| sr_player_history | Skin change history per player |
| sr_player_favourites | Player's favourite skins |
Note: This library is read-only — it does not modify any data in the database.
📌 Requirements
- Node.js 18+ (uses global
fetch) - MySQL (
mysql2) or PostgreSQL (pg) driver installed
