Как сделать свой api на python
Перейти к содержимому

Как сделать свой api на python

  • автор:

Как сделать свой api на python

Рассмотренного в прошлых темах материала достаточно для создания примитивного приложения. В этой теме попробуем реализовать простейшее приложение Web API в стиле REST. Архитектура REST предполагает применение следующих методов или типов запросов HTTP для взаимодействия с сервером, где каждый тип запроса отвечает за определенное действие:

  • GET (получение данных)
  • POST (добавление данных)
  • PUT (изменение данных)
  • DELETE (удаление данных)

Для каждого из этих типов запросов класс FastAPI предоставляет соответствующие методы. Рассмотрим, как мы можем реализовать с помощью этих методов простейший API. Пусть у нас будет следующий проект:

Создание Web API на FastAPI и Python

Создание сервера

В файле main.py определим следующий код:

import uuid from fastapi import FastAPI, Body, status from fastapi.responses import JSONResponse, FileResponse class Person: def __init__(self, name, age): self.name = name self.age = age self.id = str(uuid.uuid4()) # условная база данных - набор объектов Person people = [Person("Tom", 38), Person("Bob", 42), Person("Sam", 28)] # для поиска пользователя в списке people def find_person(id): for person in people: if person.id == id: return person return None app = FastAPI() @app.get("/") async def main(): return FileResponse("public/index.html") @app.get("/api/users") def get_people(): return people @app.get("/api/users/") def get_person(id): # получаем пользователя по id person = find_person(id) print(person) # если не найден, отправляем статусный код и сообщение об ошибке if person==None: return JSONResponse( status_code=status.HTTP_404_NOT_FOUND, content= < "message": "Пользователь не найден" >) #если пользователь найден, отправляем его return person @app.post("/api/users") def create_person(data = Body()): person = Person(data["name"], data["age"]) # добавляем объект в список people people.append(person) return person @app.put("/api/users") def edit_person(data = Body()): # получаем пользователя по id person = find_person(data["id"]) # если не найден, отправляем статусный код и сообщение об ошибке if person == None: return JSONResponse( status_code=status.HTTP_404_NOT_FOUND, content= < "message": "Пользователь не найден" >) # если пользователь найден, изменяем его данные и отправляем обратно клиенту person.age = data["age"] person.name = data["name"] return person @app.delete("/api/users/") def delete_person(id): # получаем пользователя по id person = find_person(id) # если не найден, отправляем статусный код и сообщение об ошибке if person == None: return JSONResponse( status_code=status.HTTP_404_NOT_FOUND, content= < "message": "Пользователь не найден" >) # если пользователь найден, удаляем его people.remove(person) return person

Разберем в общих чертах этот код. Прежде всего для представления данных, с которыми мы будем работать, определяем класс Person .

class Person: def __init__(self, name, age): self.name = name self.age = age self.id = str(uuid.uuid4())

Этот класс содержит три атрибута. Два атрибута — name и age будут представлять имя и возраст пользователя и будут устанавливаться через конструктор. А третий атрибут — id будет служить для уникальной идентификации данного объекта и будет хранить значение guid. Для генерации guid применяется функция uuid.uuid4() из пакета uuid . В конструкторе Person сгенерированный guid преобразуется в строку и присваивается атрибуту id.

Для хранения данных в приложении определим список people, который будет выполнять роль условной базы данных и будет хранить объекты Person.

people = [Person("Tom", 38), Person("Bob", 42), Person("Sam", 28)]

Для поиска объекта Person в этом списке определена вспомогательная функция find_person() .

При обращении к корню веб-приложения, то есть по пути «/», оно будет отправлять в ответ файл index.html , то есть веб-страницу, посредством которой пользователь сможет взаимодействовать с сервером:

@app.get("/") def main(): return FileResponse("public/index.html")

Далее определяются функции, которые собственно и представляют API. Вначале определяется функция, которая обрабатывает запрос типа GET по пути «api/users»:

@app.get("/api/users") def get_people(): return people

Запрос GET предполагает получение объектов, и в данном случае отправляем выше определенный список объектов Person.

Когда клиент обращается к приложению для получения одного объекта по id в запрос типа GET по адресу «api/users/», то срабатывает другая функция:

@app.get("/api/users/") def get_person(id): person = find_person(id) if person==None: return JSONResponse( status_code=status.HTTP_404_NOT_FOUND, content= < "message": "Пользователь не найден" >) return person

Здесь через параметр id получаем из пути запроса идентификатор объекта Person и по этому идентификатору ищем нужный объект в списке people. Если объект по id не был найден, то возвращаем с помощью класса JSONResponse статусный код 404 с некоторым сообщением в формате JSON. Если объект найден, то отправляем найденный объект клиенту.

При получении запроса типа DELETE по маршруту «/api/users/» срабатывает другая функция:

@app.delete("/api/users/") def delete_person(id): person = find_person(id) if person == None: return JSONResponse( status_code=status.HTTP_404_NOT_FOUND, content= < "message": "Пользователь не найден" >) people.remove(person) return person

Здесь действует аналогичная логика — если объект по id не найден, отправляет статусный код 404. Если же объект найден, то удаляем его из списка и посылаем клиенту.

При получении запроса с методом POST по адресу «/api/users» срабатывает следующая функция:

