Latest commit

History

143 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Build StatusMaven CentralCoverage Status

FriendlyID (Java, Swift, Rust, Go)

What is the FriendlyID library?

The FriendlyID library converts a given UUID (with 36 characters) to a URL-friendly ID (a "FriendlyID") which is based on Base62 (with a maximum of 22 characters), as in the example below:

UUID Friendly ID
c3587ec5-0976-497f-8374-61e0c2ea3da5 -> 5wbwf6yUxVBcr48AMbz9cb
| | 36 characters 22 characters or less

In addition, this library allows to:

  • convert from a FriendlyID back to the original UUID; and
  • create a new, random FriendlyID

Why use a FriendlyID?

Universal Unique IDs (UUIDs) provide a non-sequential and unique identifier that can be generated separately from the source database. As a result, it is not possible to guess either the previous or next identifier. That's great, but, to achieve this level of security, a UUID is long (128 bits long) and looks ugly (36 alphanumeric characters including four hyphens which are added to make it easier to read the UUID), as in this example: 123e4567-e89b-12d3-a456-426655440000.

Such a format is:

  • difficult to read (especially if it is part of a URL)
  • difficult to remember
  • cannot be copied with just two mouse-clicks (you have to select manually the start and end positions)
  • can easily become broken across lines when it is copied, pasted, edited, or sent.

Our FriendlyID Java library solves these problems by converting a given UUID using Base62 with alphanumeric characters in the range [0-9A-Za-z] into a FriendlyId which consists of a maximum of 22 characters (but in fact often contains fewer characters).

Supported languages

Curently FriendlyId supports Java (this project) and

Tools

There are available CLI converters for many platforms.

Use cases

Basic (returning a user in a database)

Let us assume that a method in the controller for returning users requires the relevant UUID in order to find a given user in a database, as in this example:

@GetMapping("/users/{userId}") publicUsergetUser(@PathVariableUUIDuserId) {
[implementationdeleted]
}

Without using the Friendly ID library, you could access a given user as follows:

curl http://localhost:8080/users/c3587ec5-0976-497f-8374-61e0c2ea3da5

After adding the FriendlyID library, the controller method itself does not change, but you would be able to access a given user using the relevant FriendlyID as follows:

curl http://localhost:8080/users/5wbwf6yUxVBcr48AMbz9cb

In addition, if a given document returned by such a method contains objects of type UUID, those IDs will also be shortened into FriendlyID format.

Advanced (Optimizing testing)

The FriendlyID library makes it possible to define for UUIDs values which are easy to read. By using names instead of hard-to-remember UUIDs, you can write much simpler tests for your code, for example:

@TestpublicvoidshouldGetUser() { mockMvc.perform(get("/users/{userId}", "John")) .andExpect(status().isOk()) .andExpect(content().contentType("application/json")) .andExpect(jsonPath("$.uuid", is("John"))); } 

In the above example, the variable "John" is decoded by the library to the correct UUID, in this case, 00000000-0000-0000-0000-000000a69efb. In this way, you can give a variable in a test class a truly meaningful value and, as a result, an assertion which refers to that variable becomes exceptionally easy to understand in your test program.

FriendlyID library

Dependencies

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Usage

FriendlyIds.createFriendlyId();

This creates a new, random FriendlyID, for example: 5wbwf6yUxVBcr48AMbz9cb

FriendlyIds.toFriendlyId(UUID.fromString("c3587ec5-0976-497f-8374-61e0c2ea3da5"));

This converts a UUID in the form of a string to a FriendlyID, for example: 5wbwf6yUxVBcr48AMbz9cb

FriendlyIds.toUuid("5wbwf6yUxVBcr48AMbz9cb");
// orFriendlyIds.toUuid("c3587ec5-0976-497f-8374-61e0c2ea3da5");

This converts a FriendlyID or UUID string to UUID. Both formats are accepted.

Notes

  • As every UUID is a 128-bit number, a FriendlyID can also store only a 128-bit number.
  • If a FriendlyID has any leading zeros, those leading zeros are ignored - for example, 00cafe is treated as cafe.

Integrations

Spring Boot integration

The FriendlyID library includes a Spring configuration to make it easy to add shorter IDs to an application. With a typical application based on Spring Boot, for your controllers to be able to use FriendlyIDs when communicating with the outside world, just add one new starter dependency as follows:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-spring-boot-starter</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Let us assume that you'll use this sample application:

@SpringBootApplication@RestControllerpublicclassApplication {
publicstaticvoidmain(String[] args) {
SpringApplication.run(Application.class, args);
}
@GetMapping("/bars/{bar}")
publicBargetBar(@PathVariableUUIDbar) {
returnnewBar(UUID.randomUUID());
}
@ValueclassBar {
privatefinalUUIDid;
}
} 

This command: curl http://localhost:8080/bars/5fD1KwsxRcGhBqWNju0jzt

will result in the following output:

{"id":"52OMXhWiAqUWwII0c97Svl"}

In this case, Bar is a POJO class which is converted by Spring MVC to a JSON document. This Bar object has one field of type UUID, and this field is output to the JSON document as a FriendlyID instead of a UUID. Although the application uses the relevant UUID internally, from an external point of view, only the FriendlyID is visible.

Jackson integration

First, add the following Jackson module dependency:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-jackson-datatype</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Then register the FriendlyIdModule module as follows:

ObjectMappermapper = newObjectMapper()
.registerModule(newFriendlyIdModule());

jOOQ integration

The FriendlyID library provides a jOOQ converter for seamless integration with jOOQ's code generation and type-safe queries.

First, add the dependency:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-jooq</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Configure the converter in your jOOQ code generation configuration:

<forcedTypes>
<forcedType>
<userType>com.devskiller.friendly_id.type.FriendlyId</userType>
<converter>com.devskiller.friendly_id.jooq.FriendlyIdConverter</converter>
<includeExpression>.*\.id</includeExpression>
<includeTypes>UUID</includeTypes>
</forcedType>
</forcedTypes>

This automatically converts UUID database columns to FriendlyId value objects in your generated jOOQ records.

JPA integration

The FriendlyID library includes a JPA AttributeConverter for transparent conversion between UUID database columns and FriendlyId value objects.

First, add the dependency:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-jpa</artifactId>
<version>2.0.0-beta3</version>
</dependency>

The converter is automatically applied to all FriendlyId attributes in your entities:

@EntitypublicclassUser {
@IdprivateFriendlyIdid;
privateStringname;
// getters/setters
}

The FriendlyId value object stores UUID internally (16 bytes) and computes the FriendlyId string only when needed, making it more memory-efficient than storing strings.

OpenFeign integration

The FriendlyID library provides automatic encoding/decoding for Spring Cloud OpenFeign clients.

First, add the dependency:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-openfeign</artifactId>
<version>2.0.0-beta3</version>
</dependency>

The integration is automatically configured when Spring Cloud OpenFeign is on the classpath:

@FeignClient(name = "user-service")
publicinterfaceUserClient {
@GetMapping("/users/{id}")
UserDtogetUser(@PathVariableUUIDid); // Sends FriendlyId string@GetMapping("/users/{id}/profile")
ProfileDtogetProfile(@PathVariableFriendlyIdid); // Also works with FriendlyId value object
}

UUID and FriendlyId parameters are automatically converted to FriendlyId strings in requests, and FriendlyId strings in responses are converted back to UUID or FriendlyId objects.

Migration Guide

Migrating from 1.x to 2.x

Version 2.0 introduces several breaking changes to support Spring Boot 4 and Jackson 3.

Requirements

VersionJavaSpring BootJackson
1.x8+2.x, 3.x2.x
2.x21+4.x3.x

For Spring Boot 4 + Jackson 3 projects

Update dependencies to use the new version:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-spring-boot-starter</artifactId>
<version>2.0.0-beta3</version>
</dependency>

For Spring Boot 3 + Jackson 2 projects

Use the new Jackson 2.x module:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-jackson2-datatype</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Register the module:

ObjectMappermapper = newObjectMapper()
.registerModule(newFriendlyIdJackson2Module());

Utility class renamed

The utility class has been renamed from FriendlyId to FriendlyIds (plural) following Java conventions:

// Before (1.x)importcom.devskiller.friendly_id.FriendlyId;
FriendlyId.toUuid("5wbwf6yUxVBcr48AMbz9cb");
// After (2.x)importcom.devskiller.friendly_id.FriendlyIds;
FriendlyIds.toUuid("5wbwf6yUxVBcr48AMbz9cb");

The old FriendlyId class is deprecated and will be removed in a future version.

@IdFormat annotation

The @IdFormat annotation has been moved from the Jackson module to the core module:

// Before (1.x)importcom.devskiller.friendly_id.jackson.IdFormat;
importcom.devskiller.friendly_id.jackson.FriendlyIdFormat;
// After (2.x)importcom.devskiller.friendly_id.IdFormat;
importcom.devskiller.friendly_id.FriendlyIdFormat;

This change allows using the same annotation with both Jackson 2.x and Jackson 3.x modules.

FriendlyId value object

Version 2.x introduces the FriendlyId value object type for type-safe ID handling:

importcom.devskiller.friendly_id.type.FriendlyId;
// Create from UUIDFriendlyIdid = FriendlyId.of(uuid);
// Or use static import friendly methodimportstaticcom.devskiller.friendly_id.type.FriendlyId.friendlyId;
FriendlyIdid = friendlyId(uuid);
// Parse from string (accepts both FriendlyId and UUID formats)FriendlyIdid = FriendlyId.parse("5wbwf6yUxVBcr48AMbz9cb");
FriendlyIdid = FriendlyId.parse("c3587ec5-0976-497f-8374-61e0c2ea3da5");
// Create randomFriendlyIdid = FriendlyId.random();
// Get UUIDUUIDuuid = id.toUuid();
// Get string representationStringfriendlyIdString = id.value(); // Returns FriendlyId stringStringfriendlyIdString = id.toString(); // Same as value()

The value object can be used in:

  • @PathVariable FriendlyId id
  • @RequestParam FriendlyId id
  • @RequestBody with JSON fields
  • JPA entities
  • jOOQ records

Null-safety with JSpecify

Version 2.x uses JSpecify annotations for null-safety. All packages are marked with @NullMarked, meaning parameters and return values are non-null by default.

Jackson module names

Jackson VersionModule ClassArtifact
Jackson 3.xFriendlyIdModulefriendly-id-jackson-datatype
Jackson 2.xFriendlyIdJackson2Modulefriendly-id-jackson2-datatype

Contributing

Thinking of helping us out? We invite you to take a look at:

License

The project is licensed under the Apache 2.0 license. For further details, please see the License page.

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Latest commit

History

