Check Username Availability
Real-time availability probe for the registration / settings form. Public — no auth required. Returns false on bad format too, so the UI can stop after one round-trip.
GET /api/v1/users/check-username — 🌐 Public · Rate limit: 30 req / minute
Real-time check used by the registration / settings form before submit. Returns { available: true } only when the value normalizes within the configured length bounds, matches the format regex, and is not currently held by an active user or the reserved-name list.
This is the only public endpoint in the user module — needed because the registration form runs before login. The 30/min throttle is per-IP. Pair it with client-side debouncing (~300ms) to keep within budget.
A length / format failure also returns { available: false } — there is no separate "invalid format" code path. If you need to distinguish "taken" from "malformed", validate format client-side before calling. The cooldown rule applies only to PATCH /users/username, not here.
Request
Query parameters
| Param | Type | Required | Notes |
|---|---|---|---|
username | string | ✓ | The candidate username — server normalizes via .toLowerCase().trim() before evaluation |
No body, no headers required.
Response
200 OK — ApiResponseOf<UsernameAvailabilityDto>
{
"success": true,
"data": {
"available": true
}
}| Field | Type | Notes |
|---|---|---|
available | boolean | true only when length + format + uniqueness + not-reserved all pass |
Errors
| HTTP | code / i18nKey | Reason |
|---|---|---|
429 | (throttle) | Rate limit exceeded (30 req/min) |
The endpoint never throws on invalid input — available: false is the universal "not usable" signal.
Side effects
- Normalize input (
.toLowerCase().trim()). - Length check against
site.username_min_length/site.username_max_length(admin-managed) → if outside bounds, return{ available: false }. - Format check against
^[a-z0-9._-]+$→ if failing, return{ available: false }. prisma.user.findUnique({ where: { username } })→ if hit, return{ available: false }.prisma.reservedUsername.findUnique({ where: { username } })→ if hit, return{ available: false }.- Otherwise: return
{ available: true }. No mutations.
Code samples
curl 'https://api.bio.re/api/v1/users/check-username?username=johndoe'async function checkUsername(username: string): Promise<boolean> {
const url = new URL('https://api.bio.re/api/v1/users/check-username');
url.searchParams.set('username', username);
const res = await fetch(url);
const json = await res.json();
if (!res.ok || !json.success) {
throw Object.assign(new Error(json?.error?.message ?? 'Check failed'), {
code: json?.error?.code,
});
}
return json.data.available;
}import { useQuery } from '@tanstack/react-query';
export const usernameKeys = {
availability: (username: string) => ['users', 'check-username', username] as const,
};
export function useUsernameAvailability(username: string) {
// Caller is responsible for debouncing the input upstream.
return useQuery({
queryKey: usernameKeys.availability(username),
queryFn: async () => {
const url = new URL('/api/v1/users/check-username', window.location.origin);
url.searchParams.set('username', username);
const res = await fetch(url);
const json = await res.json();
if (!res.ok || !json.success) {
throw Object.assign(new Error(json?.error?.message ?? 'Check failed'), {
code: json?.error?.code,
i18nKey: json?.error?.i18nKey,
});
}
return json.data.available as boolean;
},
enabled: username.length >= 3,
staleTime: 10_000, // 10s — registration form lifetime is short
});
}Try it
Authorization
bearer In: header
Query Parameters
Response Body
application/json
curl -X GET "https://loading/api/v1/users/check-username?username=string"{
"success": true,
"data": {
"available": true
}
}Source
| Source | Path | Lines |
|---|---|---|
| Controller | apps/api-core/src/modules/user/user.controller.ts | 114–121 (checkUsername, @Public()) |
| DTO (response) | apps/api-core/src/modules/user/dto/user-client-response.dto.ts | 120–123 (UsernameAvailabilityDto) |
| Service | apps/api-core/src/modules/user/user.service.ts | 246–259 (checkUsernameAvailability) |
| Prisma models | packages/prisma/prisma/schema.prisma | User.username, ReservedUsername |
Change Username
Set or rotate the user's username with availability + reserved checks, a per-user cooldown, and a history record. First-time set has no cooldown.
Change Email (Request)
Start an email-change flow by re-confirming the password and sending a verification link to the NEW email. Account email only flips after the new address is verified.