@app.post("/api/users") def create_person(data = Body()): person = Person(data["name"], data["age"]) people.append(person) return person

Запрос типа POST предполагает передачу приложению отправляемых данных. Причем мы ожидаем, что клиент отправит данные, которые содержат значения name и age. Для краткости мы пока опускаем валидацию входных данных. И для получения данных из тела запроса с помощью класса Body получаем данные в параметр data и затем используем данные из этого параметра для создания объекта Person. Затем созданный объект добавляется в список people и отправляется назад клиенту.

Если приложению приходит PUT-запрос по адресу «/api/users», то аналогичным образом получаем отправленные клиентом данные в виде объекта Person и пытаемся найти подобный объект в списке people. Если объект не найден, отправляем статусный код 404. Если объект найден, то изменяем его данные и отправляем обратно клиенту:

@app.put("/api/users") def edit_person(data = Body()): person = find_person(data["id"]) if person == None: return JSONResponse( status_code=status.HTTP_404_NOT_FOUND, content= < "message": "Пользователь не найден" >) person.age = data["age"] person.name = data["name"] return person

Таким образом, мы определили простейший API. Теперь добавим код клиента.

Определение клиента

Теперь в проекте определим папку public , в которую добавим новый файл index.html

Определим в файле index.html следующим код для взаимодействия с сервером FastAPI:

    METANIT.COM td button 

Список пользователей

Имя:

Возраст:

ИмяВозраст

Основная логика здесь заключена в коде javascript. При загрузке страницы в браузере получаем все объекты из БД с помощью функции getUsers() :

async function getUsers() < // отправляет запрос и получаем ответ const response = await fetch("/api/users", < method: "GET", headers: < "Accept": "application/json" >>); // если запрос прошел нормально if (response.ok === true) < // получаем данные const users = await response.json(); const rows = document.querySelector("tbody"); // добавляем полученные элементы в таблицу users.forEach(user =>rows.append(row(user))); > >

Для добавления строк в таблицу используется функция row() , которая возвращает строку. В этой строке будут определены ссылки для изменения и удаления пользователя.

Ссылка для изменения пользователя с помощью функции getUser() получает с сервера выделенного пользователя:

async function getUser(id) < const response = await fetch(`/api/users/$`, < method: "GET", headers: < "Accept": "application/json" >>); if (response.ok === true) < const user = await response.json(); document.getElementById("userId").value = user.id; document.getElementById("userName").value = user.name; document.getElementById("userAge").value = user.age; >else < // если произошла ошибка, получаем сообщение об ошибке const error = await response.json(); console.log(error.message); // и выводим его на консоль >>

И выделенный пользователь добавляется в форму над таблицей. Эта же форма применяется и для добавления объекта. С помощью скрытого поля, которое хранит id пользователя, мы можем узнать, какое действие выполняется — добавление или редактирование. Если id не установлен (равен пустой строке), то выполняется функция createUser, которая отправляет данные в POST-запросе:

async function createUser(userName, userAge) < const response = await fetch("api/users", < method: "POST", headers: < "Accept": "application/json", "Content-Type": "application/json" >, body: JSON.stringify(< name: userName, age: parseInt(userAge, 10) >) >); if (response.ok === true) < const user = await response.json(); document.querySelector("tbody").append(row(user)); >else < const error = await response.json(); console.log(error.message); >>

Если же ранее пользователь был загружен на форму, и в скрытом поле сохранился его id, то выполняется функция editUser, которая отправляет PUT-запрос:

async function editUser(userId, userName, userAge) < const response = await fetch("api/users", < method: "PUT", headers: < "Accept": "application/json", "Content-Type": "application/json" >, body: JSON.stringify(< id: userId, name: userName, age: parseInt(userAge, 10) >) >); if (response.ok === true) < const user = await response.json(); document.querySelector(`tr[data-rowid='$']`).replaceWith(row(user)); > else < const error = await response.json(); console.log(error.message); >>

И функция deleteUser() посылает на сервер запрос типа DELETE на удаление пользователя, и при успешном удалении на сервере удаляет объект по id из списка объектов Person.

Теперь запустим проект, и по умолчанию приложение отправит браузеру веб-страницу index.html , которая загрузит список объектов:

взаимодействие javascript с Web API на FastAPI и Python

После этого мы сможем выполнять все базовые операции с пользователями — получение, добавление, изменение, удаление. Например, добавим нового пользователя:

Как создать API с помощью Python и Django

API, Application Programming Interface (программный интерфейс приложения), — очень широкое понятие в бэкенд-разработке. Тот API, который мы рассмотрим сегодня, представляет собой сайт без фронтенд-составляющей. Вместо рендеринга HTML-страниц, бэкенд возвращает данные в JSON формате для использования их в нативных или веб-приложениях. Самое пристальное внимание при написании API (как и при написании вебсайтов) нужно обратить на то, как он будет использоваться. Сегодня мы поговорим о том, как использовать Django для создания API для простого приложения со списком дел.