143 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Build StatusMaven CentralCoverage Status

FriendlyID (Java, Swift, Rust, Go)

What is the FriendlyID library?

The FriendlyID library converts a given UUID (with 36 characters) to a URL-friendly ID (a "FriendlyID") which is based on Base62 (with a maximum of 22 characters), as in the example below:

UUID Friendly ID
c3587ec5-0976-497f-8374-61e0c2ea3da5 -> 5wbwf6yUxVBcr48AMbz9cb
| | 36 characters 22 characters or less

In addition, this library allows to:

  • convert from a FriendlyID back to the original UUID; and
  • create a new, random FriendlyID

Why use a FriendlyID?

Universal Unique IDs (UUIDs) provide a non-sequential and unique identifier that can be generated separately from the source database. As a result, it is not possible to guess either the previous or next identifier. That's great, but, to achieve this level of security, a UUID is long (128 bits long) and looks ugly (36 alphanumeric characters including four hyphens which are added to make it easier to read the UUID), as in this example: 123e4567-e89b-12d3-a456-426655440000.

Such a format is:

  • difficult to read (especially if it is part of a URL)
  • difficult to remember
  • cannot be copied with just two mouse-clicks (you have to select manually the start and end positions)
  • can easily become broken across lines when it is copied, pasted, edited, or sent.

Our FriendlyID Java library solves these problems by converting a given UUID using Base62 with alphanumeric characters in the range [0-9A-Za-z] into a FriendlyId which consists of a maximum of 22 characters (but in fact often contains fewer characters).

Supported languages

Curently FriendlyId supports Java (this project) and

Tools

There are available CLI converters for many platforms.

Use cases

Basic (returning a user in a database)

Let us assume that a method in the controller for returning users requires the relevant UUID in order to find a given user in a database, as in this example:

@GetMapping("/users/{userId}") publicUsergetUser(@PathVariableUUIDuserId) {
[implementationdeleted]
}

Without using the Friendly ID library, you could access a given user as follows:

curl http://localhost:8080/users/c3587ec5-0976-497f-8374-61e0c2ea3da5

After adding the FriendlyID library, the controller method itself does not change, but you would be able to access a given user using the relevant FriendlyID as follows:

curl http://localhost:8080/users/5wbwf6yUxVBcr48AMbz9cb

In addition, if a given document returned by such a method contains objects of type UUID, those IDs will also be shortened into FriendlyID format.

Advanced (Optimizing testing)

The FriendlyID library makes it possible to define for UUIDs values which are easy to read. By using names instead of hard-to-remember UUIDs, you can write much simpler tests for your code, for example:

@TestpublicvoidshouldGetUser() { mockMvc.perform(get("/users/{userId}", "John")) .andExpect(status().isOk()) .andExpect(content().contentType("application/json")) .andExpect(jsonPath("$.uuid", is("John"))); } 

In the above example, the variable "John" is decoded by the library to the correct UUID, in this case, 00000000-0000-0000-0000-000000a69efb. In this way, you can give a variable in a test class a truly meaningful value and, as a result, an assertion which refers to that variable becomes exceptionally easy to understand in your test program.

FriendlyID library

Dependencies

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Usage

FriendlyIds.createFriendlyId();

This creates a new, random FriendlyID, for example: 5wbwf6yUxVBcr48AMbz9cb

FriendlyIds.toFriendlyId(UUID.fromString("c3587ec5-0976-497f-8374-61e0c2ea3da5"));

This converts a UUID in the form of a string to a FriendlyID, for example: 5wbwf6yUxVBcr48AMbz9cb

FriendlyIds.toUuid("5wbwf6yUxVBcr48AMbz9cb");
// orFriendlyIds.toUuid("c3587ec5-0976-497f-8374-61e0c2ea3da5");

This converts a FriendlyID or UUID string to UUID. Both formats are accepted.

Notes

  • As every UUID is a 128-bit number, a FriendlyID can also store only a 128-bit number.
  • If a FriendlyID has any leading zeros, those leading zeros are ignored - for example, 00cafe is treated as cafe.

Integrations

Spring Boot integration

The FriendlyID library includes a Spring configuration to make it easy to add shorter IDs to an application. With a typical application based on Spring Boot, for your controllers to be able to use FriendlyIDs when communicating with the outside world, just add one new starter dependency as follows:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-spring-boot-starter</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Let us assume that you'll use this sample application:

@SpringBootApplication@RestControllerpublicclassApplication {
publicstaticvoidmain(String[] args) {
SpringApplication.run(Application.class, args);
}
@GetMapping("/bars/{bar}")
publicBargetBar(@PathVariableUUIDbar) {
returnnewBar(UUID.randomUUID());
}
@ValueclassBar {
privatefinalUUIDid;
}
} 

This command: curl http://localhost:8080/bars/5fD1KwsxRcGhBqWNju0jzt

will result in the following output:

{"id":"52OMXhWiAqUWwII0c97Svl"}

In this case, Bar is a POJO class which is converted by Spring MVC to a JSON document. This Bar object has one field of type UUID, and this field is output to the JSON document as a FriendlyID instead of a UUID. Although the application uses the relevant UUID internally, from an external point of view, only the FriendlyID is visible.

Jackson integration

First, add the following Jackson module dependency:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-jackson-datatype</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Then register the FriendlyIdModule module as follows:

ObjectMappermapper = newObjectMapper()
.registerModule(newFriendlyIdModule());

jOOQ integration

The FriendlyID library provides a jOOQ converter for seamless integration with jOOQ's code generation and type-safe queries.

First, add the dependency:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-jooq</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Configure the converter in your jOOQ code generation configuration:

<forcedTypes>
<forcedType>
<userType>com.devskiller.friendly_id.type.FriendlyId</userType>
<converter>com.devskiller.friendly_id.jooq.FriendlyIdConverter</converter>
<includeExpression>.*\.id</includeExpression>
<includeTypes>UUID</includeTypes>
</forcedType>
</forcedTypes>

This automatically converts UUID database columns to FriendlyId value objects in your generated jOOQ records.

JPA integration

The FriendlyID library includes a JPA AttributeConverter for transparent conversion between UUID database columns and FriendlyId value objects.

First, add the dependency:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-jpa</artifactId>
<version>2.0.0-beta3</version>
</dependency>

The converter is automatically applied to all FriendlyId attributes in your entities:

@EntitypublicclassUser {
@IdprivateFriendlyIdid;
privateStringname;
// getters/setters
}

The FriendlyId value object stores UUID internally (16 bytes) and computes the FriendlyId string only when needed, making it more memory-efficient than storing strings.

OpenFeign integration

The FriendlyID library provides automatic encoding/decoding for Spring Cloud OpenFeign clients.

First, add the dependency:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-openfeign</artifactId>
<version>2.0.0-beta3</version>
</dependency>

The integration is automatically configured when Spring Cloud OpenFeign is on the classpath:

@FeignClient(name = "user-service")
publicinterfaceUserClient {
@GetMapping("/users/{id}")
UserDtogetUser(@PathVariableUUIDid); // Sends FriendlyId string@GetMapping("/users/{id}/profile")
ProfileDtogetProfile(@PathVariableFriendlyIdid); // Also works with FriendlyId value object
}

UUID and FriendlyId parameters are automatically converted to FriendlyId strings in requests, and FriendlyId strings in responses are converted back to UUID or FriendlyId objects.

Migration Guide

Migrating from 1.x to 2.x

Version 2.0 introduces several breaking changes to support Spring Boot 4 and Jackson 3.

Requirements

VersionJavaSpring BootJackson
1.x8+2.x, 3.x2.x
2.x21+4.x3.x

For Spring Boot 4 + Jackson 3 projects

Update dependencies to use the new version:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-spring-boot-starter</artifactId>
<version>2.0.0-beta3</version>
</dependency>

For Spring Boot 3 + Jackson 2 projects

Use the new Jackson 2.x module:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-jackson2-datatype</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Register the module:

ObjectMappermapper = newObjectMapper()
.registerModule(newFriendlyIdJackson2Module());

Utility class renamed

The utility class has been renamed from FriendlyId to FriendlyIds (plural) following Java conventions:

// Before (1.x)importcom.devskiller.friendly_id.FriendlyId;
FriendlyId.toUuid("5wbwf6yUxVBcr48AMbz9cb");
// After (2.x)importcom.devskiller.friendly_id.FriendlyIds;
FriendlyIds.toUuid("5wbwf6yUxVBcr48AMbz9cb");

The old FriendlyId class is deprecated and will be removed in a future version.

@IdFormat annotation

The @IdFormat annotation has been moved from the Jackson module to the core module:

// Before (1.x)importcom.devskiller.friendly_id.jackson.IdFormat;
importcom.devskiller.friendly_id.jackson.FriendlyIdFormat;
// After (2.x)importcom.devskiller.friendly_id.IdFormat;
importcom.devskiller.friendly_id.FriendlyIdFormat;

This change allows using the same annotation with both Jackson 2.x and Jackson 3.x modules.

FriendlyId value object

Version 2.x introduces the FriendlyId value object type for type-safe ID handling:

importcom.devskiller.friendly_id.type.FriendlyId;
// Create from UUIDFriendlyIdid = FriendlyId.of(uuid);
// Or use static import friendly methodimportstaticcom.devskiller.friendly_id.type.FriendlyId.friendlyId;
FriendlyIdid = friendlyId(uuid);
// Parse from string (accepts both FriendlyId and UUID formats)FriendlyIdid = FriendlyId.parse("5wbwf6yUxVBcr48AMbz9cb");
FriendlyIdid = FriendlyId.parse("c3587ec5-0976-497f-8374-61e0c2ea3da5");
// Create randomFriendlyIdid = FriendlyId.random();
// Get UUIDUUIDuuid = id.toUuid();
// Get string representationStringfriendlyIdString = id.value(); // Returns FriendlyId stringStringfriendlyIdString = id.toString(); // Same as value()

The value object can be used in:

  • @PathVariable FriendlyId id
  • @RequestParam FriendlyId id
  • @RequestBody with JSON fields
  • JPA entities
  • jOOQ records

Null-safety with JSpecify

Version 2.x uses JSpecify annotations for null-safety. All packages are marked with @NullMarked, meaning parameters and return values are non-null by default.

Jackson module names

Jackson VersionModule ClassArtifact
Jackson 3.xFriendlyIdModulefriendly-id-jackson-datatype
Jackson 2.xFriendlyIdJackson2Modulefriendly-id-jackson2-datatype

Contributing

Thinking of helping us out? We invite you to take a look at:

License

The project is licensed under the Apache 2.0 license. For further details, please see the License page.

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

143 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Build StatusMaven CentralCoverage Status

