---
# Library API Documentation - Manual API Docs Example | Oxidoc (lib/index)
---
title: Library API Documentation - Manual API Docs Example | Oxidoc
description: Example of writing manual API documentation with Oxidoc. Create library reference pages with full control over layout alongside auto-generated OpenAPI docs.
---
# Library API Documentation
This section demonstrates how to write **manual API documentation** using Oxidoc. Unlike the auto-generated [API Reference](/api) which parses an OpenAPI spec, manual API docs give you full control over the content and layout.
Manual API docs are ideal for:
- Libraries and packages (Rust crates, npm packages, Python modules)
- Projects with only a few endpoints that don't need a full OpenAPI spec
- Internal tools where you want richer explanations than a spec provides
- SDK documentation with code examples in multiple languages
## How It Works
This entire `/lib` section lives in a separate `lib/` content directory — independent from the main `docs/` directory. Oxidoc supports multiple content directories, each with its own sidebar and URL path.
Here is the config that powers this section:
[routing]
header_links = [
{ label = "Docs", href = "/" },
{ label = "Lib", href = "/lib" },
{ label = "API", href = "/api" },
]
navigation = [
{ path = "/", dir = "docs", groups = [...] },
{ path = "/lib", dir = "lib", groups = [
{ group = "Library API", pages = [
"index",
"manual-api",
"library-example",
] },
] },
{ path = "/api", openapi = "./openapi.yaml" },
]
Key points:
- `dir = "lib"` tells Oxidoc to scan the `lib/` directory for `.rdx` files
- Each content directory gets its own sidebar navigation
- Search works across **all** sections — docs, lib, and API pages are all searchable together
- You can add as many content directories as you need
Learn how to write API documentation manually in RDX.
A complete example documenting a library's public API.
---
# Writing Manual API Docs - Hand-Crafted References (lib/manual-api)
---
title: Writing Manual API Docs - Hand-Crafted References
description: Learn how to write manual API documentation in Oxidoc using RDX components. Create structured reference pages with code examples and parameter tables.
---
# Writing Manual API Docs
When your project doesn't have an OpenAPI spec — or when you want more control over how your API is presented — you can write API documentation manually using RDX components. This page shows the patterns you can use.
## Documenting an Endpoint
Use a combination of headings, code blocks, tables, and callouts to describe each endpoint clearly. Here is a complete example of a single endpoint documented manually:
---
### `POST /api/users` Stable
Create a new user account.
**Parameters**
| Parameter | Type | Required | Description |
|:----------|:-----|:---------|:------------|
| `name` | string | Yes | The user's display name |
| `email` | string | Yes | Email address |
| `role` | string | No | Role assignment (default: `"member"`) |
**Request body**
```json
{
"name": "Alice",
"email": "alice@example.com",
"role": "admin"
}
```
**Response (201 Created)**
```json
{
"id": 42,
"name": "Alice",
"email": "alice@example.com",
"role": "admin",
"created_at": "2025-01-15T10:30:00Z"
}
```
**Code examples**
```bash
curl -X POST https://api.example.com/api/users \
-H "Content-Type: application/json" \
-d '{"name": "Alice", "email": "alice@example.com"}'
```
```javascript
const response = await fetch("https://api.example.com/api/users", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "Alice", email: "alice@example.com" }),
});
```
```python
import requests
response = requests.post(
"https://api.example.com/api/users",
json={"name": "Alice", "email": "alice@example.com"},
)
```
This endpoint requires a valid API key in the `Authorization` header.
---
## Patterns Used Above
The example above uses these RDX features — all of which you can see in action on this page:
- **Headings** with inline code for the method and path
- **Badge** component to show endpoint status (Stable, Beta, Deprecated)
- **Tables** for parameters
- **Fenced code blocks** for request/response JSON
- **Tabs** for multi-language code examples
- **Callout** for authentication notes
## Organizing API Docs
For larger APIs, use sidebar groups to organize endpoints by resource:
{ path = "/lib", dir = "lib", groups = [
{ group = "Overview", pages = ["index"] },
{ group = "Users", pages = ["users/create", "users/list", "users/get"] },
{ group = "Projects", pages = ["projects/create", "projects/list"] },
] }
Each page in `lib/users/create.rdx` documents one endpoint or a closely related set of endpoints.
## Tips
Use Badge components to mark endpoint status:
Stable Beta Deprecated
Use Callout blocks to highlight authentication requirements, rate limits, or other important notes that apply to specific endpoints.
---
# Library API Example - Sample Reference Page (lib/library-example)
---
title: Library API Example - Sample Reference Page
description: A complete example of documenting a library's public API with Oxidoc. Demonstrates function signatures, parameter tables, and interactive code samples.
---
# Library API Example
This page shows a complete example of documenting a library's public API using manual RDX documentation. We'll document a fictional `oxidoc-store` key-value storage library.
---
## oxidoc-store
A fast, typed key-value store for Rust applications.
cargo add oxidoc-store
---
## `Store::new`
Create a new in-memory store.
use oxidoc_store::Store;
let store = Store::new();
| Parameter | Type | Description |
|:----------|:-----|:------------|
| _(none)_ | — | Creates an empty store with default settings |
**Returns:** `Store` — A new store instance.
---
## `Store::with_capacity`
Create a store with pre-allocated capacity.
let store = Store::with_capacity(1000);
| Parameter | Type | Description |
|:----------|:-----|:------------|
| `capacity` | `usize` | Number of entries to pre-allocate |
**Returns:** `Store` — A new store with the given capacity.
Use `with_capacity` when you know the approximate number of entries to avoid reallocations.
---
## `Store::set` Stable
Insert or update a key-value pair.
store.set("user:1", User { name: "Alice", age: 30 });
| Parameter | Type | Description |
|:----------|:-----|:------------|
| `key` | `&str` | The key to store under |
| `value` | `impl Serialize` | The value to store (must implement `serde::Serialize`) |
**Returns:** `Option` — The previous value if the key already existed, or `None`.
---
## `Store::get` Stable
Retrieve a value by key.
let user: Option = store.get("user:1");
| Parameter | Type | Description |
|:----------|:-----|:------------|
| `key` | `&str` | The key to look up |
**Returns:** `Option` — The value deserialized to type `T`, or `None` if not found.
---
## `Store::delete`
Remove a key-value pair.
let removed: bool = store.delete("user:1");
| Parameter | Type | Description |
|:----------|:-----|:------------|
| `key` | `&str` | The key to remove |
**Returns:** `bool` — `true` if the key existed and was removed.
---
## `Store::list` Beta
List all keys matching a prefix.
let user_keys: Vec = store.list("user:");
| Parameter | Type | Description |
|:----------|:-----|:------------|
| `prefix` | `&str` | Key prefix to filter by |
**Returns:** `Vec` — All keys starting with the given prefix.
---
## `Store::persist` New
Save the store to disk.
store.persist("./data/store.bin")?;
| Parameter | Type | Description |
|:----------|:-----|:------------|
| `path` | `impl AsRef` | File path to write to |
**Returns:** `Result<()>` — Error if the file cannot be written.
`persist` acquires an exclusive file lock. Do not call from multiple processes simultaneously.
---
## `Store::load`
Load a store from a previously persisted file.
let store = Store::load("./data/store.bin")?;
| Parameter | Type | Description |
|:----------|:-----|:------------|
| `path` | `impl AsRef` | File path to read from |
**Returns:** `Result` — The restored store, or an error if the file is missing or corrupt.
---
## Full Example
use oxidoc_store::Store;
use serde::{Serialize, Deserialize};
fn main() -> Result<(), Box> {
let store = Store::new();
// Insert data
store.set("config:theme", "dark");
store.set("config:lang", "en");
// Retrieve
let theme: Option = store.get("config:theme");
println!("Theme: {:?}", theme);
// List by prefix
let configs = store.list("config:");
println!("Config keys: {:?}", configs);
// Persist to disk
store.persist("./app.store")?;
Ok(())
}