diff --git a/docs/advanced/index.md b/docs/advanced/index.md
index f6178249ce..bbc4156909 100644
--- a/docs/advanced/index.md
+++ b/docs/advanced/index.md
@@ -4,7 +4,7 @@ The **Advanced User Guide** is gradually growing, you can already read about som
At some point it will include:
-* How to use `async` and `await` with the async session.
+* [How to use `async` and `await` with the async session](../tutorial/async/index.md).
* How to run migrations.
* How to combine **SQLModel** models with SQLAlchemy.
* ...and more. 🤓
diff --git a/docs/tutorial/async/index.md b/docs/tutorial/async/index.md
new file mode 100644
index 0000000000..40017125d6
--- /dev/null
+++ b/docs/tutorial/async/index.md
@@ -0,0 +1,108 @@
+# Async with SQLModel
+
+**SQLModel** is based on SQLAlchemy, which has great support for `async` and `await` with `asyncio`.
+
+You can use **SQLModel** in an asynchronous way as well.
+
+## Install `aiosqlite`
+
+For this example, we will use **SQLite** with the `aiosqlite` driver to make it asynchronous.
+
+Make sure you install `aiosqlite`:
+
+
+
+```console
+$ pip install aiosqlite
+---> 100%
+```
+
+
+
+## Create the Async Engine
+
+Instead of `create_engine`, we use `create_async_engine` from `sqlalchemy.ext.asyncio`.
+
+And we change the connection URL to use `sqlite+aiosqlite`.
+
+{* ./docs_src/tutorial/async/tutorial001_py310.py ln[1:16] hl[2,15:16] *}
+
+## Async Session
+
+To use an asynchronous session, we use `AsyncSession` from `sqlmodel.ext.asyncio`.
+
+It is a subclass of SQLAlchemy's `AsyncSession` with added support for SQLModel's `exec()` method.
+
+## Create the Database and Tables
+
+When using an async engine, we cannot call `SQLModel.metadata.create_all(engine)` directly because it is a synchronous operation.
+
+Instead, we use `engine.begin()` and `conn.run_sync()`.
+
+{* ./docs_src/tutorial/async/tutorial001_py310.py ln[19:21] hl[19:21] *}
+
+## Async FastAPI Dependency
+
+We can create an async dependency to get the session.
+
+{* ./docs_src/tutorial/async/tutorial001_py310.py ln[27:30] hl[27:30] *}
+
+## Async Path Operations
+
+Now we can use `async def` for our path operations and `await` the session methods.
+
+We use `await session.exec()` to execute queries, and `await session.commit()`, `await session.refresh()` for mutations.
+
+{* ./docs_src/tutorial/async/tutorial001_py310.py ln[33:45] hl[35:37,42:43] *}
+
+## Full Example
+
+Here is the complete file:
+
+{* ./docs_src/tutorial/async/tutorial001_py310.py *}
+
+## Common Pitfalls and Best Practices
+
+### Use `await` for Database Operations
+
+When using `AsyncSession`, remember to `await` all methods that interact with the database.
+
+This includes:
+* `session.exec()`
+* `session.commit()`
+* `session.refresh()`
+* `session.get()`
+* `session.delete()`
+
+### Relationships and Lazy Loading
+
+By default, SQLAlchemy (and SQLModel) uses "lazy loading" for relationships. In synchronous code, this means that when you access a relationship attribute, it automatically fetches the data from the database.
+
+In asynchronous code, **lazy loading is not supported** because it would need to perform I/O without an `await`.
+
+If you try to access a relationship that hasn't been loaded yet, you will get an error.
+
+To solve this, you should use **eager loading** with `selectinload`.
+
+```Python
+from sqlalchemy.ext.asyncio import AsyncSession
+from sqlalchemy.orm import selectinload
+from sqlmodel import select
+
+# ...
+
+async def read_heroes(session: AsyncSession):
+ statement = select(Hero).options(selectinload(Hero.team))
+ result = await session.exec(statement)
+ heroes = result.all()
+ return heroes
+```
+
+### Async Database Drivers
+
+Make sure you use an asynchronous database driver.
+
+* For **SQLite**, use `aiosqlite` with `sqlite+aiosqlite://`.
+* For **PostgreSQL**, use `asyncpg` with `postgresql+asyncpg://`.
+
+If you use a synchronous driver (like `sqlite://` or `postgresql://`), it will not work with `create_async_engine`.
diff --git a/docs_src/tutorial/async/tutorial001.py b/docs_src/tutorial/async/tutorial001.py
new file mode 100644
index 0000000000..ccd1851372
--- /dev/null
+++ b/docs_src/tutorial/async/tutorial001.py
@@ -0,0 +1,52 @@
+from typing import List, Union
+
+from fastapi import Depends, FastAPI
+from sqlalchemy.ext.asyncio import create_async_engine
+from sqlmodel import Field, SQLModel, select
+from sqlmodel.ext.asyncio import AsyncSession
+
+
+class Hero(SQLModel, table=True):
+ id: Union[int, None] = Field(default=None, primary_key=True)
+ name: str = Field(index=True)
+ secret_name: str
+ age: Union[int, None] = Field(default=None, index=True)
+
+
+sqlite_file_name = "database.db"
+sqlite_url = f"sqlite+aiosqlite:///{sqlite_file_name}"
+
+engine = create_async_engine(sqlite_url, echo=True)
+
+
+async def init_db():
+ async with engine.begin() as conn:
+ await conn.run_sync(SQLModel.metadata.create_all)
+
+
+app = FastAPI()
+
+
+@app.on_event("startup")
+async def on_startup():
+ await init_db()
+
+
+async def get_session():
+ async with AsyncSession(engine) as session:
+ yield session
+
+
+@app.post("/heroes/", response_model=Hero)
+async def create_hero(hero: Hero, session: AsyncSession = Depends(get_session)):
+ session.add(hero)
+ await session.commit()
+ await session.refresh(hero)
+ return hero
+
+
+@app.get("/heroes/", response_model=List[Hero])
+async def read_heroes(session: AsyncSession = Depends(get_session)):
+ result = await session.exec(select(Hero))
+ heroes = result.all()
+ return heroes
diff --git a/docs_src/tutorial/async/tutorial001_py310.py b/docs_src/tutorial/async/tutorial001_py310.py
new file mode 100644
index 0000000000..b85fc265d0
--- /dev/null
+++ b/docs_src/tutorial/async/tutorial001_py310.py
@@ -0,0 +1,50 @@
+from fastapi import Depends, FastAPI
+from sqlalchemy.ext.asyncio import create_async_engine
+from sqlmodel import Field, SQLModel, select
+from sqlmodel.ext.asyncio import AsyncSession
+
+
+class Hero(SQLModel, table=True):
+ id: int | None = Field(default=None, primary_key=True)
+ name: str = Field(index=True)
+ secret_name: str
+ age: int | None = Field(default=None, index=True)
+
+
+sqlite_file_name = "database.db"
+sqlite_url = f"sqlite+aiosqlite:///{sqlite_file_name}"
+
+engine = create_async_engine(sqlite_url, echo=True)
+
+
+async def init_db():
+ async with engine.begin() as conn:
+ await conn.run_sync(SQLModel.metadata.create_all)
+
+
+app = FastAPI()
+
+
+@app.on_event("startup")
+async def on_startup():
+ await init_db()
+
+
+async def get_session():
+ async with AsyncSession(engine) as session:
+ yield session
+
+
+@app.post("/heroes/", response_model=Hero)
+async def create_hero(hero: Hero, session: AsyncSession = Depends(get_session)):
+ session.add(hero)
+ await session.commit()
+ await session.refresh(hero)
+ return hero
+
+
+@app.get("/heroes/", response_model=list[Hero])
+async def read_heroes(session: AsyncSession = Depends(get_session)):
+ result = await session.exec(select(Hero))
+ heroes = result.all()
+ return heroes
diff --git a/docs_src/tutorial/async/tutorial001_py39.py b/docs_src/tutorial/async/tutorial001_py39.py
new file mode 100644
index 0000000000..ddf9eaccaf
--- /dev/null
+++ b/docs_src/tutorial/async/tutorial001_py39.py
@@ -0,0 +1,52 @@
+from typing import List, Optional
+
+from fastapi import Depends, FastAPI
+from sqlalchemy.ext.asyncio import create_async_engine
+from sqlmodel import Field, SQLModel, select
+from sqlmodel.ext.asyncio import AsyncSession
+
+
+class Hero(SQLModel, table=True):
+ id: Optional[int] = Field(default=None, primary_key=True)
+ name: str = Field(index=True)
+ secret_name: str
+ age: Optional[int] = Field(default=None, index=True)
+
+
+sqlite_file_name = "database.db"
+sqlite_url = f"sqlite+aiosqlite:///{sqlite_file_name}"
+
+engine = create_async_engine(sqlite_url, echo=True)
+
+
+async def init_db():
+ async with engine.begin() as conn:
+ await conn.run_sync(SQLModel.metadata.create_all)
+
+
+app = FastAPI()
+
+
+@app.on_event("startup")
+async def on_startup():
+ await init_db()
+
+
+async def get_session():
+ async with AsyncSession(engine) as session:
+ yield session
+
+
+@app.post("/heroes/", response_model=Hero)
+async def create_hero(hero: Hero, session: AsyncSession = Depends(get_session)):
+ session.add(hero)
+ await session.commit()
+ await session.refresh(hero)
+ return hero
+
+
+@app.get("/heroes/", response_model=List[Hero])
+async def read_heroes(session: AsyncSession = Depends(get_session)):
+ result = await session.exec(select(Hero))
+ heroes = result.all()
+ return heroes
diff --git a/mkdocs.yml b/mkdocs.yml
index b89516e024..05979d5227 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -127,6 +127,7 @@ nav:
- tutorial/fastapi/tests.md
- Advanced User Guide:
- advanced/index.md
+ - tutorial/async/index.md
- advanced/decimal.md
- advanced/uuid.md
- Resources:
diff --git a/sqlmodel/ext/asyncio/__init__.py b/sqlmodel/ext/asyncio/__init__.py
index e69de29bb2..51b71e7e98 100644
--- a/sqlmodel/ext/asyncio/__init__.py
+++ b/sqlmodel/ext/asyncio/__init__.py
@@ -0,0 +1 @@
+from .session import AsyncSession as AsyncSession