FriendlyID (Java, Swift, Rust, Go)

What is the FriendlyID library?

The FriendlyID library converts a given UUID (with 36 characters) to a URL-friendly ID (a "FriendlyID") which is based on Base62 (with a maximum of 22 characters), as in the example below:

UUID Friendly ID
c3587ec5-0976-497f-8374-61e0c2ea3da5 -> 5wbwf6yUxVBcr48AMbz9cb
| | 36 characters 22 characters or less

In addition, this library allows to:

  • convert from a FriendlyID back to the original UUID; and
  • create a new, random FriendlyID

Why use a FriendlyID?

Universal Unique IDs (UUIDs) provide a non-sequential and unique identifier that can be generated separately from the source database. As a result, it is not possible to guess either the previous or next identifier. That's great, but, to achieve this level of security, a UUID is long (128 bits long) and looks ugly (36 alphanumeric characters including four hyphens which are added to make it easier to read the UUID), as in this example: 123e4567-e89b-12d3-a456-426655440000.

Such a format is:

  • difficult to read (especially if it is part of a URL)
  • difficult to remember
  • cannot be copied with just two mouse-clicks (you have to select manually the start and end positions)
  • can easily become broken across lines when it is copied, pasted, edited, or sent.

Our FriendlyID Java library solves these problems by converting a given UUID using Base62 with alphanumeric characters in the range [0-9A-Za-z] into a FriendlyId which consists of a maximum of 22 characters (but in fact often contains fewer characters).

Supported languages

Curently FriendlyId supports Java (this project) and

Tools

There are available CLI converters for many platforms.

Use cases

Basic (returning a user in a database)

Let us assume that a method in the controller for returning users requires the relevant UUID in order to find a given user in a database, as in this example:

@GetMapping("/users/{userId}") publicUsergetUser(@PathVariableUUIDuserId) {
[implementationdeleted]
}

Without using the Friendly ID library, you could access a given user as follows:

curl http://localhost:8080/users/c3587ec5-0976-497f-8374-61e0c2ea3da5

After adding the FriendlyID library, the controller method itself does not change, but you would be able to access a given user using the relevant FriendlyID as follows:

curl http://localhost:8080/users/5wbwf6yUxVBcr48AMbz9cb

In addition, if a given document returned by such a method contains objects of type UUID, those IDs will also be shortened into FriendlyID format.

Advanced (Optimizing testing)

The FriendlyID library makes it possible to define for UUIDs values which are easy to read. By using names instead of hard-to-remember UUIDs, you can write much simpler tests for your code, for example:

@TestpublicvoidshouldGetUser() { mockMvc.perform(get("/users/{userId}", "John")) .andExpect(status().isOk()) .andExpect(content().contentType("application/json")) .andExpect(jsonPath("$.uuid", is("John"))); } 

In the above example, the variable "John" is decoded by the library to the correct UUID, in this case, 00000000-0000-0000-0000-000000a69efb. In this way, you can give a variable in a test class a truly meaningful value and, as a result, an assertion which refers to that variable becomes exceptionally easy to understand in your test program.

FriendlyID library

Dependencies

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Usage

FriendlyIds.createFriendlyId();

This creates a new, random FriendlyID, for example: 5wbwf6yUxVBcr48AMbz9cb

FriendlyIds.toFriendlyId(UUID.fromString("c3587ec5-0976-497f-8374-61e0c2ea3da5"));

This converts a UUID in the form of a string to a FriendlyID, for example: 5wbwf6yUxVBcr48AMbz9cb

FriendlyIds.toUuid("5wbwf6yUxVBcr48AMbz9cb");
// orFriendlyIds.toUuid("c3587ec5-0976-497f-8374-61e0c2ea3da5");

This converts a FriendlyID or UUID string to UUID. Both formats are accepted.

Notes

  • As every UUID is a 128-bit number, a FriendlyID can also store only a 128-bit number.
  • If a FriendlyID has any leading zeros, those leading zeros are ignored - for example, 00cafe is treated as cafe.

Integrations

Spring Boot integration

The FriendlyID library includes a Spring configuration to make it easy to add shorter IDs to an application. With a typical application based on Spring Boot, for your controllers to be able to use FriendlyIDs when communicating with the outside world, just add one new starter dependency as follows:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-spring-boot-starter</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Let us assume that you'll use this sample application:

@SpringBootApplication@RestControllerpublicclassApplication {
publicstaticvoidmain(String[] args) {
SpringApplication.run(Application.class, args);
}
@GetMapping("/bars/{bar}")
publicBargetBar(@PathVariableUUIDbar) {
returnnewBar(UUID.randomUUID());
}
@ValueclassBar {
privatefinalUUIDid;
}
} 

This command: curl http://localhost:8080/bars/5fD1KwsxRcGhBqWNju0jzt

will result in the following output:

{"id":"52OMXhWiAqUWwII0c97Svl"}

In this case, Bar is a POJO class which is converted by Spring MVC to a JSON document. This Bar object has one field of type UUID, and this field is output to the JSON document as a FriendlyID instead of a UUID. Although the application uses the relevant UUID internally, from an external point of view, only the FriendlyID is visible.

Jackson integration

First, add the following Jackson module dependency:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-jackson-datatype</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Then register the FriendlyIdModule module as follows:

ObjectMappermapper = newObjectMapper()
.registerModule(newFriendlyIdModule());

jOOQ integration

The FriendlyID library provides a jOOQ converter for seamless integration with jOOQ's code generation and type-safe queries.

First, add the dependency:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-jooq</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Configure the converter in your jOOQ code generation configuration:

<forcedTypes>
<forcedType>
<userType>com.devskiller.friendly_id.type.FriendlyId</userType>
<converter>com.devskiller.friendly_id.jooq.FriendlyIdConverter</converter>
<includeExpression>.*\.id</includeExpression>
<includeTypes>UUID</includeTypes>
</forcedType>
</forcedTypes>

This automatically converts UUID database columns to FriendlyId value objects in your generated jOOQ records.

JPA integration

The FriendlyID library includes a JPA AttributeConverter for transparent conversion between UUID database columns and FriendlyId value objects.

First, add the dependency:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-jpa</artifactId>
<version>2.0.0-beta3</version>
</dependency>

The converter is automatically applied to all FriendlyId attributes in your entities:

@EntitypublicclassUser {
@IdprivateFriendlyIdid;
privateStringname;
// getters/setters
}

The FriendlyId value object stores UUID internally (16 bytes) and computes the FriendlyId string only when needed, making it more memory-efficient than storing strings.

OpenFeign integration

The FriendlyID library provides automatic encoding/decoding for Spring Cloud OpenFeign clients.

First, add the dependency:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-openfeign</artifactId>
<version>2.0.0-beta3</version>
</dependency>

The integration is automatically configured when Spring Cloud OpenFeign is on the classpath:

@FeignClient(name = "user-service")
publicinterfaceUserClient {
@GetMapping("/users/{id}")
UserDtogetUser(@PathVariableUUIDid); // Sends FriendlyId string@GetMapping("/users/{id}/profile")
ProfileDtogetProfile(@PathVariableFriendlyIdid); // Also works with FriendlyId value object
}

UUID and FriendlyId parameters are automatically converted to FriendlyId strings in requests, and FriendlyId strings in responses are converted back to UUID or FriendlyId objects.

Migration Guide

Migrating from 1.x to 2.x

Version 2.0 introduces several breaking changes to support Spring Boot 4 and Jackson 3.

Requirements

VersionJavaSpring BootJackson
1.x8+2.x, 3.x2.x
2.x21+4.x3.x

For Spring Boot 4 + Jackson 3 projects

Update dependencies to use the new version:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-spring-boot-starter</artifactId>
<version>2.0.0-beta3</version>
</dependency>

For Spring Boot 3 + Jackson 2 projects

Use the new Jackson 2.x module:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-jackson2-datatype</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Register the module:

ObjectMappermapper = newObjectMapper()
.registerModule(newFriendlyIdJackson2Module());

Utility class renamed

The utility class has been renamed from FriendlyId to FriendlyIds (plural) following Java conventions:

// Before (1.x)importcom.devskiller.friendly_id.FriendlyId;
FriendlyId.toUuid("5wbwf6yUxVBcr48AMbz9cb");
// After (2.x)importcom.devskiller.friendly_id.FriendlyIds;
FriendlyIds.toUuid("5wbwf6yUxVBcr48AMbz9cb");

The old FriendlyId class is deprecated and will be removed in a future version.

@IdFormat annotation

The @IdFormat annotation has been moved from the Jackson module to the core module:

// Before (1.x)importcom.devskiller.friendly_id.jackson.IdFormat;
importcom.devskiller.friendly_id.jackson.FriendlyIdFormat;
// After (2.x)importcom.devskiller.friendly_id.IdFormat;
importcom.devskiller.friendly_id.FriendlyIdFormat;

This change allows using the same annotation with both Jackson 2.x and Jackson 3.x modules.

FriendlyId value object

Version 2.x introduces the FriendlyId value object type for type-safe ID handling:

importcom.devskiller.friendly_id.type.FriendlyId;
// Create from UUIDFriendlyIdid = FriendlyId.of(uuid);
// Or use static import friendly methodimportstaticcom.devskiller.friendly_id.type.FriendlyId.friendlyId;
FriendlyIdid = friendlyId(uuid);
// Parse from string (accepts both FriendlyId and UUID formats)FriendlyIdid = FriendlyId.parse("5wbwf6yUxVBcr48AMbz9cb");
FriendlyIdid = FriendlyId.parse("c3587ec5-0976-497f-8374-61e0c2ea3da5");
// Create randomFriendlyIdid = FriendlyId.random();
// Get UUIDUUIDuuid = id.toUuid();
// Get string representationStringfriendlyIdString = id.value(); // Returns FriendlyId stringStringfriendlyIdString = id.toString(); // Same as value()

The value object can be used in:

  • @PathVariable FriendlyId id
  • @RequestParam FriendlyId id
  • @RequestBody with JSON fields
  • JPA entities
  • jOOQ records

Null-safety with JSpecify

Version 2.x uses JSpecify annotations for null-safety. All packages are marked with @NullMarked, meaning parameters and return values are non-null by default.

Jackson module names

Jackson VersionModule ClassArtifact
Jackson 3.xFriendlyIdModulefriendly-id-jackson-datatype
Jackson 2.xFriendlyIdJackson2Modulefriendly-id-jackson2-datatype

Contributing

Thinking of helping us out? We invite you to take a look at:

License

The project is licensed under the Apache 2.0 license. For further details, please see the License page.

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

143 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Build StatusMaven CentralCoverage Status