Нам понадобится несколько инструментов. Для выполнения всех шагов я бы рекомендовал вам, вместе с данной статьей, клонировать вот этот учебный проект из GitHub-репозитория. Также вы должны установить Python 3, Django 2.2, и djangorestframework 3.9 (из репозитория запустите pip install -r requirements.txt для установки библиотек). Если не все будет понятно с установкой Django, можно воспользоваться официальной документацией. Также вам нужно будет скачать бесплатную версию Postman. Postman – отличный инструмент для разработки и тестирования API, но в этой статье мы воспользуемся лишь его самыми базовыми функциями.

Для начала откройте папку taskmanager , содержащую manage.py , и выполните python manage.py migrate в командной строке, чтобы применить миграции баз данных к дефолтной sqlite базе данных Django. Создайте суперпользователя с помощью python manage.py createsuperuser и не забудьте записать имя пользователя и пароль. Они понадобятся нам позже. Затем выполните python manage.py runserver для взаимодействия с API.

Вы можете работать с API двумя способами: просматривая фронтенд Django REST фреймворка или выполняя http-запросы. Откройте браузер и перейдите к 127.0.0.1:8000 или к localhost через порт 8000, где Django-проекты запускаются по умолчанию. Вы увидите веб-страницу со списком доступных конечных точек API. Это важнейший принцип в RESTful подходе к API-разработке: сам API должен показывать пользователям, что доступно и как это использовать.

Сначала давайте посмотрим на функцию api_index в views.py. Она содержит список конечных точек, которые вы посещаете.

 @define_usage(returns='url_usage': 'Dict'>) @api_view(['GET']) @permission_classes((AllowAny,)) def api_index(request): details = <> for item in list(globals().items()): if item[0][0:4] == 'api_': if hasattr(item[1], 'usage'): details[reverse(item[1].__name__)] = item[1].usage return Response(details) 

API функции для каждого представления (view в Django) обернуты тремя декораторами. Мы еще вернемся к @define_usage . @api_view нужен для Django REST фреймворка и отвечает за две вещи: шаблон для веб-страницы, которая в результате получится, и HTTP-метод, поддерживаемый конечной точкой. Чтобы разрешить доступ к этому url без проверки подлинности, @permission_classes , также из Django REST фреймворка, задан как AllowAny . Главная функция API обращается к глобальной области видимости приложения чтобы «собрать» все определенные нами функции. Так как мы добавили к каждой функции представления префикс api_ , мы можем легко их отфильтровать и вернуть словарь, содержащий информацию об их вызовах. Детали вызовов предоставляются пользовательским декоратором, написанным в decorators.py.

 def define_usage(params=None, returns=None): def decorator(function): cls = function.view_class header = None # Нужна ли аутентификация для вызова этого представления? if IsAuthenticated in cls.permission_classes: header = 'Authorization': 'Token String'> # Создаем лист доступных методов, исключая 'OPTIONS' methods = [method.upper() for method in cls.http_method_names if method != 'options'] # Создаем словарь для ответа usage = 'Request Types': methods, 'Headers': header, 'Body': params, 'Returns': returns> # Защита от побочных эффектов @wraps(function) def _wrapper(*args, **kwargs): return function(*args, **kwargs) _wrapper.usage = usage return _wrapper return decorator 

Декоратор — часть синтаксиса, которая позволяет легко определять функции высокого порядка, чтобы добавить функциям представления атрибуты (как классам). Представления на основе функций с декораторами — отличный компромисс между простыми функциями и представлениями на основе классов в Django. Этот декоратор предоставляет четыре информационных элемента: типы запросов, заголовки, параметры и возвращаемое значение каждой функции. Декоратор генерирует заголовок и информацию о методе на основе информации, полученной от других декораторов, прикрепленных к функции, и принимает в качестве входных данных параметры и возвращаемые значения на момент вызова данного декоратора.

Помимо вывода результатов index-запроса, мы также будем использовать наш API для взаимодействия с данными пользователя, так что нам понадобится какой-то способ аутентификации. Если бы это был не просто учебный проект, а что-то посерьезнее, можно было бы реализовать регистрацию пользователей (к тому же, это отличная практика, если вы хотите проверить, насколько вы разобрались с понятиями из этой статьи). Но вместо этого мы просто войдем под учетной записью суперпользователя, которую создали заранее.

 @define_usage(params='username': 'String', 'password': 'String'>, returns='authenticated': 'Bool', 'token': 'Token String'>) @api_view(['POST']) @permission_classes((AllowAny,)) def api_signin(request): try: username = request.data['username'] password = request.data['password'] except: return Response('error': 'Please provide correct username and password'>, status=HTTP_400_BAD_REQUEST) user = authenticate(username=username, password=password) if user is not None: token, _ = Token.objects.get_or_create(user=user) return Response('authenticated': True, 'token': "Token " + token.key>) else: return Response('authenticated': False, 'token': None>) 

Важно отметить, что для того, чтобы идентификация на основе токенов заработала, нужно настроить несколько параметров. Гид по настройке можно найти в файле settings.py учебного проекта.

Чтобы верифицировать пользователя, метод api_signin запрашивает имя пользователя и пароль и использует встроенный в Django метод authenticate . Если предоставленные учетные данные верны, он возвращает токен, позволяющий клиенту получить доступ к защищенным конечным точкам API. Помните о том, что данный токен предоставляет те же права доступа, что и пароль, и поэтому должен надежно храниться в клиентском приложении. Теперь мы наконец можем поработать с Postman. Откройте приложение и используйте его для отправки post-запроса к /signin/, как показано на скриншоте.

