# 0.8.x ➡️ 1.x.x

1.0 version introduces major breaking changes that need you to update some of your code and migrate your data.

## Id. are UUID

Users and OAuth accounts id. are now represented as real UUID objects instead of plain strings.  This change was introduced to leverage efficient storage and indexing for DBMS that supports UUID (especially PostgreSQL and Mongo).

### In Python code

If you were doing comparison betwen a user id. and a string (in unit tests for example), you should now cast the id. to string:

```py
# Before
assert "d35d213e-f3d8-4f08-954a-7e0d1bea286f" == user.id

# Now
assert "d35d213e-f3d8-4f08-954a-7e0d1bea286f" == str(user.id)
```

If you were refering to user id. in your Pydantic models, the field should now be of `UUID4` type instead of `str`:

```py
from pydantic import BaseModel, UUID4

# Before
class Model(BaseModel):
    user_id: str

# After
class Model(BaseModel):
    user_id: UUID4
```

#### MongoDB

To avoid any issues, it's recommended to use the `standard` UUID representation when instantiating the MongoDB client:

```py
DATABASE_URL = "mongodb://localhost:27017"
client = motor.motor_asyncio.AsyncIOMotorClient(
    DATABASE_URL, uuidRepresentation="standard"
)
```

This parameter controls how the UUID values will be encoded in the database. By default, it's set to `pythonLegacy` but new applications should consider setting this to `standard` for cross language compatibility. [Read more about this](https://pymongo.readthedocs.io/en/stable/api/pymongo/mongo_client.html#pymongo.mongo_client.MongoClient).


### In database

Id. were before stored as strings in the database. You should make a migration to convert string data to UUID data.

!!! danger
    Scripts below are provided as guidelines. Please **review them carefully**, **adapt them** and check that they are working on a test database before applying them to production. **BE CAREFUL. THEY CAN DESTROY YOUR DATA.**.

#### PostgreSQL

PostgreSQL supports UUID type. If not already, you should enable the `uuid-ossp` extension:

```sql
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
```

To convert the existing id. string column, we can:

1. Create a new column with UUID type.
2. Fill it with the id. converted to UUID.
3. Drop the original id. column.
4. Make the new column a primary key and rename it.

```sql
ALTER TABLE "user" ADD uuid_id UUID;
UPDATE "user" SET uuid_id = uuid(id);
ALTER TABLE "user" DROP id;
ALTER TABLE "user" ADD PRIMARY KEY (uuid_id);
ALTER TABLE "user" RENAME COLUMN uuid_id TO id;
```

#### MySQL

MySQL doesn't support UUID type. We'll just convert the column to `CHAR(36)` type:

```sql
ALTER TABLE "user" MODIFY id CHAR(36);
```

#### MongoDB

##### Mongo shell

For MongoDB, we can use a `forEach` iterator to convert the id. for each document:

```js
db.getCollection('users').find().forEach(function(user) {
  var uuid = UUID(user.id);
  db.getCollection('users').update({_id: user._id}, [{$set: {id: uuid}}]);
});
```

##### Python

```py
import uuid

import motor.motor_asyncio


async def migrate_uuid():
    client = motor.motor_asyncio.AsyncIOMotorClient(
        DATABASE_URL, uuidRepresentation="standard"
    )
    db = client["database_name"]
    users = db["users"]

    async for user in users.find({}):
        await users.update_one(
            {"_id": user["_id"]},
            {"$set": {"id": uuid.UUID(user["id"])}},
        )
```

## Splitted routers

You now have the responsibility to **wire the routers**. FastAPI Users doesn't give a bloated users router anymore.

**Event handlers** are also removed. You have to provide your "after-" logic as a parameter of the router generator.

### Before

```py
jwt_authentication = JWTAuthentication(secret=SECRET, lifetime_seconds=3600)

app = FastAPI()
fastapi_users = FastAPIUsers(
    user_db, [jwt_authentication], User, UserCreate, UserUpdate, UserDB,
)
app.include_router(fastapi_users.router, prefix="/users", tags=["users"])


@fastapi_users.on_after_register()
def on_after_register(user: User, request: Request):
    print(f"User {user.id} has registered.")


@fastapi_users.on_after_forgot_password()
def on_after_forgot_password(user: User, token: str, request: Request):
    print(f"User {user.id} has forgot their password. Reset token: {token}")
```

### After

```py
def on_after_register(user: UserDB, request: Request):
    print(f"User {user.id} has registered.")


def on_after_forgot_password(user: UserDB, token: str, request: Request):
    print(f"User {user.id} has forgot their password. Reset token: {token}")


jwt_authentication = JWTAuthentication(secret=SECRET, lifetime_seconds=3600)

app = FastAPI()
fastapi_users = FastAPIUsers(
    user_db, [jwt_authentication], User, UserCreate, UserUpdate, UserDB,
)
app.include_router(
    fastapi_users.get_auth_router(jwt_authentication), prefix="/auth/jwt", tags=["auth"]
)
app.include_router(
    fastapi_users.get_register_router(on_after_register), prefix="/auth", tags=["auth"]
)
app.include_router(
    fastapi_users.get_reset_password_router(
        SECRET, after_forgot_password=on_after_forgot_password
    ),
    prefix="/auth",
    tags=["auth"],
)
app.include_router(fastapi_users.get_users_router(), prefix="/users", tags=["users"])
```

Important things to notice:

* `FastAPIUsers` takes two arguments less (`reset_password_token_secret` and `reset_password_token_lifetime_seconds`).
* You have more flexibility to choose the **prefix** and **tags** of the routers.
* The `/login`/`/logout` are now your responsibility to include for each backend. The path will change (before `/login/jwt`, after `/jwt/login`).
* If you don't care about some of those routers, you can discard them.