FriendlyID (Java, Swift, Rust, Go)

What is the FriendlyID library?

The FriendlyID library converts a given UUID (with 36 characters) to a URL-friendly ID (a "FriendlyID") which is based on Base62 (with a maximum of 22 characters), as in the example below:

UUID Friendly ID
c3587ec5-0976-497f-8374-61e0c2ea3da5 -> 5wbwf6yUxVBcr48AMbz9cb
| | 36 characters 22 characters or less

In addition, this library allows to:

  • convert from a FriendlyID back to the original UUID; and
  • create a new, random FriendlyID

Why use a FriendlyID?

Universal Unique IDs (UUIDs) provide a non-sequential and unique identifier that can be generated separately from the source database. As a result, it is not possible to guess either the previous or next identifier. That's great, but, to achieve this level of security, a UUID is long (128 bits long) and looks ugly (36 alphanumeric characters including four hyphens which are added to make it easier to read the UUID), as in this example: 123e4567-e89b-12d3-a456-426655440000.

Such a format is:

  • difficult to read (especially if it is part of a URL)
  • difficult to remember
  • cannot be copied with just two mouse-clicks (you have to select manually the start and end positions)
  • can easily become broken across lines when it is copied, pasted, edited, or sent.

Our FriendlyID Java library solves these problems by converting a given UUID using Base62 with alphanumeric characters in the range [0-9A-Za-z] into a FriendlyId which consists of a maximum of 22 characters (but in fact often contains fewer characters).

Supported languages

Curently FriendlyId supports Java (this project) and

Tools

There are available CLI converters for many platforms.

Use cases

Basic (returning a user in a database)

Let us assume that a method in the controller for returning users requires the relevant UUID in order to find a given user in a database, as in this example:

@GetMapping("/users/{userId}") publicUsergetUser(@PathVariableUUIDuserId) {
[implementationdeleted]
}

Without using the Friendly ID library, you could access a given user as follows:

curl http://localhost:8080/users/c3587ec5-0976-497f-8374-61e0c2ea3da5

After adding the FriendlyID library, the controller method itself does not change, but you would be able to access a given user using the relevant FriendlyID as follows:

curl http://localhost:8080/users/5wbwf6yUxVBcr48AMbz9cb

In addition, if a given document returned by such a method contains objects of type UUID, those IDs will also be shortened into FriendlyID format.

Advanced (Optimizing testing)

The FriendlyID library makes it possible to define for UUIDs values which are easy to read. By using names instead of hard-to-remember UUIDs, you can write much simpler tests for your code, for example:

@TestpublicvoidshouldGetUser() { mockMvc.perform(get("/users/{userId}", "John")) .andExpect(status().isOk()) .andExpect(content().contentType("application/json")) .andExpect(jsonPath("$.uuid", is("John"))); } 

In the above example, the variable "John" is decoded by the library to the correct UUID, in this case, 00000000-0000-0000-0000-000000a69efb. In this way, you can give a variable in a test class a truly meaningful value and, as a result, an assertion which refers to that variable becomes exceptionally easy to understand in your test program.

FriendlyID library

Dependencies

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Usage

FriendlyIds.createFriendlyId();

This creates a new, random FriendlyID, for example: 5wbwf6yUxVBcr48AMbz9cb

FriendlyIds.toFriendlyId(UUID.fromString("c3587ec5-0976-497f-8374-61e0c2ea3da5"));

This converts a UUID in the form of a string to a FriendlyID, for example: 5wbwf6yUxVBcr48AMbz9cb

FriendlyIds.toUuid("5wbwf6yUxVBcr48AMbz9cb");
// orFriendlyIds.toUuid("c3587ec5-0976-497f-8374-61e0c2ea3da5");

This converts a FriendlyID or UUID string to UUID. Both formats are accepted.

Notes

  • As every UUID is a 128-bit number, a FriendlyID can also store only a 128-bit number.
  • If a FriendlyID has any leading zeros, those leading zeros are ignored - for example, 00cafe is treated as cafe.

Integrations

Spring Boot integration

The FriendlyID library includes a Spring configuration to make it easy to add shorter IDs to an application. With a typical application based on Spring Boot, for your controllers to be able to use FriendlyIDs when communicating with the outside world, just add one new starter dependency as follows:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-spring-boot-starter</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Let us assume that you'll use this sample application:

@SpringBootApplication@RestControllerpublicclassApplication {
publicstaticvoidmain(String[] args) {
SpringApplication.run(Application.class, args);
}
@GetMapping("/bars/{bar}")
publicBargetBar(@PathVariableUUIDbar) {
returnnewBar(UUID.randomUUID());
}
@ValueclassBar {
privatefinalUUIDid;
}
} 

This command: curl http://localhost:8080/bars/5fD1KwsxRcGhBqWNju0jzt

will result in the following output:

{"id":"52OMXhWiAqUWwII0c97Svl"}

In this case, Bar is a POJO class which is converted by Spring MVC to a JSON document. This Bar object has one field of type UUID, and this field is output to the JSON document as a FriendlyID instead of a UUID. Although the application uses the relevant UUID internally, from an external point of view, only the FriendlyID is visible.

Jackson integration

First, add the following Jackson module dependency:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-jackson-datatype</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Then register the FriendlyIdModule module as follows:

ObjectMappermapper = newObjectMapper()
.registerModule(newFriendlyIdModule());

jOOQ integration

The FriendlyID library provides a jOOQ converter for seamless integration with jOOQ's code generation and type-safe queries.

First, add the dependency:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-jooq</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Configure the converter in your jOOQ code generation configuration:

<forcedTypes>
<forcedType>
<userType>com.devskiller.friendly_id.type.FriendlyId</userType>
<converter>com.devskiller.friendly_id.jooq.FriendlyIdConverter</converter>
<includeExpression>.*\.id</includeExpression>
<includeTypes>UUID</includeTypes>
</forcedType>
</forcedTypes>

This automatically converts UUID database columns to FriendlyId value objects in your generated jOOQ records.

JPA integration

The FriendlyID library includes a JPA AttributeConverter for transparent conversion between UUID database columns and FriendlyId value objects.

First, add the dependency:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-jpa</artifactId>
<version>2.0.0-beta3</version>
</dependency>

The converter is automatically applied to all FriendlyId attributes in your entities:

@EntitypublicclassUser {
@IdprivateFriendlyIdid;
privateStringname;
// getters/setters
}

The FriendlyId value object stores UUID internally (16 bytes) and computes the FriendlyId string only when needed, making it more memory-efficient than storing strings.

OpenFeign integration

The FriendlyID library provides automatic encoding/decoding for Spring Cloud OpenFeign clients.

First, add the dependency:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-openfeign</artifactId>
<version>2.0.0-beta3</version>
</dependency>

The integration is automatically configured when Spring Cloud OpenFeign is on the classpath:

@FeignClient(name = "user-service")
publicinterfaceUserClient {
@GetMapping("/users/{id}")
UserDtogetUser(@PathVariableUUIDid); // Sends FriendlyId string@GetMapping("/users/{id}/profile")
ProfileDtogetProfile(@PathVariableFriendlyIdid); // Also works with FriendlyId value object
}

UUID and FriendlyId parameters are automatically converted to FriendlyId strings in requests, and FriendlyId strings in responses are converted back to UUID or FriendlyId objects.

Migration Guide

Migrating from 1.x to 2.x

Version 2.0 introduces several breaking changes to support Spring Boot 4 and Jackson 3.

Requirements

VersionJavaSpring BootJackson
1.x8+2.x, 3.x2.x
2.x21+4.x3.x

For Spring Boot 4 + Jackson 3 projects

Update dependencies to use the new version:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-spring-boot-starter</artifactId>
<version>2.0.0-beta3</version>
</dependency>

For Spring Boot 3 + Jackson 2 projects

Use the new Jackson 2.x module:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-jackson2-datatype</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Register the module:

ObjectMappermapper = newObjectMapper()
.registerModule(newFriendlyIdJackson2Module());

Utility class renamed

The utility class has been renamed from FriendlyId to FriendlyIds (plural) following Java conventions:

// Before (1.x)importcom.devskiller.friendly_id.FriendlyId;
FriendlyId.toUuid("5wbwf6yUxVBcr48AMbz9cb");
// After (2.x)importcom.devskiller.friendly_id.FriendlyIds;
FriendlyIds.toUuid("5wbwf6yUxVBcr48AMbz9cb");

The old FriendlyId class is deprecated and will be removed in a future version.

@IdFormat annotation

The @IdFormat annotation has been moved from the Jackson module to the core module:

// Before (1.x)importcom.devskiller.friendly_id.jackson.IdFormat;
importcom.devskiller.friendly_id.jackson.FriendlyIdFormat;
// After (2.x)importcom.devskiller.friendly_id.IdFormat;
importcom.devskiller.friendly_id.FriendlyIdFormat;

This change allows using the same annotation with both Jackson 2.x and Jackson 3.x modules.

FriendlyId value object

Version 2.x introduces the FriendlyId value object type for type-safe ID handling:

importcom.devskiller.friendly_id.type.FriendlyId;
// Create from UUIDFriendlyIdid = FriendlyId.of(uuid);
// Or use static import friendly methodimportstaticcom.devskiller.friendly_id.type.FriendlyId.friendlyId;
FriendlyIdid = friendlyId(uuid);
// Parse from string (accepts both FriendlyId and UUID formats)FriendlyIdid = FriendlyId.parse("5wbwf6yUxVBcr48AMbz9cb");
FriendlyIdid = FriendlyId.parse("c3587ec5-0976-497f-8374-61e0c2ea3da5");
// Create randomFriendlyIdid = FriendlyId.random();
// Get UUIDUUIDuuid = id.toUuid();
// Get string representationStringfriendlyIdString = id.value(); // Returns FriendlyId stringStringfriendlyIdString = id.toString(); // Same as value()

The value object can be used in:

  • @PathVariable FriendlyId id
  • @RequestParam FriendlyId id
  • @RequestBody with JSON fields
  • JPA entities
  • jOOQ records

Null-safety with JSpecify

Version 2.x uses JSpecify annotations for null-safety. All packages are marked with @NullMarked, meaning parameters and return values are non-null by default.

Jackson module names

Jackson VersionModule ClassArtifact
Jackson 3.xFriendlyIdModulefriendly-id-jackson-datatype
Jackson 2.xFriendlyIdJackson2Modulefriendly-id-jackson2-datatype

Contributing

Thinking of helping us out? We invite you to take a look at:

License

The project is licensed under the Apache 2.0 license. For further details, please see the License page.

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Latest commit

History

143 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Build StatusMaven CentralCoverage Status

FriendlyID (Java, Swift, Rust, Go)