Теперь, когда у вас есть токен для вашего пользователя, можно разобраться и с остальными составляющими API. Так что давайте немного отвлечемся и посмотрим на Django-модель, лежащую в основе API.

 class Task(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) #Каждая задача принадлежит только одному пользователю description = models.CharField(max_length=150) #У каждой задачи есть описание due = models.DateField() #У каждой задачи есть дата выполнения, тип datetime.date 

Модель Task представляет собой довольно простой подход к менеджменту задач нашего приложения. Каждый элемент имеет описание, например, «Написать API с помощью Django» и дату выполнения. Задачи также связаны внешним ключом с объектом Django User , что означает, что каждая задача принадлежит только одному конкретному пользователю, но каждый пользователь может иметь неограниченное количество задач или не иметь вовсе. Каждый объект Django также имеет идентификатор, уникальное целое число, которое можно использовать для ссылки на индивидуальные задачи.

Приложения типа этого часто называют CRUD-приложениями, от «Create, Read, Update, Destroy» (Создание, Чтение, Модификация, Удаление), четырех операций, поддерживаемых нашим приложением на объектах Task.

Для начала создадим пустой список задач, связанных с конкретным пользователем. Используйте Postman для создания GET-запроса к /all/, как на скриншоте ниже. Не забудьте добавить токен к заголовкам этого и всех последующих запросов.

 @define_usage(returns='tasks': 'Dict'>) @api_view(['GET']) @authentication_classes((SessionAuthentication, BasicAuthentication, TokenAuthentication)) @permission_classes((IsAuthenticated,)) def api_all_tasks(request): tasks = taskSerializer(request.user.task_set.all(), many=True) return Response('tasks': tasks.data>) 

Функция api_all_tasks довольно проста. Стоит обратить внимание лишь на смену требований к проверке подлинности и классов разрешения на аутентификацию токеном. У нас есть новый декоратор @authentication_classes , позволяющий выполнять как дефолтные методы аутентификации Django REST framework, так и TokenAuthentication . Это позволяет нам ссылаться на все экземпляры User как на request.user , как если бы пользователи залогинились через стандартную Django-сессию. Декоратор @define_usage показывает нам, что api_all_tasks не принимает параметров (в отличие от GET-запроса) и возвращает лишь одну вещь — список задач. Поскольку данная функция возвращает данные в формате JSON (JavaScript Object Notation), мы используем сериализатор, чтобы сообщить Django, как парсить данные для отправки.

 class taskSerializer(serializers.ModelSerializer): class Meta: model = Task fields = ('id', 'description', 'due') 

Эта простая модель определяет данные для класса Task: идентификатор, описание и дату выполнения. Сериализаторы могут добавлять и исключать поля и данные из модели. Например, вот этот сериализатор не возвращает идентификатор пользователя, т.к. он бесполезен для конечного клиента.

 @define_usage(params='description': 'String', 'due_in': 'Int'>, returns='done': 'Bool'>) @api_view(['PUT']) @authentication_classes((SessionAuthentication, BasicAuthentication, TokenAuthentication)) @permission_classes((IsAuthenticated,)) def api_new_task(request): task = Task(user=request.user, description=request.data['description'], due=date.today() + timedelta(days=int(request.data['due_in']))) task.save() return Response('done': True>) 

Теперь нам нужно создать задачу. Для этого используем api_new_task . Обычно для создания объекта в базе данных используется PUT-запрос. Обратите внимание, что этот метод, как и два других, не требует предварительной сериализации данных. Вместо этого мы передаем параметры в конструктор объекта класса Task, их же мы затем сохраним в базу данных. Мы отправляем количество дней для выполнения задачи, так как это гораздо проще, чем пытаться отправить объект Python-класса Date . Затем в API мы сохраняем какую-нибудь дату в далеком будущем. Чтобы увидеть созданный объект, нужно создать запрос к /new/ для создания задачи и повторить запрос к /all/.

 @define_usage(params='task_id': 'Int', 'description': 'String', 'due_in': 'Int'>, returns='done': 'Bool'>) @api_view(['POST']) @authentication_classes((SessionAuthentication, BasicAuthentication, TokenAuthentication)) @permission_classes((IsAuthenticated,)) def api_update_task(request): task = request.user.task_set.get(id=int(request.data['task_id'])) try: task.description = request.data['description'] except: #Обновление описания необязательно pass try: task.due = date.today() + timedelta(days=int(request.data['due_in'])) except: #Обновление даты выполнения необязательно pass task.save() return Response('done': True>) 

Для редактирования только что созданной задачи нужно создать POST-запрос к api_update_task через /update/. Мы включаем task_id для ссылки на правильную задачу из пользовательского task_set . Код здесь будет немного сложнее, т.к. мы хотим иметь возможность обновлять описания и/ или дату выполнения задачи.

 @define_usage(params='task_id': 'Int'>, returns='done': 'Bool'>) @api_view(['DELETE']) @authentication_classes((SessionAuthentication, BasicAuthentication, TokenAuthentication)) @permission_classes((IsAuthenticated,)) def api_delete_task(request): task = request.user.task_set.get(id=int(request.data['task_id'])) task.delete() return Response('done': True>) 

