JavaScript · API · Просунутий
Клас API-клієнта
Обгорніть API у багаторазовий клас для чистого та підтримуваного коду на fetch.
Короткий вступ до теми та пояснення перед вправами (вправи нижче):
Побудова APIClient крок за кроком
#Довідник: сесія та клас клієнта
#Вправи:
Базовий APIClient із login
#Створіть клас `APIClient` з `constructor`, що встановлює `this.baseUrl` і `this.token = null`, та методом `login(username, password)`, що отримує токен і зберігає його. Перевірте, викликавши `login()` і виведіть `client.token` (перші 20 символів).
class APIClient {
constructor() {
// встановити baseUrl і token
}
async login(username, password) {
// отримати токен з /api/auth/token/ і зберегти в this.token
}
}
async function main() {
const client = new APIClient();
await client.login('YOUR_USERNAME', 'YOUR_PASSWORD');
console.log(client.token.slice(0, 20));
}
main();
Рішення
class APIClient {
constructor() {
this.baseUrl = 'https://apilearn.tukas.dev';
this.token = null;
}
async login(username, password) {
const data = await fetch(`${this.baseUrl}/api/auth/token/`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password }),
}).then(r => r.json());
this.token = data.token;
}
}
async function main() {
const client = new APIClient();
await client.login('YOUR_USERNAME', 'YOUR_PASSWORD');
console.log(client.token.slice(0, 20));
}
main();
Додати метод _fetch()
#Додайте метод `_fetch(path, options = {})`, який автоматично будує заголовки авторизації та викликає `fetch`. Потім додайте `getProfile()`, що використовує `_fetch`. Перевірте обидва.
class APIClient {
constructor() {
this.baseUrl = 'https://apilearn.tukas.dev';
this.token = null;
}
async login(username, password) {
const data = await fetch(`${this.baseUrl}/api/auth/token/`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password }),
}).then(r => r.json());
this.token = data.token;
}
async _fetch(path, options = {}) {
// побудувати заголовки з авторизацією, викликати fetch
}
async getProfile() {
// використати _fetch
}
}
async function main() {
const client = new APIClient();
await client.login('YOUR_USERNAME', 'YOUR_PASSWORD');
const profile = await client.getProfile();
console.log(profile.username);
}
main();
Рішення
class APIClient {
constructor() {
this.baseUrl = 'https://apilearn.tukas.dev';
this.token = null;
}
async login(username, password) {
const data = await fetch(`${this.baseUrl}/api/auth/token/`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password }),
}).then(r => r.json());
this.token = data.token;
}
async _fetch(path, options = {}) {
const headers = {
'Content-Type': 'application/json',
...(this.token ? { 'Authorization': `Token ${this.token}` } : {}),
...options.headers,
};
return fetch(`${this.baseUrl}${path}`, { ...options, headers });
}
async getProfile() {
return this._fetch('/api/users/profile/').then(r => r.json());
}
}
async function main() {
const client = new APIClient();
await client.login('YOUR_USERNAME', 'YOUR_PASSWORD');
const profile = await client.getProfile();
console.log(profile.username);
}
main();
Додати getProducts()
#Додайте метод `getProducts(params = {})`, що приймає об'єкт параметрів і повертає масив `results`. Перевірте без параметрів та з `{ category: "chairs" }`.
class APIClient {
constructor() {
this.baseUrl = 'https://apilearn.tukas.dev';
this.token = null;
}
async login(username, password) {
const data = await fetch(`${this.baseUrl}/api/auth/token/`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password }),
}).then(r => r.json());
this.token = data.token;
}
async _fetch(path, options = {}) {
const headers = {
'Content-Type': 'application/json',
...(this.token ? { 'Authorization': `Token ${this.token}` } : {}),
...options.headers,
};
return fetch(`${this.baseUrl}${path}`, { ...options, headers });
}
async getProducts(params = {}) {
// використати URLSearchParams і _fetch, повернути results
}
}
async function main() {
const client = new APIClient();
await client.login('YOUR_USERNAME', 'YOUR_PASSWORD');
console.log((await client.getProducts()).length);
console.log((await client.getProducts({ category: 'chairs' })).length);
}
main();
Рішення
class APIClient {
constructor() {
this.baseUrl = 'https://apilearn.tukas.dev';
this.token = null;
}
async login(username, password) {
const data = await fetch(`${this.baseUrl}/api/auth/token/`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password }),
}).then(r => r.json());
this.token = data.token;
}
async _fetch(path, options = {}) {
const headers = {
'Content-Type': 'application/json',
...(this.token ? { 'Authorization': `Token ${this.token}` } : {}),
...options.headers,
};
return fetch(`${this.baseUrl}${path}`, { ...options, headers });
}
async getProducts(params = {}) {
const qs = new URLSearchParams(params);
return this._fetch(`/api/products/?${qs}`).then(r => r.json()).then(d => d.results);
}
}
async function main() {
const client = new APIClient();
await client.login('YOUR_USERNAME', 'YOUR_PASSWORD');
console.log((await client.getProducts()).length);
console.log((await client.getProducts({ category: 'chairs' })).length);
}
main();
Повний APIClient
#Побудуйте повний APIClient із методами: `login()`, `_fetch()`, `getProducts(params)`, `getProfile()`, `addToCart(slug, qty)`, `clearCart()`. Потім послідовно використайте всі методи.
class APIClient {
// реалізувати всі методи
}
async function main() {
const client = new APIClient();
await client.login('YOUR_USERNAME', 'YOUR_PASSWORD');
const products = await client.getProducts();
console.log('Товарів:', products.length);
const item = await client.addToCart(products[0].slug);
console.log('Id позиції кошика:', item.id);
const profile = await client.getProfile();
console.log('Користувач:', profile.username);
await client.clearCart();
console.log('Кошик очищено');
}
main();
Рішення
class APIClient {
constructor() {
this.baseUrl = 'https://apilearn.tukas.dev';
this.token = null;
}
async _fetch(path, options = {}) {
const headers = {
'Content-Type': 'application/json',
...(this.token ? { 'Authorization': `Token ${this.token}` } : {}),
...options.headers,
};
return fetch(`${this.baseUrl}${path}`, { ...options, headers });
}
async login(username, password) {
const data = await this._fetch('/api/auth/token/', {
method: 'POST',
body: JSON.stringify({ username, password }),
}).then(r => r.json());
this.token = data.token;
}
async getProducts(params = {}) {
const qs = new URLSearchParams(params);
return this._fetch(`/api/products/?${qs}`).then(r => r.json()).then(d => d.results);
}
async getProfile() {
return this._fetch('/api/users/profile/').then(r => r.json());
}
async addToCart(productSlug, quantity = 1) {
return this._fetch('/api/cart/items/', {
method: 'POST',
body: JSON.stringify({ product_slug: productSlug, quantity }),
}).then(r => r.json());
}
async clearCart() {
await this._fetch('/api/cart/items/', { method: 'DELETE' });
}
}
async function main() {
const client = new APIClient();
await client.login('YOUR_USERNAME', 'YOUR_PASSWORD');
const products = await client.getProducts();
console.log('Products:', products.length);
const item = await client.addToCart(products[0].slug);
console.log('Cart item id:', item.id);
const profile = await client.getProfile();
console.log('User:', profile.username);
await client.clearCart();
console.log('Cart cleared');
}
main();
Додати метод placeOrder()
#Розширте APIClient методом `placeOrder(phoneNumber)`. Потім використайте клієнт для: входу, додавання товару, оформлення замовлення та виведення `id` і `status` замовлення.
class APIClient {
constructor() {
this.baseUrl = 'https://apilearn.tukas.dev';
this.token = null;
}
async _fetch(path, options = {}) {
const headers = {
'Content-Type': 'application/json',
...(this.token ? { 'Authorization': `Token ${this.token}` } : {}),
...options.headers,
};
return fetch(`${this.baseUrl}${path}`, { ...options, headers });
}
async login(username, password) {
const data = await this._fetch('/api/auth/token/', {
method: 'POST',
body: JSON.stringify({ username, password }),
}).then(r => r.json());
this.token = data.token;
}
async getProducts(params = {}) {
const qs = new URLSearchParams(params);
return this._fetch(`/api/products/?${qs}`).then(r => r.json()).then(d => d.results);
}
async addToCart(productSlug, quantity = 1) {
return this._fetch('/api/cart/items/', {
method: 'POST',
body: JSON.stringify({ product_slug: productSlug, quantity }),
}).then(r => r.json());
}
async placeOrder(phoneNumber) {
// POST /api/orders/ і повернути результат
}
}
async function main() {
const client = new APIClient();
await client.login('YOUR_USERNAME', 'YOUR_PASSWORD');
const products = await client.getProducts();
await client.addToCart(products[0].slug);
const order = await client.placeOrder('+1234567890');
console.log(order.id);
console.log(order.status);
}
main();
Рішення
class APIClient {
constructor() {
this.baseUrl = 'https://apilearn.tukas.dev';
this.token = null;
}
async _fetch(path, options = {}) {
const headers = {
'Content-Type': 'application/json',
...(this.token ? { 'Authorization': `Token ${this.token}` } : {}),
...options.headers,
};
return fetch(`${this.baseUrl}${path}`, { ...options, headers });
}
async login(username, password) {
const data = await this._fetch('/api/auth/token/', {
method: 'POST',
body: JSON.stringify({ username, password }),
}).then(r => r.json());
this.token = data.token;
}
async getProducts(params = {}) {
const qs = new URLSearchParams(params);
return this._fetch(`/api/products/?${qs}`).then(r => r.json()).then(d => d.results);
}
async addToCart(productSlug, quantity = 1) {
return this._fetch('/api/cart/items/', {
method: 'POST',
body: JSON.stringify({ product_slug: productSlug, quantity }),
}).then(r => r.json());
}
async placeOrder(phoneNumber) {
return this._fetch('/api/orders/', {
method: 'POST',
body: JSON.stringify({ phone_number: phoneNumber }),
}).then(r => r.json());
}
}
async function main() {
const client = new APIClient();
await client.login('YOUR_USERNAME', 'YOUR_PASSWORD');
const products = await client.getProducts();
await client.addToCart(products[0].slug);
const order = await client.placeOrder('+1234567890');
console.log(order.id);
console.log(order.status);
}
main();