What is the FriendlyID library?

The FriendlyID library converts a given UUID (with 36 characters) to a URL-friendly ID (a "FriendlyID") which is based on Base62 (with a maximum of 22 characters), as in the example below:

UUID Friendly ID
c3587ec5-0976-497f-8374-61e0c2ea3da5 -> 5wbwf6yUxVBcr48AMbz9cb
| | 36 characters 22 characters or less

In addition, this library allows to:

  • convert from a FriendlyID back to the original UUID; and
  • create a new, random FriendlyID

Why use a FriendlyID?

Universal Unique IDs (UUIDs) provide a non-sequential and unique identifier that can be generated separately from the source database. As a result, it is not possible to guess either the previous or next identifier. That's great, but, to achieve this level of security, a UUID is long (128 bits long) and looks ugly (36 alphanumeric characters including four hyphens which are added to make it easier to read the UUID), as in this example: 123e4567-e89b-12d3-a456-426655440000.

Such a format is:

  • difficult to read (especially if it is part of a URL)
  • difficult to remember
  • cannot be copied with just two mouse-clicks (you have to select manually the start and end positions)
  • can easily become broken across lines when it is copied, pasted, edited, or sent.

Our FriendlyID Java library solves these problems by converting a given UUID using Base62 with alphanumeric characters in the range [0-9A-Za-z] into a FriendlyId which consists of a maximum of 22 characters (but in fact often contains fewer characters).

Supported languages

Curently FriendlyId supports Java (this project) and

Tools

There are available CLI converters for many platforms.

Use cases

Basic (returning a user in a database)

Let us assume that a method in the controller for returning users requires the relevant UUID in order to find a given user in a database, as in this example:

@GetMapping("/users/{userId}") publicUsergetUser(@PathVariableUUIDuserId) {
[implementationdeleted]
}

Without using the Friendly ID library, you could access a given user as follows:

curl http://localhost:8080/users/c3587ec5-0976-497f-8374-61e0c2ea3da5

After adding the FriendlyID library, the controller method itself does not change, but you would be able to access a given user using the relevant FriendlyID as follows:

curl http://localhost:8080/users/5wbwf6yUxVBcr48AMbz9cb

In addition, if a given document returned by such a method contains objects of type UUID, those IDs will also be shortened into FriendlyID format.

Advanced (Optimizing testing)

The FriendlyID library makes it possible to define for UUIDs values which are easy to read. By using names instead of hard-to-remember UUIDs, you can write much simpler tests for your code, for example:

@TestpublicvoidshouldGetUser() { mockMvc.perform(get("/users/{userId}", "John")) .andExpect(status().isOk()) .andExpect(content().contentType("application/json")) .andExpect(jsonPath("$.uuid", is("John"))); } 

In the above example, the variable "John" is decoded by the library to the correct UUID, in this case, 00000000-0000-0000-0000-000000a69efb. In this way, you can give a variable in a test class a truly meaningful value and, as a result, an assertion which refers to that variable becomes exceptionally easy to understand in your test program.

FriendlyID library

Dependencies

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Usage

FriendlyIds.createFriendlyId();

This creates a new, random FriendlyID, for example: 5wbwf6yUxVBcr48AMbz9cb

FriendlyIds.toFriendlyId(UUID.fromString("c3587ec5-0976-497f-8374-61e0c2ea3da5"));

This converts a UUID in the form of a string to a FriendlyID, for example: 5wbwf6yUxVBcr48AMbz9cb

FriendlyIds.toUuid("5wbwf6yUxVBcr48AMbz9cb");
// orFriendlyIds.toUuid("c3587ec5-0976-497f-8374-61e0c2ea3da5");

This converts a FriendlyID or UUID string to UUID. Both formats are accepted.

Notes

  • As every UUID is a 128-bit number, a FriendlyID can also store only a 128-bit number.
  • If a FriendlyID has any leading zeros, those leading zeros are ignored - for example, 00cafe is treated as cafe.

Integrations

Spring Boot integration

The FriendlyID library includes a Spring configuration to make it easy to add shorter IDs to an application. With a typical application based on Spring Boot, for your controllers to be able to use FriendlyIDs when communicating with the outside world, just add one new starter dependency as follows:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-spring-boot-starter</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Let us assume that you'll use this sample application:

@SpringBootApplication@RestControllerpublicclassApplication {
publicstaticvoidmain(String[] args) {
SpringApplication.run(Application.class, args);
}
@GetMapping("/bars/{bar}")
publicBargetBar(@PathVariableUUIDbar) {
returnnewBar(UUID.randomUUID());
}
@ValueclassBar {
privatefinalUUIDid;
}
} 

This command: curl http://localhost:8080/bars/5fD1KwsxRcGhBqWNju0jzt

will result in the following output:

{"id":"52OMXhWiAqUWwII0c97Svl"}

In this case, Bar is a POJO class which is converted by Spring MVC to a JSON document. This Bar object has one field of type UUID, and this field is output to the JSON document as a FriendlyID instead of a UUID. Although the application uses the relevant UUID internally, from an external point of view, only the FriendlyID is visible.

Jackson integration

First, add the following Jackson module dependency:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-jackson-datatype</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Then register the FriendlyIdModule module as follows:

ObjectMappermapper = newObjectMapper()
.registerModule(newFriendlyIdModule());

jOOQ integration

The FriendlyID library provides a jOOQ converter for seamless integration with jOOQ's code generation and type-safe queries.

First, add the dependency:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-jooq</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Configure the converter in your jOOQ code generation configuration:

<forcedTypes>
<forcedType>
<userType>com.devskiller.friendly_id.type.FriendlyId</userType>
<converter>com.devskiller.friendly_id.jooq.FriendlyIdConverter</converter>
<includeExpression>.*\.id</includeExpression>
<includeTypes>UUID</includeTypes>
</forcedType>
</forcedTypes>

This automatically converts UUID database columns to FriendlyId value objects in your generated jOOQ records.

JPA integration

The FriendlyID library includes a JPA AttributeConverter for transparent conversion between UUID database columns and FriendlyId value objects.

First, add the dependency:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-jpa</artifactId>
<version>2.0.0-beta3</version>
</dependency>

The converter is automatically applied to all FriendlyId attributes in your entities:

@EntitypublicclassUser {
@IdprivateFriendlyIdid;
privateStringname;
// getters/setters
}

The FriendlyId value object stores UUID internally (16 bytes) and computes the FriendlyId string only when needed, making it more memory-efficient than storing strings.

OpenFeign integration

The FriendlyID library provides automatic encoding/decoding for Spring Cloud OpenFeign clients.

First, add the dependency:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-openfeign</artifactId>
<version>2.0.0-beta3</version>
</dependency>

The integration is automatically configured when Spring Cloud OpenFeign is on the classpath:

@FeignClient(name = "user-service")
publicinterfaceUserClient {
@GetMapping("/users/{id}")
UserDtogetUser(@PathVariableUUIDid); // Sends FriendlyId string@GetMapping("/users/{id}/profile")
ProfileDtogetProfile(@PathVariableFriendlyIdid); // Also works with FriendlyId value object
}

UUID and FriendlyId parameters are automatically converted to FriendlyId strings in requests, and FriendlyId strings in responses are converted back to UUID or FriendlyId objects.

Migration Guide

Migrating from 1.x to 2.x

Version 2.0 introduces several breaking changes to support Spring Boot 4 and Jackson 3.

Requirements

VersionJavaSpring BootJackson
1.x8+2.x, 3.x2.x
2.x21+4.x3.x

For Spring Boot 4 + Jackson 3 projects

Update dependencies to use the new version:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-spring-boot-starter</artifactId>
<version>2.0.0-beta3</version>
</dependency>

For Spring Boot 3 + Jackson 2 projects

Use the new Jackson 2.x module:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-jackson2-datatype</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Register the module:

ObjectMappermapper = newObjectMapper()
.registerModule(newFriendlyIdJackson2Module());

Utility class renamed

The utility class has been renamed from FriendlyId to FriendlyIds (plural) following Java conventions:

// Before (1.x)importcom.devskiller.friendly_id.FriendlyId;
FriendlyId.toUuid("5wbwf6yUxVBcr48AMbz9cb");
// After (2.x)importcom.devskiller.friendly_id.FriendlyIds;
FriendlyIds.toUuid("5wbwf6yUxVBcr48AMbz9cb");

The old FriendlyId class is deprecated and will be removed in a future version.

@IdFormat annotation

The @IdFormat annotation has been moved from the Jackson module to the core module:

// Before (1.x)importcom.devskiller.friendly_id.jackson.IdFormat;
importcom.devskiller.friendly_id.jackson.FriendlyIdFormat;
// After (2.x)importcom.devskiller.friendly_id.IdFormat;
importcom.devskiller.friendly_id.FriendlyIdFormat;

This change allows using the same annotation with both Jackson 2.x and Jackson 3.x modules.

FriendlyId value object

Version 2.x introduces the FriendlyId value object type for type-safe ID handling:

importcom.devskiller.friendly_id.type.FriendlyId;
// Create from UUIDFriendlyIdid = FriendlyId.of(uuid);
// Or use static import friendly methodimportstaticcom.devskiller.friendly_id.type.FriendlyId.friendlyId;
FriendlyIdid = friendlyId(uuid);
// Parse from string (accepts both FriendlyId and UUID formats)FriendlyIdid = FriendlyId.parse("5wbwf6yUxVBcr48AMbz9cb");
FriendlyIdid = FriendlyId.parse("c3587ec5-0976-497f-8374-61e0c2ea3da5");
// Create randomFriendlyIdid = FriendlyId.random();
// Get UUIDUUIDuuid = id.toUuid();
// Get string representationStringfriendlyIdString = id.value(); // Returns FriendlyId stringStringfriendlyIdString = id.toString(); // Same as value()

The value object can be used in:

  • @PathVariable FriendlyId id
  • @RequestParam FriendlyId id
  • @RequestBody with JSON fields
  • JPA entities
  • jOOQ records

Null-safety with JSpecify

Version 2.x uses JSpecify annotations for null-safety. All packages are marked with @NullMarked, meaning parameters and return values are non-null by default.

Jackson module names

Jackson VersionModule ClassArtifact
Jackson 3.xFriendlyIdModulefriendly-id-jackson-datatype
Jackson 2.xFriendlyIdJackson2Modulefriendly-id-jackson2-datatype

Contributing

Thinking of helping us out? We invite you to take a look at:

License

The project is licensed under the Apache 2.0 license. For further details, please see the License page.

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

143 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Build StatusMaven CentralCoverage Status

FriendlyID (Java, Swift, Rust, Go)

What is the FriendlyID library?