Используйте DELETE-запрос к api_delete_task через /delete/ для удаления задачи. Этот метод работает аналогично функции api_update_task , за исключением того, что вместо изменения задачи он удаляет ее.

Сегодня мы с вами разобрались, как реализовать index-запрос, аутентификацию на основе токенов и четыре основных HTTP-метода для Django API. Вы можете использовать эти знания для поддержки любых веб- и нативных мобильных приложений или для разработки публичного API для обмена данными. Не стесняйтесь клонировать учебный проект, содержащий весь код, представленный в этой статье, и попробуйте реализовать такие расширения как пагинация, ограничение числа запросов и создание пользователя.

© Copyright 2014 — 2024 mkdev | Privacy Policy

Как создать API на Python без усилий на деплой

Аватарка пользователя Елена Капаца

Разработчики нередко приходят к мысли создать API на Python (англ. Application Programming Interface — программный интерфейс приложения). Если вы лишь недавно повстречались с этим термином, обладаете идеей для своего такого ПО и хотите разобраться, как устроено содержимое проекта, статья познакомит с базовой терминологией и проведет от первого шага с подключением фреймворка FastAPI и до HTTP-запроса.

Немного про HTTP

Для тех, кто не знаком с концепцией, расскажу вкратце: это способ обмена данными от сервера к клиенту и обратно. Мы пользуемся им каждый день в подавляющем большинстве сервисов:

Как создать API на Python без усилий на деплой 1

Этот «протокол передачи гипертекста» (англ. HyperText Transfer Protocol) стал популярен в 90-х и сегодня де-факто является стандартом обмена между устройствами. Развертывая простенький проект на Django, вы тоже, кстати, пользуетесь HTTP.

Если вам интересна миграция с low-code CMS на Django, изучите эту статью.

Вы наверняка уже слышали про классические типы запросов: GET (получить) и POST (создать). Среди прочих выделю еще DELETE (удалить), INSERT (вставить) и UPDATE (обновить). Один из них мы реализуем своими руками в этом гайде.

Как создать API на Python без усилий на деплой 2

Деплой FastAPI на Railway

Чтобы вы могли быстрее пощупать новоиспеченное ПО, я использую фреймворк FastAPI. Создавая проект на его базе, вы подключите автоматом:

  • main.py — главный для разработчика файл, ответственный за HTTP-запросы;
  • requirements.txt, содержащий список всех необходимых сторонних инструментов с версиями;
  • .gitignore, описывающий файлы, которые при коммите стоит игнорировать, например, директорию виртуального окружения;
  • railway.json помогает серверу схватить настройки проекта и запускать API автоматически после каждого обновления.

Клонировав этот код с маркетплейса, вы найдете все необходимое, чтобы сдружиться с Railway. Это условно бесплатный сервис для развертывания ваших приложений, то есть круглосуточно доступный сервер. Он пришел отчасти на замену Heroku и предлагает любому пользователю 5 демо-долларов, которые расходуются довольно медленно, по паре центов в день.

Как создать API на Python без усилий на деплой 3

В следующие 3-5 минут проект саморазвернется: установятся библиотеки, настроится триггер для пересборки API в случае коммита. Давайте вместе пройдемся по логам и научимся их читать при проблемах в будущем:

Первое, что отдает нам Railway — это перечень запускаемых команд:

╔══════════════════════════════ Nixpacks v1.19.0 ══════════════════════════════╗ ║ setup │ python38, gcc ║ ║──────────────────────────────────────────────────────────────────────────────║ ║ install │ python -m venv --copies /opt/venv && . /opt/venv/bin/activate ║ ║ │ && pip install -r requirements.txt ║ ║──────────────────────────────────────────────────────────────────────────────║ ║ start │ hypercorn main:app --bind "[::]:$PORT" ║ ╚══════════════════════════════════════════════════════════════════════════════╝ 

Затем устанавливаются все необходимые библиотеки:

#10 8.082 Collecting anyio=3.4.0 #10 8.093 Downloading anyio-4.1.0-py3-none-any.whl (83 kB) #10 8.100 ━━━━━━━━━━━━━━━━━━━━━━ 83.9/83.9 kB 21.1 MB/s eta 0:00:00 

Система уведомляет об успешно установленных инструментах:

#10 9.476 Successfully installed annotated-types-0.6.0 anyio-4.1.0 exceptiongroup-1.2.0 fastapi-0.100.0 h11-0.14.0 h2-4.1.0 hpack-4.0.0 hypercorn-0.14.4 hyperframe-6.0.1 idna-3.6 priority-2.0.0 pydantic-2.5.2 pydantic-core-2.14.5 sniffio-1.3.0 starlette-0.27.0 tomli-2.0.1 typing-extensions-4.8.0 wsproto-1.2.0 

Если вы намереваетесь повторить мой путь с другим шаблоном, то наверняка столкнетесь с крушением билда. Сборки устаревают быстро, конфликт зависимостей никто не отменял, и прежде чем темплейт заработал, мне пришлось пройти 4 попытки по другим ссылкам.

Деплой успешно выполнен:

Как создать API на Python без усилий на деплой 4

В разделе Settings блока FastAPI вы найдете перманентный URL, ведущий на поддомен Railway. Он позволит обращаться к вашему API с любого устройства:

Как создать API на Python без усилий на деплой 5

По умолчанию в главном файле проекта из методов только / (root-метод). Выглядит он таким образом, вскоре мы познакомимся поближе:

@app.get("/") async def root(): return

Перейдя по ссылке fastapi-production-fd50.up.railway.app я получу hello-world-приветствие:

Как создать API на Python без усилий на деплой 6

Cоздаем глагол GET

Для разработки собственных методов мне понадобится запустить этот же проект на ноутбуке. Потому теперь я клонирую себе репозиторий (он добавился в листинг моего аккаунта на GitHub):

Как создать API на Python без усилий на деплой 7

Открыв эту новую директорию в VSCode, создаю виртуальное окружение и устанавливаю инструменты:

python3 -m venv fastapi_env pip3 install -r requirements.txt 

Чтобы файлы fastapi_env не попали в репозиторий и не усложнили тем самым чтение коммита, исключим эту подпапку с помощью .gitignore, добавив строку:

fastapi_env\ 

Для запуска API в режиме отладки на своей машине используется специальная утилита hypercorn:

hypercorn main:app --reload 

Теперь тот же root-метод доступен по адресу 127.0.0.1:8000:

Как создать API на Python без усилий на деплой 8

Чтобы от гайд был полезнее, я использую пример из своей практики. При каждодневной выгрузке логов разговоров с моим ботом мне приходится указывать временной интервал.

payload = ]> 

Это тело запроса для библиотеки requests, и оно содержит смещение (сервер расположен где-то в UTC±0:00, то есть полночь там наступает на три часа раньше, чем в Москве):

Как создать API на Python без усилий на деплой 9

Каждый раз обсчитывать смещение при тестах неудобно, особенно, когда выгружаешь логи за период, не равный 24 часам. Так что давайте создадим метод, чтобы рассчитывал смещение. Для этого в main.py добавлю декоратор @app.get(«/get») (на самом деле, вместо get можно поставить любое слово) и функцию get_time() :

. import datetime from datetime import timedelta . today = datetime.today().date() yesterday = today - timedelta(days=7) . @app.get("/get") async def get_time(): return

Первый собственный метод готов. Теперь проверим его работу, перейдя по ссылке http://127.0.0.1:8000/get:

Как создать API на Python без усилий на деплой 10

Создав коммит с такой функцией, вы получите автоматический редеплой от Railway и работающий метод /get по ссылке fastapi-production-fd50.up.railway.app/get. Вместо datetime.datetime.now() несложно подставить другое интересующее время и получить смещение от него.

Как создать API на Python без усилий на деплой 11

Заключение

Практика показывает: найти реальную причину для своего API сложнее, чем создать его. Давайте повторим: для собственного API вам потребуется:

  • Найти сервер, на котором интерфейс будет базироваться. Свой ноутбук — это не сервер для продакшена;
  • Выбрать фреймворк для API (например, FastAPI / litestar);
  • Создать проект фреймворка по документации или с темплейта и развернуть его на сервере;
  • Описать собственные методы;
  • Погордиться собой, ведь свой эндпоинт — это очень круто!

Познакомиться с моим проектом вы можете на GitHub.

Пишем API на Python (с Flask и RapidAPI)

Если вы читаете эту статью, вероятно, вы уже знакомы с возможностями, которые открываются при использовании API (Application Programming Interface).

Добавив в свое приложение один из многих открытых API, вы можете расширить функциональность этого приложения либо же дополнить его нужными данными. Но что, если вы разработали уникальную функцию, которой хотите поделиться с коммьюнити?

Ответ прост: нужно создать собственный API.

Несмотря на то, что это поначалу кажется сложной задачей, на самом деле всё просто. Мы расскажем, как это сделать с помощью Python.

Что нужно для начала работы

  • Python 3;
  • Flask — простой и легкий в использовании фреймворк для создания веб-приложений;
  • Flask-RESTful — расширение для Flask, которое позволяет разработать REST API быстро и с минимальной настройкой.
pip install flask-restful

Рекомендуем бесплатный интенсив по программированию для начинающих:
Разработка telegram-бота на C# — 26–28 августа. Бесплатный интенсив, который позволяет разобраться в том, как работают боты-помощники, в особенностях работы с API Telegram и прочих нюансах. Трое лучших участников получат от Skillbox 30 000 рублей.

Перед тем как начать

Мы собираемся разработать RESTful API с базовой CRUID-функциональностью.

Чтобы полностью понять задачу, давайте разберемся с двумя терминами, упомянутыми выше.

Что такое REST?

REST API (Representational State Transfer) — это API, которое использует HTTP-запросы для обмена данными.

  • Архитектура клиент-сервер: клиент взаимодействует с пользовательским интерфейсом, а сервер — с бэкендом и хранилищем данных. Клиент и сервер независимы, любой из них может быть заменен отдельно от другого.
  • Stateless — никакие клиентские данные не сохраняются на сервере. Состояние сеанса хранится на стороне клиента.
  • Кэшируемость — клиенты могут кэшировать ответы сервера для улучшения общей производительности.

CRUD — концепция программирования, которая описывает четыре базовых действия (create, read, update и delete).

В REST API типы запросов и методы запроса отвечают за такие действия, как post, get, put, delete.

