Skip to content

Commit 340a97a

Browse files
authored
Add Learning and Examples section (#513)
* Add Learning and Examples section
1 parent c1edaf5 commit 340a97a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+3844
-14
lines changed
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# Building Your First API
2+
3+
In this guide, we’ll walk you through how to build a basic API using Esmerald. You’ll learn how to define routes,
4+
use path and query parameters, and return structured responses.
5+
6+
## Prerequisites
7+
8+
- Esmerald installed: `pip install esmerald[standard]`
9+
- Familiarity with basic Python and HTTP concepts
10+
11+
---
12+
13+
## Defining Your First Routes
14+
15+
In Esmerald, routes are created using decorators like `@get`, `@post`, etc., which map to HTTP methods.
16+
17+
```python
18+
from esmerald import Esmerald, get
19+
20+
@get("/")
21+
def home() -> dict:
22+
return {"message": "Welcome to your API!"}
23+
24+
app = Esmerald(routes=[home])
25+
```
26+
27+
Start the server with:
28+
```bash
29+
uvicorn main:app --reload
30+
```
31+
32+
Visit `http://127.0.0.1:8000/` to see your message.
33+
34+
---
35+
36+
## Path Parameters
37+
38+
Path parameters let you capture parts of the URL.
39+
40+
```python
41+
from esmerald import get
42+
43+
@get("/users/{user_id}")
44+
def get_user(user_id: int) -> dict:
45+
return {"user_id": user_id, "name": f"User {user_id}"}
46+
```
47+
48+
- `http://127.0.0.1:8000/users/42``{ "user_id": 42, "name": "User 42" }`
49+
50+
Path parameters are automatically converted to the specified type (`int` in this case).
51+
52+
---
53+
54+
## Query Parameters
55+
56+
Query parameters are passed using `?key=value` syntax.
57+
58+
```python
59+
from esmerald import get, Query
60+
61+
@get("/search")
62+
def search(term: str = Query(...), limit: int = Query(10)) -> dict:
63+
return {"term": term, "limit": limit}
64+
```
65+
66+
- `http://127.0.0.1:8000/search?term=esmerald&limit=5`
67+
68+
You’ll get:
69+
```json
70+
{"term": "esmerald", "limit": 5}
71+
```
72+
73+
---
74+
75+
## JSON Body (POST requests)
76+
77+
To handle JSON body data, just declare a parameter with a type.
78+
79+
```python
80+
from pydantic import BaseModel
81+
from esmerald import post
82+
83+
class User(BaseModel):
84+
name: str
85+
age: int
86+
87+
@post("/users")
88+
def create_user(user: User) -> dict:
89+
return {"created": True, "user": user.model_dump()}
90+
```
91+
92+
Curl test:
93+
```bash
94+
curl -X POST http://localhost:8000/users -H "Content-Type: application/json" \
95+
-d '{"name": "Alice", "age": 30}'
96+
```
97+
98+
---
99+
100+
## Route Summary
101+
102+
| Decorator | HTTP Method |
103+
|----------|-------------|
104+
| `@get()` | GET |
105+
| `@post()`| POST |
106+
| `@put()` | PUT |
107+
| `@patch()`| PATCH |
108+
| `@delete()`| DELETE |
109+
| `@options()`| OPTIONS |
110+
| `@trace()` | TRACE |
111+
112+
---
113+
114+
## What's Next?
115+
116+
Now that you know how to build basic routes, let’s move into **request validation, response models, and data schemas**.
117+
118+
👉 Continue to [the next section](03-request-and-response-models.md) to start defining schemas using Pydantic and working with structured input/output.
119+
120+
---
121+
122+
You're on your way to mastering Esmerald APIs! 💚
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# Request and Response Models
2+
3+
In this section, you'll learn how to validate and structure input and output using Pydantic models in Esmerald.
4+
5+
## Why Use Models?
6+
7+
Pydantic models offer:
8+
- **Automatic validation** of incoming request data
9+
- **Auto-generated documentation**
10+
- **Clear structure** for responses
11+
12+
---
13+
14+
## Request Model Example
15+
16+
Define a Pydantic model to validate incoming data:
17+
18+
```python
19+
from pydantic import BaseModel
20+
from esmerald import post
21+
22+
class CreateUserRequest(BaseModel):
23+
name: str
24+
age: int
25+
26+
@post("/users")
27+
def create_user(data: CreateUserRequest) -> dict:
28+
return {"message": f"Created user {data.name} who is {data.age} years old"}
29+
```
30+
31+
When a POST request is made to `/users`, Esmerald:
32+
- Automatically parses the JSON
33+
- Validates it against `CreateUserRequest`
34+
- Injects the model instance into the handler
35+
36+
Invalid request? Esmerald returns a 422 with helpful details.
37+
38+
---
39+
40+
## Response Model Example
41+
42+
Define a model to **structure** the output:
43+
44+
```python
45+
from pydantic import BaseModel
46+
from esmerald import get
47+
48+
class UserResponse(BaseModel):
49+
id: int
50+
name: str
51+
52+
@get("/users/{user_id}", response_model=UserResponse)
53+
def get_user(user_id: int) -> UserResponse:
54+
return UserResponse(id=user_id, name=f"User {user_id}")
55+
```
56+
57+
- `response_model` controls the shape of the output in docs and response
58+
- Esmerald converts the returned model into JSON automatically
59+
60+
---
61+
62+
## Nested Models
63+
64+
```python
65+
class Address(BaseModel):
66+
city: str
67+
country: str
68+
69+
class UserProfile(BaseModel):
70+
name: str
71+
address: Address
72+
73+
@get("/profile")
74+
def get_profile() -> UserProfile:
75+
return UserProfile(name="Alice", address=Address(city="Berlin", country="Germany"))
76+
```
77+
78+
---
79+
80+
## List of Models
81+
82+
Returning a list? Just use `list[Model]` or `List[Model]`.
83+
84+
```python
85+
@get("/users", response_model=list[UserResponse])
86+
def list_users() -> list[UserResponse]:
87+
return [UserResponse(id=1, name="Alice"), UserResponse(id=2, name="Bob")]
88+
```
89+
90+
---
91+
92+
## What's Next?
93+
94+
You've now learned how to:
95+
- Use request models for validation
96+
- Use response models for output structure
97+
- Handle nesting and collections
98+
99+
👉 Continue to [the next section](04-handling-errors.md) to learn about custom error handling, exceptions, and status codes in Esmerald.
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# Handling Errors
2+
3+
In this section, you'll learn how to handle errors and exceptions in Esmerald.
4+
5+
Esmerald provides robust error-handling capabilities out of the box, and also allows you to define your own.
6+
7+
---
8+
9+
## Raising HTTP Exceptions
10+
11+
Use `HTTPException` to raise an error with a specific status code and optional detail message:
12+
13+
```python
14+
from esmerald import get, HTTPException
15+
16+
@get("/items/{item_id}")
17+
def get_item(item_id: int) -> dict:
18+
if item_id != 1:
19+
raise HTTPException(status_code=404, detail="Item not found")
20+
return {"item_id": item_id}
21+
```
22+
23+
This returns a response like:
24+
25+
```json
26+
{
27+
"detail": "Item not found"
28+
}
29+
```
30+
31+
---
32+
33+
## Built-in Error Responses
34+
35+
Esmerald automatically returns helpful responses for:
36+
37+
- Validation errors (status code `422`)
38+
- Missing routes (`404`)
39+
- Internal server errors (`500`)
40+
41+
Example: Sending an invalid body to a route expecting a `Pydantic` model returns:
42+
43+
```json
44+
{
45+
"detail": [
46+
{
47+
"loc": ["body", "name"],
48+
"msg": "field required",
49+
"type": "value_error.missing"
50+
}
51+
]
52+
}
53+
```
54+
55+
---
56+
57+
## Custom Exception Handlers
58+
59+
You can define a custom exception handler for your own exception classes:
60+
61+
```python
62+
from esmerald import Esmerald, Request, HTTPException, ExceptionHandler
63+
64+
class MyCustomError(Exception):
65+
pass
66+
67+
def my_custom_handler(request: Request, exc: MyCustomError):
68+
return {"detail": "Something custom went wrong!"}
69+
70+
app = Esmerald(
71+
routes=[],
72+
exception_handlers={MyCustomError: my_custom_handler}
73+
)
74+
```
75+
76+
Raise it like so:
77+
78+
```python
79+
@get("/fail")
80+
def fail():
81+
raise MyCustomError()
82+
```
83+
84+
---
85+
86+
## Returning Custom Status Codes
87+
88+
Just set `status_code` when raising `HTTPException`:
89+
90+
```python
91+
from esmerald import post, HTTPException
92+
93+
@post("/login")
94+
def login() -> None:
95+
raise HTTPException(status_code=401, detail="Invalid credentials")
96+
```
97+
98+
---
99+
100+
## Returning Custom Error Structures
101+
102+
You can return custom error payloads using a response directly:
103+
104+
```python
105+
from esmerald.responses import JSONResponse
106+
107+
@get("/custom-error")
108+
def custom_error() -> None:
109+
return JSONResponse({"error": "custom"}, status_code=400)
110+
```
111+
112+
---
113+
114+
## What's Next?
115+
116+
You now know how to:
117+
118+
- Raise HTTP exceptions
119+
- Customize error messages and handlers
120+
- Return proper status codes
121+
122+
👉 Continue to [the next section](05-routing.md) to learn how to organize your routes, include routers, and build modular APIs.

0 commit comments

Comments
 (0)