The FriendlyID library converts a given UUID (with 36 characters) to a URL-friendly ID (a "FriendlyID") which is based on Base62 (with a maximum of 22 characters), as in the example below:

UUID Friendly ID
c3587ec5-0976-497f-8374-61e0c2ea3da5 -> 5wbwf6yUxVBcr48AMbz9cb
| | 36 characters 22 characters or less

In addition, this library allows to:

  • convert from a FriendlyID back to the original UUID; and
  • create a new, random FriendlyID

Why use a FriendlyID?

Universal Unique IDs (UUIDs) provide a non-sequential and unique identifier that can be generated separately from the source database. As a result, it is not possible to guess either the previous or next identifier. That's great, but, to achieve this level of security, a UUID is long (128 bits long) and looks ugly (36 alphanumeric characters including four hyphens which are added to make it easier to read the UUID), as in this example: 123e4567-e89b-12d3-a456-426655440000.

Such a format is:

  • difficult to read (especially if it is part of a URL)
  • difficult to remember
  • cannot be copied with just two mouse-clicks (you have to select manually the start and end positions)
  • can easily become broken across lines when it is copied, pasted, edited, or sent.

Our FriendlyID Java library solves these problems by converting a given UUID using Base62 with alphanumeric characters in the range [0-9A-Za-z] into a FriendlyId which consists of a maximum of 22 characters (but in fact often contains fewer characters).

Supported languages

Curently FriendlyId supports Java (this project) and

Tools

There are available CLI converters for many platforms.

Use cases

Basic (returning a user in a database)

Let us assume that a method in the controller for returning users requires the relevant UUID in order to find a given user in a database, as in this example:

@GetMapping("/users/{userId}") publicUsergetUser(@PathVariableUUIDuserId) {
[implementationdeleted]
}

Without using the Friendly ID library, you could access a given user as follows:

curl http://localhost:8080/users/c3587ec5-0976-497f-8374-61e0c2ea3da5

After adding the FriendlyID library, the controller method itself does not change, but you would be able to access a given user using the relevant FriendlyID as follows:

curl http://localhost:8080/users/5wbwf6yUxVBcr48AMbz9cb

In addition, if a given document returned by such a method contains objects of type UUID, those IDs will also be shortened into FriendlyID format.

Advanced (Optimizing testing)

The FriendlyID library makes it possible to define for UUIDs values which are easy to read. By using names instead of hard-to-remember UUIDs, you can write much simpler tests for your code, for example:

@TestpublicvoidshouldGetUser() { mockMvc.perform(get("/users/{userId}", "John")) .andExpect(status().isOk()) .andExpect(content().contentType("application/json")) .andExpect(jsonPath("$.uuid", is("John"))); } 

In the above example, the variable "John" is decoded by the library to the correct UUID, in this case, 00000000-0000-0000-0000-000000a69efb. In this way, you can give a variable in a test class a truly meaningful value and, as a result, an assertion which refers to that variable becomes exceptionally easy to understand in your test program.

FriendlyID library

Dependencies

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Usage

FriendlyIds.createFriendlyId();

This creates a new, random FriendlyID, for example: 5wbwf6yUxVBcr48AMbz9cb

FriendlyIds.toFriendlyId(UUID.fromString("c3587ec5-0976-497f-8374-61e0c2ea3da5"));

This converts a UUID in the form of a string to a FriendlyID, for example: 5wbwf6yUxVBcr48AMbz9cb

FriendlyIds.toUuid("5wbwf6yUxVBcr48AMbz9cb");
// orFriendlyIds.toUuid("c3587ec5-0976-497f-8374-61e0c2ea3da5");

This converts a FriendlyID or UUID string to UUID. Both formats are accepted.

Notes

  • As every UUID is a 128-bit number, a FriendlyID can also store only a 128-bit number.
  • If a FriendlyID has any leading zeros, those leading zeros are ignored - for example, 00cafe is treated as cafe.

Integrations

Spring Boot integration

The FriendlyID library includes a Spring configuration to make it easy to add shorter IDs to an application. With a typical application based on Spring Boot, for your controllers to be able to use FriendlyIDs when communicating with the outside world, just add one new starter dependency as follows:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-spring-boot-starter</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Let us assume that you'll use this sample application:

@SpringBootApplication@RestControllerpublicclassApplication {
publicstaticvoidmain(String[] args) {
SpringApplication.run(Application.class, args);
}
@GetMapping("/bars/{bar}")
publicBargetBar(@PathVariableUUIDbar) {
returnnewBar(UUID.randomUUID());
}
@ValueclassBar {
privatefinalUUIDid;
}
} 

This command: curl http://localhost:8080/bars/5fD1KwsxRcGhBqWNju0jzt

will result in the following output:

{"id":"52OMXhWiAqUWwII0c97Svl"}

In this case, Bar is a POJO class which is converted by Spring MVC to a JSON document. This Bar object has one field of type UUID, and this field is output to the JSON document as a FriendlyID instead of a UUID. Although the application uses the relevant UUID internally, from an external point of view, only the FriendlyID is visible.

Jackson integration

First, add the following Jackson module dependency:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-jackson-datatype</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Then register the FriendlyIdModule module as follows:

ObjectMappermapper = newObjectMapper()
.registerModule(newFriendlyIdModule());

jOOQ integration

The FriendlyID library provides a jOOQ converter for seamless integration with jOOQ's code generation and type-safe queries.

First, add the dependency:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-jooq</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Configure the converter in your jOOQ code generation configuration:

<forcedTypes>
<forcedType>
<userType>com.devskiller.friendly_id.type.FriendlyId</userType>
<converter>com.devskiller.friendly_id.jooq.FriendlyIdConverter</converter>
<includeExpression>.*\.id</includeExpression>
<includeTypes>UUID</includeTypes>
</forcedType>
</forcedTypes>

This automatically converts UUID database columns to FriendlyId value objects in your generated jOOQ records.

JPA integration

The FriendlyID library includes a JPA AttributeConverter for transparent conversion between UUID database columns and FriendlyId value objects.

First, add the dependency:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-jpa</artifactId>
<version>2.0.0-beta3</version>
</dependency>

The converter is automatically applied to all FriendlyId attributes in your entities:

@EntitypublicclassUser {
@IdprivateFriendlyIdid;
privateStringname;
// getters/setters
}

The FriendlyId value object stores UUID internally (16 bytes) and computes the FriendlyId string only when needed, making it more memory-efficient than storing strings.

OpenFeign integration

The FriendlyID library provides automatic encoding/decoding for Spring Cloud OpenFeign clients.

First, add the dependency:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-openfeign</artifactId>
<version>2.0.0-beta3</version>
</dependency>

The integration is automatically configured when Spring Cloud OpenFeign is on the classpath:

@FeignClient(name = "user-service")
publicinterfaceUserClient {
@GetMapping("/users/{id}")
UserDtogetUser(@PathVariableUUIDid); // Sends FriendlyId string@GetMapping("/users/{id}/profile")
ProfileDtogetProfile(@PathVariableFriendlyIdid); // Also works with FriendlyId value object
}

UUID and FriendlyId parameters are automatically converted to FriendlyId strings in requests, and FriendlyId strings in responses are converted back to UUID or FriendlyId objects.

Migration Guide

Migrating from 1.x to 2.x

Version 2.0 introduces several breaking changes to support Spring Boot 4 and Jackson 3.

Requirements

VersionJavaSpring BootJackson
1.x8+2.x, 3.x2.x
2.x21+4.x3.x

For Spring Boot 4 + Jackson 3 projects

Update dependencies to use the new version:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-spring-boot-starter</artifactId>
<version>2.0.0-beta3</version>
</dependency>

For Spring Boot 3 + Jackson 2 projects

Use the new Jackson 2.x module:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-jackson2-datatype</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Register the module:

ObjectMappermapper = newObjectMapper()
.registerModule(newFriendlyIdJackson2Module());

Utility class renamed

The utility class has been renamed from FriendlyId to FriendlyIds (plural) following Java conventions:

// Before (1.x)importcom.devskiller.friendly_id.FriendlyId;
FriendlyId.toUuid("5wbwf6yUxVBcr48AMbz9cb");
// After (2.x)importcom.devskiller.friendly_id.FriendlyIds;
FriendlyIds.toUuid("5wbwf6yUxVBcr48AMbz9cb");

The old FriendlyId class is deprecated and will be removed in a future version.

@IdFormat annotation

The @IdFormat annotation has been moved from the Jackson module to the core module:

// Before (1.x)importcom.devskiller.friendly_id.jackson.IdFormat;
importcom.devskiller.friendly_id.jackson.FriendlyIdFormat;
// After (2.x)importcom.devskiller.friendly_id.IdFormat;
importcom.devskiller.friendly_id.FriendlyIdFormat;

This change allows using the same annotation with both Jackson 2.x and Jackson 3.x modules.

FriendlyId value object

Version 2.x introduces the FriendlyId value object type for type-safe ID handling:

importcom.devskiller.friendly_id.type.FriendlyId;
// Create from UUIDFriendlyIdid = FriendlyId.of(uuid);
// Or use static import friendly methodimportstaticcom.devskiller.friendly_id.type.FriendlyId.friendlyId;
FriendlyIdid = friendlyId(uuid);
// Parse from string (accepts both FriendlyId and UUID formats)FriendlyIdid = FriendlyId.parse("5wbwf6yUxVBcr48AMbz9cb");
FriendlyIdid = FriendlyId.parse("c3587ec5-0976-497f-8374-61e0c2ea3da5");
// Create randomFriendlyIdid = FriendlyId.random();
// Get UUIDUUIDuuid = id.toUuid();
// Get string representationStringfriendlyIdString = id.value(); // Returns FriendlyId stringStringfriendlyIdString = id.toString(); // Same as value()

The value object can be used in:

  • @PathVariable FriendlyId id
  • @RequestParam FriendlyId id
  • @RequestBody with JSON fields
  • JPA entities
  • jOOQ records

Null-safety with JSpecify

Version 2.x uses JSpecify annotations for null-safety. All packages are marked with @NullMarked, meaning parameters and return values are non-null by default.

Jackson module names

Jackson VersionModule ClassArtifact
Jackson 3.xFriendlyIdModulefriendly-id-jackson-datatype
Jackson 2.xFriendlyIdJackson2Modulefriendly-id-jackson2-datatype

Contributing

Thinking of helping us out? We invite you to take a look at:

License

The project is licensed under the Apache 2.0 license. For further details, please see the License page.

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

143 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Build StatusMaven CentralCoverage Status

FriendlyID (Java, Swift, Rust, Go)

What is the FriendlyID library?