Теперь, когда мы разобрались с базовыми терминами, можно приступить к созданию API.

Разработка

Давайте создадим репозиторий цитат об искусственном интеллекте. ИИ — одна из наиболее активно развивающихся технологий сегодня, а Python — популярный инструмент для работы с ИИ.

С этим API разработчик Python сможет быстро получать информацию об ИИ и вдохновляться новыми достижениями. Если у разработчика есть ценные мысли по этой теме, он сможет добавлять их в репозиторий.

Начнем с импорта необходимых модулей и настройки Flask:

from flask import Flask from flask_restful import Api, Resource, reqparse import random app = Flask(__name__) api = Api(app)

В этом сниппете Flask, Api и Resource — классы, которые нам нужны.

Reqparse — это интерфейс парсинга запросов Flask-RESTful… Также понадобится модуль random для отображения случайной цитаты.

Теперь мы создадим репозиторий цитат об ИИ.

  • цифровой ID;
  • имя автора цитаты;
  • цитату.
ai_quotes = [ < "id": 0, "author": "Kevin Kelly", "quote": "The business plans of the next 10,000 startups are easy to forecast: " + "Take X and add AI." >, < "id": 1, "author": "Stephen Hawking", "quote": "The development of full artificial intelligence could " + "spell the end of the human race… " + "It would take off on its own, and re-design " + "itself at an ever increasing rate. " + "Humans, who are limited by slow biological evolution, " + "couldn't compete, and would be superseded." >, < "id": 2, "author": "Claude Shannon", "quote": "I visualize a time when we will be to robots what " + "dogs are to humans, " + "and I’m rooting for the machines." >, < "id": 3, "author": "Elon Musk", "quote": "The pace of progress in artificial intelligence " + "(I’m not referring to narrow AI) " + "is incredibly fast. Unless you have direct " + "exposure to groups like Deepmind, " + "you have no idea how fast — it is growing " + "at a pace close to exponential. " + "The risk of something seriously dangerous " + "happening is in the five-year timeframe." + "10 years at most." >, < "id": 4, "author": "Geoffrey Hinton", "quote": "I have always been convinced that the only way " + "to get artificial intelligence to work " + "is to do the computation in a way similar to the human brain. " + "That is the goal I have been pursuing. We are making progress, " + "though we still have lots to learn about " + "how the brain actually works." >, < "id": 5, "author": "Pedro Domingos", "quote": "People worry that computers will " + "get too smart and take over the world, " + "but the real problem is that they're too stupid " + "and they've already taken over the world." >, < "id": 6, "author": "Alan Turing", "quote": "It seems probable that once the machine thinking " + "method had started, it would not take long " + "to outstrip our feeble powers… " + "They would be able to converse " + "with each other to sharpen their wits. " + "At some stage therefore, we should " + "have to expect the machines to take control." >, < "id": 7, "author": "Ray Kurzweil", "quote": "Artificial intelligence will reach " + "human levels by around 2029. " + "Follow that out further to, say, 2045, " + "we will have multiplied the intelligence, " + "the human biological machine intelligence " + "of our civilization a billion-fold." >, < "id": 8, "author": "Sebastian Thrun", "quote": "Nobody phrases it this way, but I think " + "that artificial intelligence " + "is almost a humanities discipline. It's really an attempt " + "to understand human intelligence and human cognition." >, < "id": 9, "author": "Andrew Ng", "quote": "We're making this analogy that AI is the new electricity." + "Electricity transformed industries: agriculture, " + "transportation, communication, manufacturing." >]

Теперь нужно создать ресурсный класс Quote, который будет определять операции эндпоинтов нашего API. Внутри класса нужно заявить четыре метода: get, post, put, delete.

Начнем с метода GET

Он дает возможность получить определенную цитату путем указания ее ID или же случайную цитату, если ID не указан.

