IDEncoder 0.3.0

dotnet add package IDEncoder --version 0.3.0
                    
NuGet\Install-Package IDEncoder -Version 0.3.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="IDEncoder" Version="0.3.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="IDEncoder" Version="0.3.0" />
                    
Directory.Packages.props
<PackageReference Include="IDEncoder" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add IDEncoder --version 0.3.0
                    
#r "nuget: IDEncoder, 0.3.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package IDEncoder@0.3.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=IDEncoder&version=0.3.0
                    
Install as a Cake Addin
#tool nuget:?package=IDEncoder&version=0.3.0
                    
Install as a Cake Tool

IDEncoder

NuGet License

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 — always exactly 11 characters (vs 20 digits for long.MaxValue)
  • 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.

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
Span<char> buffer = stackalloc char[IDEncoder.IDEncoder.EncodedLength];
encoder.Encode(42, buffer);

// Decode from a span (no string needed)
long id = encoder.Decode(buffer);

License

MIT

Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.3.0 88 3/18/2026
0.2.0 99 3/17/2026
0.1.1 108 3/17/2026