The FriendlyID library converts a given UUID (with 36 characters) to a URL-friendly ID (a "FriendlyID") which is based on Base62 (with a maximum of 22 characters), as in the example below:

UUID Friendly ID
c3587ec5-0976-497f-8374-61e0c2ea3da5 -> 5wbwf6yUxVBcr48AMbz9cb
| | 36 characters 22 characters or less

In addition, this library allows to:

  • convert from a FriendlyID back to the original UUID; and
  • create a new, random FriendlyID

Why use a FriendlyID?

Universal Unique IDs (UUIDs) provide a non-sequential and unique identifier that can be generated separately from the source database. As a result, it is not possible to guess either the previous or next identifier. That's great, but, to achieve this level of security, a UUID is long (128 bits long) and looks ugly (36 alphanumeric characters including four hyphens which are added to make it easier to read the UUID), as in this example: 123e4567-e89b-12d3-a456-426655440000.

Such a format is:

  • difficult to read (especially if it is part of a URL)
  • difficult to remember
  • cannot be copied with just two mouse-clicks (you have to select manually the start and end positions)
  • can easily become broken across lines when it is copied, pasted, edited, or sent.

Our FriendlyID Java library solves these problems by converting a given UUID using Base62 with alphanumeric characters in the range [0-9A-Za-z] into a FriendlyId which consists of a maximum of 22 characters (but in fact often contains fewer characters).

Supported languages

Curently FriendlyId supports Java (this project) and

Tools

There are available CLI converters for many platforms.

Use cases

Basic (returning a user in a database)

Let us assume that a method in the controller for returning users requires the relevant UUID in order to find a given user in a database, as in this example:

@GetMapping("/users/{userId}") publicUsergetUser(@PathVariableUUIDuserId) {
[implementationdeleted]
}

Without using the Friendly ID library, you could access a given user as follows:

curl http://localhost:8080/users/c3587ec5-0976-497f-8374-61e0c2ea3da5

After adding the FriendlyID library, the controller method itself does not change, but you would be able to access a given user using the relevant FriendlyID as follows:

curl http://localhost:8080/users/5wbwf6yUxVBcr48AMbz9cb

In addition, if a given document returned by such a method contains objects of type UUID, those IDs will also be shortened into FriendlyID format.

Advanced (Optimizing testing)

The FriendlyID library makes it possible to define for UUIDs values which are easy to read. By using names instead of hard-to-remember UUIDs, you can write much simpler tests for your code, for example:

@TestpublicvoidshouldGetUser() { mockMvc.perform(get("/users/{userId}", "John")) .andExpect(status().isOk()) .andExpect(content().contentType("application/json")) .andExpect(jsonPath("$.uuid", is("John"))); } 

In the above example, the variable "John" is decoded by the library to the correct UUID, in this case, 00000000-0000-0000-0000-000000a69efb. In this way, you can give a variable in a test class a truly meaningful value and, as a result, an assertion which refers to that variable becomes exceptionally easy to understand in your test program.

FriendlyID library

Dependencies

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Usage

FriendlyIds.createFriendlyId();

This creates a new, random FriendlyID, for example: 5wbwf6yUxVBcr48AMbz9cb

FriendlyIds.toFriendlyId(UUID.fromString("c3587ec5-0976-497f-8374-61e0c2ea3da5"));

This converts a UUID in the form of a string to a FriendlyID, for example: 5wbwf6yUxVBcr48AMbz9cb

FriendlyIds.toUuid("5wbwf6yUxVBcr48AMbz9cb");
// orFriendlyIds.toUuid("c3587ec5-0976-497f-8374-61e0c2ea3da5");

This converts a FriendlyID or UUID string to UUID. Both formats are accepted.

Notes

  • As every UUID is a 128-bit number, a FriendlyID can also store only a 128-bit number.
  • If a FriendlyID has any leading zeros, those leading zeros are ignored - for example, 00cafe is treated as cafe.

Integrations

Spring Boot integration

The FriendlyID library includes a Spring configuration to make it easy to add shorter IDs to an application. With a typical application based on Spring Boot, for your controllers to be able to use FriendlyIDs when communicating with the outside world, just add one new starter dependency as follows:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-spring-boot-starter</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Let us assume that you'll use this sample application:

@SpringBootApplication@RestControllerpublicclassApplication {
publicstaticvoidmain(String[] args) {
SpringApplication.run(Application.class, args);
}
@GetMapping("/bars/{bar}")
publicBargetBar(@PathVariableUUIDbar) {
returnnewBar(UUID.randomUUID());
}
@ValueclassBar {
privatefinalUUIDid;
}
} 

This command: curl http://localhost:8080/bars/5fD1KwsxRcGhBqWNju0jzt

will result in the following output:

{"id":"52OMXhWiAqUWwII0c97Svl"}

In this case, Bar is a POJO class which is converted by Spring MVC to a JSON document. This Bar object has one field of type UUID, and this field is output to the JSON document as a FriendlyID instead of a UUID. Although the application uses the relevant UUID internally, from an external point of view, only the FriendlyID is visible.

Jackson integration

First, add the following Jackson module dependency:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-jackson-datatype</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Then register the FriendlyIdModule module as follows:

ObjectMappermapper = newObjectMapper()
.registerModule(newFriendlyIdModule());

jOOQ integration

The FriendlyID library provides a jOOQ converter for seamless integration with jOOQ's code generation and type-safe queries.

First, add the dependency:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-jooq</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Configure the converter in your jOOQ code generation configuration:

<forcedTypes>
<forcedType>
<userType>com.devskiller.friendly_id.type.FriendlyId</userType>
<converter>com.devskiller.friendly_id.jooq.FriendlyIdConverter</converter>
<includeExpression>.*\.id</includeExpression>
<includeTypes>UUID</includeTypes>
</forcedType>
</forcedTypes>

This automatically converts UUID database columns to FriendlyId value objects in your generated jOOQ records.

JPA integration

The FriendlyID library includes a JPA AttributeConverter for transparent conversion between UUID database columns and FriendlyId value objects.

First, add the dependency:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-jpa</artifactId>
<version>2.0.0-beta3</version>
</dependency>

The converter is automatically applied to all FriendlyId attributes in your entities:

@EntitypublicclassUser {
@IdprivateFriendlyIdid;
privateStringname;
// getters/setters
}

The FriendlyId value object stores UUID internally (16 bytes) and computes the FriendlyId string only when needed, making it more memory-efficient than storing strings.

OpenFeign integration

The FriendlyID library provides automatic encoding/decoding for Spring Cloud OpenFeign clients.

First, add the dependency:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-openfeign</artifactId>
<version>2.0.0-beta3</version>
</dependency>

The integration is automatically configured when Spring Cloud OpenFeign is on the classpath:

@FeignClient(name = "user-service")
publicinterfaceUserClient {
@GetMapping("/users/{id}")
UserDtogetUser(@PathVariableUUIDid); // Sends FriendlyId string@GetMapping("/users/{id}/profile")
ProfileDtogetProfile(@PathVariableFriendlyIdid); // Also works with FriendlyId value object
}

UUID and FriendlyId parameters are automatically converted to FriendlyId strings in requests, and FriendlyId strings in responses are converted back to UUID or FriendlyId objects.

Migration Guide

Migrating from 1.x to 2.x

Version 2.0 introduces several breaking changes to support Spring Boot 4 and Jackson 3.

Requirements

VersionJavaSpring BootJackson
1.x8+2.x, 3.x2.x
2.x21+4.x3.x

For Spring Boot 4 + Jackson 3 projects

Update dependencies to use the new version:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-spring-boot-starter</artifactId>
<version>2.0.0-beta3</version>
</dependency>

For Spring Boot 3 + Jackson 2 projects

Use the new Jackson 2.x module:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-jackson2-datatype</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Register the module:

ObjectMappermapper = newObjectMapper()
.registerModule(newFriendlyIdJackson2Module());

Utility class renamed

The utility class has been renamed from FriendlyId to FriendlyIds (plural) following Java conventions:

// Before (1.x)importcom.devskiller.friendly_id.FriendlyId;
FriendlyId.toUuid("5wbwf6yUxVBcr48AMbz9cb");
// After (2.x)importcom.devskiller.friendly_id.FriendlyIds;
FriendlyIds.toUuid("5wbwf6yUxVBcr48AMbz9cb");

The old FriendlyId class is deprecated and will be removed in a future version.

@IdFormat annotation

The @IdFormat annotation has been moved from the Jackson module to the core module:

// Before (1.x)importcom.devskiller.friendly_id.jackson.IdFormat;
importcom.devskiller.friendly_id.jackson.FriendlyIdFormat;
// After (2.x)importcom.devskiller.friendly_id.IdFormat;
importcom.devskiller.friendly_id.FriendlyIdFormat;

This change allows using the same annotation with both Jackson 2.x and Jackson 3.x modules.

FriendlyId value object

Version 2.x introduces the FriendlyId value object type for type-safe ID handling:

importcom.devskiller.friendly_id.type.FriendlyId;
// Create from UUIDFriendlyIdid = FriendlyId.of(uuid);
// Or use static import friendly methodimportstaticcom.devskiller.friendly_id.type.FriendlyId.friendlyId;
FriendlyIdid = friendlyId(uuid);
// Parse from string (accepts both FriendlyId and UUID formats)FriendlyIdid = FriendlyId.parse("5wbwf6yUxVBcr48AMbz9cb");
FriendlyIdid = FriendlyId.parse("c3587ec5-0976-497f-8374-61e0c2ea3da5");
// Create randomFriendlyIdid = FriendlyId.random();
// Get UUIDUUIDuuid = id.toUuid();
// Get string representationStringfriendlyIdString = id.value(); // Returns FriendlyId stringStringfriendlyIdString = id.toString(); // Same as value()

The value object can be used in:

  • @PathVariable FriendlyId id
  • @RequestParam FriendlyId id
  • @RequestBody with JSON fields
  • JPA entities
  • jOOQ records

Null-safety with JSpecify

Version 2.x uses JSpecify annotations for null-safety. All packages are marked with @NullMarked, meaning parameters and return values are non-null by default.

Jackson module names

Jackson VersionModule ClassArtifact
Jackson 3.xFriendlyIdModulefriendly-id-jackson-datatype
Jackson 2.xFriendlyIdJackson2Modulefriendly-id-jackson2-datatype

Contributing

Thinking of helping us out? We invite you to take a look at:

License

The project is licensed under the Apache 2.0 license. For further details, please see the License page.

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Latest commit

History

143 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Build StatusMaven CentralCoverage Status

FriendlyID (Java, Swift, Rust, Go)

What is the FriendlyID library?