class Quote(Resource): def get(self, if 0: return random.choice(ai_quotes), 200 for quote in ai_quotes: if(quote["id"] == id): return quote, 200 return "Quote not found", 404

Метод GET возвращает случайную цитату, если ID содержит дефолтное значение, т.е. при вызове метода ID не был задан.

Если он задан, то метод ищет среди цитат и находит ту, которая содержит заданный ID. Если же ничего не найдено, выводится сообщение “Quote not found, 404”.

Помните: метод возвращает HTTP-статус 200 в случае успешного запроса и 404, если запись не найдена.

Теперь давайте создадим POST-метод для добавления новой цитаты в репозиторий

Он будет получать идентификатор каждой новой цитаты при вводе. Кроме того, POST будет использовать reqparse для парсинга параметров, которые будут идти в теле запроса (автор и текст цитаты).

def post(self, id): parser = reqparse.RequestParser() parser.add_argument("author") parser.add_argument("quote") params = parser.parse_args() for quote in ai_quotes: if(id == quote["id"]): return f"Quote with id already exists", 400 quote = < "id": int(id), "author": params["author"], "quote": params["quote"] >ai_quotes.append(quote) return quote, 201

В коде выше POST-метод принял ID цитаты. Затем, используя reqparse, он получил автора и цитату из запроса, сохранив их в словаре params.

Если цитата с указанным ID уже существует, то метод выводит соответствующее сообщение и код 400.

Если цитата с указанным ID еще не была создана, метод создает новую запись с указанным ID и автором, а также другими параметрами. Затем он добавляет запись в список ai_quotes и возвращает запись с новой цитатой вместе с кодом 201.

Теперь создаем PUT-метод для изменения существующей цитаты в репозитории

def put(self, id): parser = reqparse.RequestParser() parser.add_argument("author") parser.add_argument("quote") params = parser.parse_args() for quote in ai_quotes: if(id == quote["id"]): quote["author"] = params["author"] quote["quote"] = params["quote"] return quote, 200 quote = < "id": id, "author": params["author"], "quote": params["quote"] >ai_quotes.append(quote) return quote, 201

PUT-метод, аналогично предыдущему примеру, берет ID и input и парсит параметры цитаты, используя reqparse.

Если цитата с указанным ID существует, метод обновит ее с новыми параметрами, а затем выведет обновленную цитату с кодом 200. Если цитаты с указанным ID еще нет, будет создана новая запись с кодом 201.

Наконец, давайте создадим DELETE-метод для удаления цитаты, которая уже не вдохновляет

def delete(self, id): global ai_quotes ai_quotes = [qoute for qoute in ai_quotes if qoute["id"] != id] return f"Quote with id is deleted.", 200

Этот метод получает ID цитаты при вводе и обновляет список ai_quotes, используя общий список.

Теперь, когда мы создали все методы, всё, что нам нужно, — просто добавить resource к API, задать путь и запустить Flask.

api.add_resource(Quote, "/ai-quotes", "/ai-quotes/", "/ai-quotes/") if __name__ == '__main__': app.run(debug=True)

Наш REST API Service готов!

Далее мы можем сохранить код в файл app.py, запустив его в консоли при помощи команды:

python3 app.py

Если все хорошо, то мы получим нечто вроде этого:

* Debug mode: on
* Running on 127.0.0.1:5000/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: XXXXXXX

Тестируем API

После того как API создан, его нужно протестировать.

Сделать это можно при помощи консольной утилиты curl или клиента Insomnia REST либо же опубликовав API на Rapid API.

Публикуем наш API

RapidAPI — самый большой в мире маркетплейс с более чем 10 000 API (и около 1 млн разработчиков).

RapidAPI не только предоставляет единый интерфейс для работы со сторонними API, но и даtт возможность быстро и без проблем опубликовать ваш собственный API.

Для того чтобы сделать это, сначала нужно опубликовать его на каком-нибудь сервере в сети. В нашем случае воспользуемся Heroku. Работа с ним не должна вызвать никаких сложностей, (узнать о нём больше можно здесь).

Как опубликовать ваш API на Heroku

1. Устанавливаем Heroku.

Первым делом нужно зарегистрироваться и установить Heroku Command Line Interface (CLI). Это работает на Ubuntu 16+.

sudo snap install heroku —classic

heroku login

2. Добавляем необходимые файлы.

  • requirements.txt со списком необходимых Python модулей;
  • Procfile, который указывает, какие команды должны быть выполнены для запуска приложения;
  • .gitignore — для исключения файлов, которые не нужны на сервере
  • flask
  • flask-restful
  • gunicorn

Procfile будет содержать:

web: gunicorn app:app

Теперь, когда созданы файлы, давайте инициализируем git-репо и закоммитим:

git init
git add
git commit -m «First API commit»

3. Создаем новое Heroku-приложение.

heroku create

Отправляем master branch в удаленный репо Heroku:

git push heroku master

Теперь можно начать, открыв API Service при помощи команд:

heroku ps:scale web=1
heroku open

API будет доступно по адресу your-random-heroku-name.herokuapp.com/ai-quotes.

Как добавить ваш Python API в маркетплейс RapidAPI

После того как API-сервис опубликован на Heroku, можно добавить его к Rapid API. Здесь подробная документация по этой теме.

1. Создаем аккаунт RapidAPI.

Регистрируем бесплатную учетную запись — это можно сделать при помощи Facebook, Google, GitHub.

2. Добавляем API в панель управления.

3. Далее вводим общую информацию о своем API.

4. После нажатия “Add API” появляется новая страничка, где можно ввести информацию о нашем API.

5. Теперь можно либо вручную ввести эндпоинты API, либо загрузить swagger-file при помощи OpenAPI.

Ну а теперь нужно задать эндпоинты нашего API на странице Endpoints. В нашем случае эндпоинты соответствуют концепции CRUD (get, post, put, delete).

Далее нужно создать эндпоинт GET AI Quote, который выводит случайную цитату (в том случае, если ID дефолтный) или цитату для указанного ID.

Для создания эндпоинта нужно нажать кнопку “Create Endpoint”.

Повторяем этот процесс для всех других эндпоинтов API. На этом всё! Поздравляю, вы опубликовали ваш API!

Если все хорошо, страничка API будет выглядеть как-то так:

Заключение

В этой статье мы изучили процесс создания собственного RESTful API Service на Python, вместе с процессом публикации API в облаке Heroku и добавлением его в каталог RapidAPI.

Но в тестовом варианте были показаны только базовые принципы разработки API — такие нюансы, как безопасность, отказоустойчивость и масштабируемость, не рассматривались.

При разработке реального API все это нужно учитывать.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *