IDEncoder 0.4.0
See the version list below for details.
dotnet add package IDEncoder --version 0.4.0
NuGet\Install-Package IDEncoder -Version 0.4.0
<PackageReference Include="IDEncoder" Version="0.4.0" />
<PackageVersion Include="IDEncoder" Version="0.4.0" />
<PackageReference Include="IDEncoder" />
paket add IDEncoder --version 0.4.0
#r "nuget: IDEncoder, 0.4.0"
#:package IDEncoder@0.4.0
#addin nuget:?package=IDEncoder&version=0.4.0
#tool nuget:?package=IDEncoder&version=0.4.0
IDEncoder
A .NET library that encodes numeric IDs (long) into short, URL-safe Base62 strings — similar to how YouTube, Twitter and other services expose public IDs like dQw4w9WgXcQ instead of raw database numbers.
Uses Blowfish encryption under the hood with no external cryptography dependencies — the cipher is implemented from scratch.
Database: 42
URL: /video/xK9mQ3bPl2a
JSON: { "id": "xK9mQ3bPl2a", "title": "..." }
Why
- Hide sequential IDs — users can't guess or enumerate resource URLs
- Short output — fixed 11 characters with the default Blowfish codec (vs 20 digits for
long.MaxValue); 2–12 characters with the Sqids-style codec - Reversible — decode back to the original number with the same key
- Salted encoding — same ID produces different strings for different entity types
- Zero external crypto dependencies — ships its own Blowfish implementation
Install
dotnet add package IDEncoder
Quick start
var encoder = new IDEncoder.IDEncoder("my-secret-key");
string encoded = encoder.Encode(42); // "xK9mQ3bPl2a"
long decoded = encoder.Decode(encoded); // 42
Salt
By default, the same numeric ID always encodes to the same string. This means a user who knows a video ID could try it as a gallery ID, a user ID, etc.
Salt solves this — the same number produces different strings for different entity types:
encoder.Encode(42); // "xK9mQ3bPl2a"
encoder.Encode(42, "video"); // "t7RqN1cWm5z"
encoder.Encode(42, "gallery"); // "p3KmW8vLn9a"
// Decoding requires the same salt
encoder.Decode("t7RqN1cWm5z", "video"); // 42
encoder.Decode("p3KmW8vLn9a", "gallery"); // 42
Salted ciphers are cached internally — no performance penalty for reusing the same salt.
Codecs
IDEncoder is a facade over an IIdCodec. Two are built in:
BlowfishIdCodec(default) — Blowfish encryption, fixed 11-char output, hides sequential IDs.new IDEncoder("secret")uses this.SqidsIdCodec— simple, non-secure, Sqids-style: short variable-length strings where sequential IDs look dissimilar, with no secret key. Obfuscation only, not cryptography.
services.AddIDEncoder(new SqidsIdCodec()); // simple codec
services.AddIDEncoder(new BlowfishIdCodec("key")); // explicit Blowfish
Custom codecs
Implement IIdCodec, or derive from IdCodec to get salt caching for free, and register it:
public sealed class MyCodec : IdCodec {
public override int MaxEncodedLength => 16;
public override string Encode(long id) => /* ... */;
public override int Encode(long id, Span<char> destination) => /* ... */;
public override long Decode(ReadOnlySpan<char> encoded) => /* ... */;
protected override IIdCodec CreateWithSalt(string salt) => /* salted variation from base */;
}
services.AddIDEncoder(new MyCodec());
Codec output is not self-describing. Don't switch codecs over data that already has encoded IDs in the wild — a string from one codec won't decode correctly with another.
ASP.NET Core integration
Registration
// Key known at startup
services.AddIDEncoder("my-secret-key");
// Key from configuration
services.AddIDEncoder(provider => {
var config = provider.GetRequiredService<IConfiguration>();
return config["IDEncoder:SecretKey"]!;
});
// Key loaded later (e.g. from database after startup)
services.AddIDEncoderProvider();
For deferred initialization, call Configure when the key becomes available:
public class AppInitializer(IDEncoderProvider encoderProvider, IMyConfigService config) {
public async Task InitializeAsync() {
string secret = await config.GetSecretAsync("ID_ENCODER_SECRET");
encoderProvider.Configure(secret);
}
}
EncodedId struct
A long wrapper that automatically encodes/decodes at JSON and route-binding boundaries:
// DTO — JSON output is a Base62 string
public record ChatResult(EncodedId Id, string Title);
// Mapping — implicit conversion from long
return new ChatResult(Id: chat.Id, Title: chat.Title);
// { "id": "xK9mQ3bPl2a", "title": "My chat" }
// Controller — parsed from URL automatically via IParsable
[HttpGet("{id}")]
public async Task<IActionResult> Get(EncodedId id) {
long dbId = id; // implicit conversion to long
var chat = await db.Chats.FindAsync(dbId);
return Ok(new ChatResult(chat.Id, chat.Title));
}
The database stores a plain long. Encoding only happens at the JSON/URL boundary.
Salt via attribute
Use [Salt("...")] on properties to automatically apply per-entity-type salt during JSON serialization:
public record VideoResult(
[property: Salt("video")] EncodedId Id,
string Title
);
public record GalleryResult(
[property: Salt("gallery")] EncodedId Id,
string Title
);
// Same DB id = 42, but different JSON output:
// VideoResult: { "id": "t7RqN1cWm5z", "title": "..." }
// GalleryResult: { "id": "p3KmW8vLn9a", "title": "..." }
Enable salt support in JSON and route binding:
services.AddControllers(o => o.UseIDEncoderModelBinding())
.AddJsonOptions(o => o.JsonSerializerOptions.UseIDEncoderSalts());
Salt also works on controller parameters for route/query binding:
[HttpPost("{id}/test")]
public async Task<IActionResult> TestConnection([Salt("s3")] EncodedId id) {
long dbId = id; // correctly decoded with "s3" salt
}
JSON behavior
| Input | Output |
|---|---|
Write(42) |
"xK9mQ3bPl2a" |
Read("xK9mQ3bPl2a") |
42 |
Read(42) |
42 (raw numbers accepted too) |
Zero-alloc API
For high-throughput scenarios, span-based overloads avoid heap allocations:
// Encode into a stack buffer sized for the active codec
Span<char> buffer = stackalloc char[encoder.MaxEncodedLength];
int n = encoder.Encode(42, buffer);
// Decode from the written slice (works for fixed- and variable-length codecs)
long id = encoder.Decode(buffer[..n]);
Upgrading from 0.3.x
The zero-alloc Encode(long, Span<char>, …) overload now returns int (the number of
characters written) instead of void. Source-compatible if you ignore the result, but a
binary-breaking change — recompile against 0.4.0. Use the returned count to slice
variable-length output before decoding: encoder.Decode(buffer[..n]).
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- No dependencies.
NuGet packages (1)
Showing the top 1 NuGet packages that depend on IDEncoder:
| Package | Downloads |
|---|---|
|
IDEncoder.AspNetCore
ASP.NET Core integration for IDEncoder — EncodedId model binding with salt support and MVC JSON wiring |
GitHub repositories
This package is not used by any popular GitHub repositories.