The FriendlyID library converts a given UUID (with 36 characters) to a URL-friendly ID (a "FriendlyID") which is based on Base62 (with a maximum of 22 characters), as in the example below:

UUID Friendly ID
c3587ec5-0976-497f-8374-61e0c2ea3da5 -> 5wbwf6yUxVBcr48AMbz9cb
| | 36 characters 22 characters or less

In addition, this library allows to:

  • convert from a FriendlyID back to the original UUID; and
  • create a new, random FriendlyID

Why use a FriendlyID?

Universal Unique IDs (UUIDs) provide a non-sequential and unique identifier that can be generated separately from the source database. As a result, it is not possible to guess either the previous or next identifier. That's great, but, to achieve this level of security, a UUID is long (128 bits long) and looks ugly (36 alphanumeric characters including four hyphens which are added to make it easier to read the UUID), as in this example: 123e4567-e89b-12d3-a456-426655440000.

Such a format is:

  • difficult to read (especially if it is part of a URL)
  • difficult to remember
  • cannot be copied with just two mouse-clicks (you have to select manually the start and end positions)
  • can easily become broken across lines when it is copied, pasted, edited, or sent.

Our FriendlyID Java library solves these problems by converting a given UUID using Base62 with alphanumeric characters in the range [0-9A-Za-z] into a FriendlyId which consists of a maximum of 22 characters (but in fact often contains fewer characters).

Supported languages

Curently FriendlyId supports Java (this project) and

Tools

There are available CLI converters for many platforms.

Use cases

Basic (returning a user in a database)

Let us assume that a method in the controller for returning users requires the relevant UUID in order to find a given user in a database, as in this example:

@GetMapping("/users/{userId}") publicUsergetUser(@PathVariableUUIDuserId) {
[implementationdeleted]
}

Without using the Friendly ID library, you could access a given user as follows:

curl http://localhost:8080/users/c3587ec5-0976-497f-8374-61e0c2ea3da5

After adding the FriendlyID library, the controller method itself does not change, but you would be able to access a given user using the relevant FriendlyID as follows:

curl http://localhost:8080/users/5wbwf6yUxVBcr48AMbz9cb

In addition, if a given document returned by such a method contains objects of type UUID, those IDs will also be shortened into FriendlyID format.

Advanced (Optimizing testing)

The FriendlyID library makes it possible to define for UUIDs values which are easy to read. By using names instead of hard-to-remember UUIDs, you can write much simpler tests for your code, for example:

@TestpublicvoidshouldGetUser() { mockMvc.perform(get("/users/{userId}", "John")) .andExpect(status().isOk()) .andExpect(content().contentType("application/json")) .andExpect(jsonPath("$.uuid", is("John"))); } 

In the above example, the variable "John" is decoded by the library to the correct UUID, in this case, 00000000-0000-0000-0000-000000a69efb. In this way, you can give a variable in a test class a truly meaningful value and, as a result, an assertion which refers to that variable becomes exceptionally easy to understand in your test program.

FriendlyID library

Dependencies

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Usage

FriendlyIds.createFriendlyId();

This creates a new, random FriendlyID, for example: 5wbwf6yUxVBcr48AMbz9cb

FriendlyIds.toFriendlyId(UUID.fromString("c3587ec5-0976-497f-8374-61e0c2ea3da5"));

This converts a UUID in the form of a string to a FriendlyID, for example: 5wbwf6yUxVBcr48AMbz9cb

FriendlyIds.toUuid("5wbwf6yUxVBcr48AMbz9cb");
// orFriendlyIds.toUuid("c3587ec5-0976-497f-8374-61e0c2ea3da5");

This converts a FriendlyID or UUID string to UUID. Both formats are accepted.

Notes

  • As every UUID is a 128-bit number, a FriendlyID can also store only a 128-bit number.
  • If a FriendlyID has any leading zeros, those leading zeros are ignored - for example, 00cafe is treated as cafe.

Integrations

Spring Boot integration

The FriendlyID library includes a Spring configuration to make it easy to add shorter IDs to an application. With a typical application based on Spring Boot, for your controllers to be able to use FriendlyIDs when communicating with the outside world, just add one new starter dependency as follows:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-spring-boot-starter</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Let us assume that you'll use this sample application:

@SpringBootApplication@RestControllerpublicclassApplication {
publicstaticvoidmain(String[] args) {
SpringApplication.run(Application.class, args);
}
@GetMapping("/bars/{bar}")
publicBargetBar(@PathVariableUUIDbar) {
returnnewBar(UUID.randomUUID());
}
@ValueclassBar {
privatefinalUUIDid;
}
} 

This command: curl http://localhost:8080/bars/5fD1KwsxRcGhBqWNju0jzt

will result in the following output:

{"id":"52OMXhWiAqUWwII0c97Svl"}

In this case, Bar is a POJO class which is converted by Spring MVC to a JSON document. This Bar object has one field of type UUID, and this field is output to the JSON document as a FriendlyID instead of a UUID. Although the application uses the relevant UUID internally, from an external point of view, only the FriendlyID is visible.

Jackson integration

First, add the following Jackson module dependency:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-jackson-datatype</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Then register the FriendlyIdModule module as follows:

ObjectMappermapper = newObjectMapper()
.registerModule(newFriendlyIdModule());

jOOQ integration

The FriendlyID library provides a jOOQ converter for seamless integration with jOOQ's code generation and type-safe queries.

First, add the dependency:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-jooq</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Configure the converter in your jOOQ code generation configuration:

<forcedTypes>
<forcedType>
<userType>com.devskiller.friendly_id.type.FriendlyId</userType>
<converter>com.devskiller.friendly_id.jooq.FriendlyIdConverter</converter>
<includeExpression>.*\.id</includeExpression>
<includeTypes>UUID</includeTypes>
</forcedType>
</forcedTypes>

This automatically converts UUID database columns to FriendlyId value objects in your generated jOOQ records.

JPA integration

The FriendlyID library includes a JPA AttributeConverter for transparent conversion between UUID database columns and FriendlyId value objects.

First, add the dependency:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-jpa</artifactId>
<version>2.0.0-beta3</version>
</dependency>

The converter is automatically applied to all FriendlyId attributes in your entities:

@EntitypublicclassUser {
@IdprivateFriendlyIdid;
privateStringname;
// getters/setters
}

The FriendlyId value object stores UUID internally (16 bytes) and computes the FriendlyId string only when needed, making it more memory-efficient than storing strings.

OpenFeign integration

The FriendlyID library provides automatic encoding/decoding for Spring Cloud OpenFeign clients.

First, add the dependency:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-openfeign</artifactId>
<version>2.0.0-beta3</version>
</dependency>

The integration is automatically configured when Spring Cloud OpenFeign is on the classpath:

@FeignClient(name = "user-service")
publicinterfaceUserClient {
@GetMapping("/users/{id}")
UserDtogetUser(@PathVariableUUIDid); // Sends FriendlyId string@GetMapping("/users/{id}/profile")
ProfileDtogetProfile(@PathVariableFriendlyIdid); // Also works with FriendlyId value object
}

UUID and FriendlyId parameters are automatically converted to FriendlyId strings in requests, and FriendlyId strings in responses are converted back to UUID or FriendlyId objects.

Migration Guide

Migrating from 1.x to 2.x

Version 2.0 introduces several breaking changes to support Spring Boot 4 and Jackson 3.

Requirements

VersionJavaSpring BootJackson
1.x8+2.x, 3.x2.x
2.x21+4.x3.x

For Spring Boot 4 + Jackson 3 projects

Update dependencies to use the new version:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-spring-boot-starter</artifactId>
<version>2.0.0-beta3</version>
</dependency>

For Spring Boot 3 + Jackson 2 projects

Use the new Jackson 2.x module:

<dependency>
<groupId>com.devskiller.friendly-id</groupId>
<artifactId>friendly-id-jackson2-datatype</artifactId>
<version>2.0.0-beta3</version>
</dependency>

Register the module:

ObjectMappermapper = newObjectMapper()
.registerModule(newFriendlyIdJackson2Module());

Utility class renamed

The utility class has been renamed from FriendlyId to FriendlyIds (plural) following Java conventions:

// Before (1.x)importcom.devskiller.friendly_id.FriendlyId;
FriendlyId.toUuid("5wbwf6yUxVBcr48AMbz9cb");
// After (2.x)importcom.devskiller.friendly_id.FriendlyIds;
FriendlyIds.toUuid("5wbwf6yUxVBcr48AMbz9cb");

The old FriendlyId class is deprecated and will be removed in a future version.

@IdFormat annotation

The @IdFormat annotation has been moved from the Jackson module to the core module:

// Before (1.x)importcom.devskiller.friendly_id.jackson.IdFormat;
importcom.devskiller.friendly_id.jackson.FriendlyIdFormat;
// After (2.x)importcom.devskiller.friendly_id.IdFormat;
importcom.devskiller.friendly_id.FriendlyIdFormat;

This change allows using the same annotation with both Jackson 2.x and Jackson 3.x modules.

FriendlyId value object

Version 2.x introduces the FriendlyId value object type for type-safe ID handling:

importcom.devskiller.friendly_id.type.FriendlyId;
// Create from UUIDFriendlyIdid = FriendlyId.of(uuid);
// Or use static import friendly methodimportstaticcom.devskiller.friendly_id.type.FriendlyId.friendlyId;
FriendlyIdid = friendlyId(uuid);
// Parse from string (accepts both FriendlyId and UUID formats)FriendlyIdid = FriendlyId.parse("5wbwf6yUxVBcr48AMbz9cb");
FriendlyIdid = FriendlyId.parse("c3587ec5-0976-497f-8374-61e0c2ea3da5");
// Create randomFriendlyIdid = FriendlyId.random();
// Get UUIDUUIDuuid = id.toUuid();
// Get string representationStringfriendlyIdString = id.value(); // Returns FriendlyId stringStringfriendlyIdString = id.toString(); // Same as value()

The value object can be used in:

  • @PathVariable FriendlyId id
  • @RequestParam FriendlyId id
  • @RequestBody with JSON fields
  • JPA entities
  • jOOQ records

Null-safety with JSpecify

Version 2.x uses JSpecify annotations for null-safety. All packages are marked with @NullMarked, meaning parameters and return values are non-null by default.

Jackson module names

Jackson VersionModule ClassArtifact
Jackson 3.xFriendlyIdModulefriendly-id-jackson-datatype
Jackson 2.xFriendlyIdJackson2Modulefriendly-id-jackson2-datatype

Contributing

Thinking of helping us out? We invite you to take a look at:

License

The project is licensed under the Apache 2.0 license. For further details, please see the License page.

Releases

Packages

Used by

Contributors

Languages