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();