Web

Zoom REST API

投稿日:2021年12月22日 更新日:

リモート会議を開催するとき、アプリケーションのZoomを使われてる人も多いと思います。
Zoomには、Pythonによってプログラミング可能なWeb-APIであるREST APIが用意されています。
このAPIを使えば、リモート会議を組み込んだアプリケーションを開発することができます。

Zoom REST APIは、REST APIのため、HTTPプロトコルのリクエスト・レスポンスを使っています。
専用のAPIライブラリなどは、提供されていません。したがって、プログラミング環境で、HTTPリクエスト・レスポンスを送受信できれば、使うことができます。

Zoom REST APIには、会議だけでなく、ウェビナー(セミナー)、チャットなどZoomで使える機能が一通り、揃っています。

■ Zoom REST APIを使うための準備

Zoom REST APIを使うためには、まず、以下を実施する必要があります。

1)Zoomにアカウント登録する
 
Zoomの無料のアカウントを作成します。誕生日(意味不明)、メールアドレスを入力して作成ます。

2)開発者用のデベロッパーサイトにログインする

Zoomのデベロッパーサイトから、1)で作成したアカウントでサインインします。







3)JWTアプリケーションを作成する

Zoomサーバへのリクエストを認証するために、JSON Web Tokens (JWT)を使います。
そのため、JWTアプリケーションを作成しておく必要があります。

右上のプルダウンから「Build App」を選択し、アプリケーションタイプのJWTの「Create APP」をクリックし、
アプリケーション名、メールアドレス、パスワードなど以下の項目を入力し、作成します。
(以下は作成したあとの画面です。)


作成後、以下のようにREST APIで使うAPIキー(API Key)とAPI秘密鍵(API Secret)が生成されます。
これらをメモしておきます

■ 会議機能を使う

主な会議機能は次の通りです。

・リモート会議を生成する
・リモート会議招待テキストを取得する
・リモート会議情報を取得する
・リモート会議全リストを取得する
 (リモート会議の存在をチェックする)
・リモート会議を削除する

Pythonによるコード例を以下に示します。

# -*- coding: utf-8 -*-
import requests
import json
import datetime
from dateutil import tz
import dateutil.parser
import jwt
import time
import random
from pprint import pprint
from tzlocal import get_localzone

#
# Meetingクラス
#
class MeetingClass:
    _token = None
    _meeting_id = None
    _meeting_status = None
    _meeting_uuid = None
    _meeting_json = None

    CONST_ZOOM_URL = 'https://api.zoom.us/v2'                           #  Zoom APIのURL
    CONST_ZOOM_API_KEY = 'pS2a2_F5SbybQyNhV0kisg'                       #  APIキー
    CONST_ZOOM_API_SECRET = 'ukS9Ien2dc00CgtxFTLSOg5LckFvk7ihMjXy'      #  APIの秘密鍵
    CONST_ZOOM_USER_ID = 'swata001@gmail.com'                           # 主催者のメールアドレス
    CONST_ZOOM_TOKEN_EXPIRE_SEC = 600                                   # トークン有効期限:600秒


    # トークン(JWT)作成メソッド
    def _GenerateToken(self,expire_sec):
        header = {"alg": "HS256", "typ": "JWT"}
        payload = {"iss": self.CONST_ZOOM_API_KEY, "exp": int(time.time() + expire_sec)}
        self._token = jwt.encode(payload, self.CONST_ZOOM_API_SECRET, algorithm='HS256', headers=header)

    #  デフォルト設定JSON生成メソッド
    def _CreateDefaultSettings(self,contact_name:str,contact_email:str):

        return {
            'host_video': True,             # start video when the host joins
            'participant_video': True,      # start video when the participants join
            'join_before_host': True,       # no host
            'jbh_time': 0,                  # Allow the participant to join the meeting at anytime.(デフォルトっぽい)
            'mute_upon_entry': True,        # mute paticipants upon entry
            'approval_type': 0,             # automatically approve
            'cn_meeting': False,            # host meeting in china
            'in_meeting': False,            # host meeting in india
            'watermark': False,             # add a watermark when viewing a shared screen
            'use_pmi': False,               # personal meeting id
            'registration_type': 1,         # The meeting's registration type
            'audio': "voip",                # How participants join the audio portion of the meeting
            'auto_recording': "none",       # The automatic recording settings
            'enforce_login': False,                     # enfoce login
            'waiting_room': False,                      # Whether to enable the Waiting Room feature if this values true,this disables the join_before_hosts setting
            'registrants_email_notification': False,    # Whether to sedn registants email notifications abount their registration approval cancellation or rejection
            'meeting_authentication': False,    # if true ,only authenticated users can join the meeting
            'contact_name': contact_name,       # The contact name for meeting registration
            'contact_email': contact_email}     # The contact email for meeting registration

    #  リクエストヘッダ生成メソッド
    def _GetHeaders(self,expire_sec: int):

        # トークン(JWT)作成
        self._GenerateToken(self.CONST_ZOOM_TOKEN_EXPIRE_SEC)

        # トークンを設定し、ヘッダ(JSON)作成
        headers = {'Authorization': f"Bearer {self._token}",
                   'Content-Type': 'application/json'}

        return headers

    #   
    #  リモート会議生成メソッド
    #
    def CreateMeeting(self, topic: str, start_time: datetime, duration_min: int,pass_code: str,contact_name:str,contact_email:str):

        # リモート会議作成用のURLを生成
        end_point =  f'/users/{self.CONST_ZOOM_USER_ID}/meetings'

        # トークンのタイムアウト値を指定し、リクエストヘッダを生成
        headers = self._GetHeaders(self.CONST_ZOOM_TOKEN_EXPIRE_SEC)

        # パスコードが未指定なら自動生成する
        if not pass_code:
            pass_code = str(random.randint(100000, 999999))
        
        # 会議のデフォルトパラメータを設定
        default_setting = self._CreateDefaultSettings(contact_name,contact_email)

        # リクエストのボディ部を生成
        body = {
            "topic": topic, # 会議の題名(Invitationに含まれる)
            'type': 2,  # scheduled meeting
            "start_time": start_time.isoformat(),
            "duration": duration_min,
            "timezone": "Osaka, Sapporo, Tokyo",
            "password": pass_code,
            #"agenda": agenda,
            "settings": default_setting}

        try:
            # リクエスト送信(生成:POST)
            # This API has a daily rate limit of 100 requests per day. 
            # Therefore, only 100 Create a Meeting API requests are permitted within a 24 hour window for a user.
            response = requests.request('POST',
                                    self.CONST_ZOOM_URL + end_point,
                                    headers=headers,
                                    data=json.dumps(body)).json()

            # レスポンス(JSON)からデータ取得
            self._meeting_id = response['id']   # meeting id
            self._status = response['status']   # 会議の状態
            self._uuid = response['uuid']       # uuid
            self._meeting_json = response       # レスポンス全体

            return self._meeting_id

        except Exception as e:
            print(e)
            return None

    #
    #  リモート会議招待用テキスト取得メソッド
    #
    def GetInvitation(self, meeting_id: int):

        # リモート会議招待用テキスト取得のURLを生成
        end_point = f"/meetings/{meeting_id}/invitation"

        # トークンのタイムアウト値を指定し、リクエストヘッダを生成
        headers = self._GetHeaders(self.CONST_ZOOM_TOKEN_EXPIRE_SEC)

        # リクエスト送信(取得:GET)
        invitation = requests.request("GET",
                                self.CONST_ZOOM_URL + end_point,
                                headers=headers).json()
        return invitation

    #
    #  リモート会議情報取得メソッド
    #
    def GetMeeting(self, meeting_id: int):

        # リモート会議情報取得のURLを生成
        end_point = f"/meetings/{meeting_id}"

        # トークンのタイムアウト値を指定し、リクエストヘッダを生成
        headers = self._GetHeaders(self.CONST_ZOOM_TOKEN_EXPIRE_SEC)

        # リクエスト送信(取得:GET)
        return requests.request("GET",
                                self.CONST_ZOOM_URL + end_point,
                                headers=headers).json()
        
    #
    #  リモート会議更新メソッド
    #
    def UpdateMeeting(self, meeting_id: int,topic: str, start_time: datetime, duration_min: int,pass_code: str,contact_name:str,contact_email:str):

        # リモート会議更新用のURLを生成
        end_point = f"/meetings/{meeting_id}"

        # トークンのタイムアウト値を指定し、リクエストヘッダを生成
        headers = self._GetHeaders(self.CONST_ZOOM_TOKEN_EXPIRE_SEC)

        # パスコードが未指定なら自動生成する
        if not pass_code:
            pass_code = str(random.randint(100000, 999999))
        
        # 会議のデフォルトパラメータを設定
        default_setting = self._CreateDefaultSettings(contact_name,contact_email)

        # リクエストのボディ部を生成
        body = {
            "topic": topic,
            'type': 2,  # scheduled meeting
            "start_time": start_time.isoformat(),
            "duration": duration_min,
            "timezone": "Osaka, Sapporo, Tokyo",
            "password": pass_code,
            "settings": default_setting}

        try:

            # リクエスト送信(更新:PATCH)
            response = requests.request('PATCH',
                                    self.CONST_ZOOM_URL + end_point,
                                    headers=headers,
                                    data=json.dumps(body))

            # 正常(status code : 204)
            if response.status_code == 204:
                return True

            return False
        
        except Exception as e:
            print(e)
            return False

    #
    #  リモート会議削除メソッド
    #
    def DeleteMeeting(self, meeting_id: int):

        # リモート会議削除用のURLを生成
        end_point = f"/meetings/{meeting_id}"

        # トークンのタイムアウト値を指定し、リクエストヘッダを生成
        headers = self._GetHeaders(self.CONST_ZOOM_TOKEN_EXPIRE_SEC)
        try:

            # リクエスト送信(削除:DELETE)
            response = requests.request('DELETE',
                                    self.CONST_ZOOM_URL + end_point,
                                    headers=headers)

            # 正常(status code : 204)
            if response.status_code == 204:
                return True

            return False
        
        except Exception as e:
            print(e)
            return False

    #
    #  リモート会議リスト取得メソッド
    #
    def GetMeetingList(self,next_page_token,page_number):

        # リモート会議リスト取得用のURLを生成
        end_point =  f'/users/{self.CONST_ZOOM_USER_ID}/meetings'

        # 取得するタイプに”スケジュール”、1ページ当たりの取得件数を100件で指定
        params =  '?type=scheduled&page_size=100'
        #params =  '?type=scheduled&page_size=1'

        # 次ページの取得の場合、次ページ用トークンとページ番号を指定
        if len(next_page_token) > 0:
            params += '&next_page_token='+ next_page_token
        if len(page_number) > 0:
            params += '&page_number='+ page_number

        # トークンのタイムアウト値を指定し、リクエストヘッダを生成
        headers = self._GetHeaders(self.CONST_ZOOM_TOKEN_EXPIRE_SEC)

        # リクエスト送信(取得:GET)
        return requests.request("GET",
                                self.CONST_ZOOM_URL + end_point + params,
                                headers=headers).json()

    #
    #  リモート会議リストを全件取得するメソッド
    #
    def GetMeetingListAll(self):

        meetings=[]
        next_page_token = ''
        page_number = 1

        #  リモート会議リスト取得       
        response =self.GetMeetingList('','')

        # レスポンスのページ番号を取得
        if 'page_number' in response and  response['page_number'] != None:
            page_number=int(response['page_number'])

        # 次ページ用トークンを取得
        next_page_token=response['next_page_token']

        # 取得した会議情報を保存
        meetings.extend(response['meetings']) 

        # 全ページ分繰り返し
        while(next_page_token != None and len(next_page_token) > 0 ):

            #  リモート会議リスト取得                  
            response =self.GetMeetingList(next_page_token,str(page_number))

            # レスポンスのページ番号を取得
            if 'page_number' in response and  response['page_number'] != None:
                page_number=int(response['page_number'])
            #else:
            #    page_number += 1

            # 次ページ用トークンを取得
            next_page_token=response['next_page_token']

            # 取得した会議情報を保存
            meetings.extend(response['meetings']) 

        return meetings

    #
    #  リモート会議リストから該当時間の会議があるかチェックするメソッド
    #
    def IsExistMeeting(self,meeting_list,start_time,duration):

        # 終わりの時間にする
        end_time =  start_time + datetime.timedelta(minutes=duration)

        for scheduled_meeting in meeting_list:

            # リモート会議情報のタイムゾーンを取得
            scheduled_timezone = tz.gettz(scheduled_meeting['timezone'])

            # リモート会議情報の開始時間をタイムゾーン付きの時間に変換する
            scheduled_start_time = dateutil.parser.parse(scheduled_meeting['start_time']).astimezone(scheduled_timezone)

            # 終わりの時間を取得
            scheduled_end_time =  scheduled_start_time + datetime.timedelta(minutes=scheduled_meeting['duration'])

            # 該当時間の会議があるかチェック
            if start_time >= scheduled_start_time or end_time <= scheduled_end_time :
                return True

        return False


if __name__ == '__main__':
    client = MeetingClass()


    # 
    #  リモート会議を生成する
    #

    #  開始・終了時間 -->  開始時間、会議時間(分)に変換
    day = '2022/6/6'
    start_time = '16:30'
    end_time = '17:30'
    mtg_start_time = datetime.datetime.strptime(day + ' ' + start_time, '%Y/%m/%d %H:%M')
    mtg_end_time = datetime.datetime.strptime(day + ' ' + end_time, '%Y/%m/%d %H:%M')
    duration_min = int((mtg_end_time - mtg_start_time).seconds / 60)

    # 開始時間にTimeZone付与
    ja = get_localzone()
    mtg_start_time = ja.localize(mtg_start_time)

    # リモート会議生成
    mtg_id = client.CreateMeeting(topic='api test',
                                start_time=mtg_start_time,
                                duration_min=duration_min,
                                pass_code = 'test',
                                contact_name = 'icebreak',
                                contact_email = 'icebreak_mt1@itresourcetech.net')


    # 
    #  リモート会議招待テキストを取得する
    #
    invitation = client.GetInvitation(mtg_id)
    pprint(invitation)


    # 
    #  リモート会議情報を取得する
    #
    meeting = client.GetMeeting(mtg_id)
    pprint(meeting)
    

    # 
    #  リモート会議全リストを取得する
    #
    meeting_list=client.GetMeetingListAll()
    pprint(meeting_list)
    print(len(meeting_list))


    # 
    #  リモート会議の存在をチェックする
    #
    JST = tz.gettz('Asia/Tokyo')
    start_time=datetime.datetime(2022,6,6,16,30,tzinfo=JST)
    result= client.IsExistMeeting(meeting_list,start_time,60)
    print(result)

    # 
    #  リモート会議を削除する
    #
    result = client.DeleteMeeting(mtg_id)

ポイントは、以下の通りです。

1)HTTPリクエストのヘッダに、生成したJWTを設定する

APIキーとAPI秘密鍵を使って、所定の形式のJWTを生成し、HTTPリクエストのヘッダに設定する必要があります。

    CONST_ZOOM_URL = 'https://api.zoom.us/v2'                           #  Zoom APIのURL
    CONST_ZOOM_API_KEY = 'XXXXXXXXXXXXXXX'                              #  APIキー
    CONST_ZOOM_API_SECRET = 'YYYYYYYYYYYYYYYYYY'                        #  APIの秘密鍵
    CONST_ZOOM_USER_ID = 'user@XXXX.com'                                # 主催者のメールアドレス
    CONST_ZOOM_TOKEN_EXPIRE_SEC = 600                                   # トークン有効期限:600秒

    # トークン(JWT)作成メソッド
    def _GenerateToken(self,expire_sec):
        header = {"alg": "HS256", "typ": "JWT"}
        payload = {"iss": self.CONST_ZOOM_API_KEY, "exp": int(time.time() + expire_sec)}
        self._token = jwt.encode(payload, self.CONST_ZOOM_API_SECRET, algorithm='HS256', headers=header)

    #  リクエストヘッダ生成メソッド
    def _GetHeaders(self,expire_sec: int):

        # トークン(JWT)作成
        self._GenerateToken(self.CONST_ZOOM_TOKEN_EXPIRE_SEC)

        # トークンを設定し、ヘッダ(JSON)作成
        headers = {'Authorization': f"Bearer {self._token}",
                   'Content-Type': 'application/json'}

        return headers

2)会議時間には、タイムゾーンを指定する

リモート会議なので、参加者がどこにいてもインターネットさえつながれば会議できます。そのため、リモート会議を生成する際、会議時間は必ずタイムゾーンを指定する必要があります。


    # 
    #  リモート会議を生成する
    #

    #  開始・終了時間 -->  開始時間、会議時間(分)に変換
    day = '2022/6/6'
    start_time = '16:30'
    end_time = '17:30'
    mtg_start_time = datetime.datetime.strptime(day + ' ' + start_time, '%Y/%m/%d %H:%M')
    mtg_end_time = datetime.datetime.strptime(day + ' ' + end_time, '%Y/%m/%d %H:%M')
    duration_min = int((mtg_end_time - mtg_start_time).seconds / 60)

    # 開始時間にTimeZone付与
    ja = get_localzone()
    mtg_start_time = ja.localize(mtg_start_time)

    # リモート会議生成
    mtg_id = client.CreateMeeting(topic='api test',
                                start_time=mtg_start_time,
                                duration_min=duration_min,
                                pass_code = 'test',
                                contact_name = 'icebreak',
                                contact_email = 'icebreak_mt1@itresourcetech.net')

3)APIからリモート会議生成時に設定できるオプションと自アカウントで事前設定するオプションがある

以下のようなオプションがAPIから設定できます。

    #  デフォルト設定JSON生成メソッド
    def _CreateDefaultSettings(self,contact_name:str,contact_email:str):

        return {
            'host_video': True,             # start video when the host joins
            'participant_video': True,      # start video when the participants join
            'join_before_host': True,       # no host
            'jbh_time': 0,                  # Allow the participant to join the meeting at anytime.(デフォルトっぽい)
            'mute_upon_entry': True,        # mute paticipants upon entry
            'approval_type': 0,             # automatically approve
            'cn_meeting': False,            # host meeting in china
            'in_meeting': False,            # host meeting in india
            'watermark': False,             # add a watermark when viewing a shared screen
            'use_pmi': False,               # personal meeting id
            'registration_type': 1,         # The meeting's registration type
            'audio': "voip",                # How participants join the audio portion of the meeting
            'auto_recording': "none",       # The automatic recording settings
            'enforce_login': False,                     # enfoce login
            'waiting_room': False,                      # Whether to enable the Waiting Room feature if this values true,this disables the join_before_hosts setting
            'registrants_email_notification': False,    # Whether to sedn registants email notifications abount their registration approval cancellation or rejection
            'meeting_authentication': False,    # if true ,only authenticated users can join the meeting
            'contact_name': contact_name,       # The contact name for meeting registration
            'contact_email': contact_email}     # The contact email for meeting registration


他にも、参加者に画面共有を自動許可する設定などは、自アカウントの設定画面から事前に設定する必要があります。



4)Zoom REST APIの定義に従って、URLとHTTPのメソッドを設定する。

機能ごとにURLを使い分けます。その際、自アカウントのメールアドレスを指定する必要があります。
REST APIに沿って、使う機能毎にGET/POST/PATCH/DELETEのメソッドを指定する必要があります。

    #   
    #  リモート会議生成メソッド
    #
    def CreateMeeting(self, topic: str, start_time: datetime, duration_min: int,pass_code: str,contact_name:str,contact_email:str):

        # リモート会議作成用のURLを生成
        end_point =  f'/users/{self.CONST_ZOOM_USER_ID}/meetings'

        # トークンのタイムアウト値を指定し、リクエストヘッダを生成
        headers = self._GetHeaders(self.CONST_ZOOM_TOKEN_EXPIRE_SEC)

        # パスコードが未指定なら自動生成する
        if not pass_code:
            pass_code = str(random.randint(100000, 999999))
        
        # 会議のデフォルトパラメータを設定
        default_setting = self._CreateDefaultSettings(contact_name,contact_email)

        # リクエストのボディ部を生成
        body = {
            "topic": topic, # 会議の題名(Invitationに含まれる)
            'type': 2,  # scheduled meeting
            "start_time": start_time.isoformat(),
            "duration": duration_min,
            "timezone": "Osaka, Sapporo, Tokyo",
            "password": pass_code,
            #"agenda": agenda,
            "settings": default_setting}

        try:
            # リクエスト送信(生成:POST)
            # This API has a daily rate limit of 100 requests per day. 
            # Therefore, only 100 Create a Meeting API requests are permitted within a 24 hour window for a user.
            response = requests.request('POST',
                                    self.CONST_ZOOM_URL + end_point,
                                    headers=headers,
                                    data=json.dumps(body)).json()

            # レスポンス(JSON)からデータ取得
            self._meeting_id = response['id']   # meeting id
            self._status = response['status']   # 会議の状態
            self._uuid = response['uuid']       # uuid
            self._meeting_json = response       # レスポンス全体

            return self._meeting_id

        except Exception as e:
            print(e)
            return None


5)すでに登録済の会議リストの複数ページ取得には、次ページ番号と次ページトークンを指定する

登録した会議がたくさんある場合、APIを使って会議リストを取得すると、次ページ用のページ番号とトークンが返却されます。次ページを取得する場合、これらを指定して取得する必要があります。


    #
    #  リモート会議リスト取得メソッド
    #
    def GetMeetingList(self,next_page_token,page_number):

        # リモート会議リスト取得用のURLを生成
        end_point =  f'/users/{self.CONST_ZOOM_USER_ID}/meetings'

        # 取得するタイプに”スケジュール”、1ページ当たりの取得件数を100件で指定
        params =  '?type=scheduled&page_size=100'
        #params =  '?type=scheduled&page_size=1'

        # 次ページの取得の場合、次ページ用トークンとページ番号を指定
        if len(next_page_token) > 0:
            params += '&next_page_token='+ next_page_token
        if len(page_number) > 0:
            params += '&page_number='+ page_number

        # トークンのタイムアウト値を指定し、リクエストヘッダを生成
        headers = self._GetHeaders(self.CONST_ZOOM_TOKEN_EXPIRE_SEC)

        # リクエスト送信(取得:GET)
        return requests.request("GET",
                                self.CONST_ZOOM_URL + end_point + params,
                                headers=headers).json()

    #
    #  リモート会議リストを全件取得するメソッド
    #
    def GetMeetingListAll(self):

        meetings=[]
        next_page_token = ''
        page_number = 1

        #  リモート会議リスト取得       
        response =self.GetMeetingList('','')

        # レスポンスのページ番号を取得
        if 'page_number' in response and  response['page_number'] != None:
            page_number=int(response['page_number'])

        # 次ページ用トークンを取得
        next_page_token=response['next_page_token']

        # 取得した会議情報を保存
        meetings.extend(response['meetings']) 

        # 全ページ分繰り返し
        while(next_page_token != None and len(next_page_token) > 0 ):

            #  リモート会議リスト取得                  
            response =self.GetMeetingList(next_page_token,str(page_number))

            # レスポンスのページ番号を取得
            if 'page_number' in response and  response['page_number'] != None:
                page_number=int(response['page_number'])
            #else:
            #    page_number += 1

            # 次ページ用トークンを取得
            next_page_token=response['next_page_token']

            # 取得した会議情報を保存
            meetings.extend(response['meetings']) 

        return meetings


6)WebHook機能を使うには、アカウントから事前にURLを指定しておく

WebHook機能とは、リモート会議生成・開始・終了・削除などが発生した際にZoomサーバから自分のサーバのURLを呼び出してくれる機能です。
いわゆるコールバックですが、あらかじめアカウントからURLを設定しておく必要があります。また、呼び出してほしいイベントを指定しておく必要があります。



■ まとめ

一つのアカウントで同じ時間帯でダブルブッキングできません。同じ時間帯で会議を設定するには、複数のアカウントが必要になります。(有料アカウントでは、一つのアカウントで、「ホストを追加(有料)」することで対応できるようです)


-Web

執筆者:


comment

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

関連記事

決済API(PAY.JPの例)

EC(電子商取引)などインターネットを使ったサービスが増える中、取引の決済処理を担う決済APIも多種多様になってきています。それら決済APIについて、解説します。 ■ 決済APIとは何か 代表的な決済 …

スクレイピング

インターネットにあるWebサイトのHTMLデータの中から、必要なデータを自動的に取得することをスクレイピングといいます。Googleなどの検索エンジンがインデックスを作成するために、自動でWebサイト …

続スクレイピング

seleniumライブラリを使ったスクレイピングについて、解説します。サイトには、ブラウザとセッションを管理するものもあり、セッションがないとアクセスできないようにしているサイトもあります。セッション …

GCPでのWebサービスの作り方(Python、Flask、MYSQL、Mail、Apache) 2/2

以下からの続きとなります。GCPでのWebサービスの作り方(Python、Flask、MYSQL、Mail、Apache) 1/2 – DXインテリジェンス (itresourcetech.net) …

GCPでのWebサービスの作り方(Python、Flask、MYSQL、Mail、Apache) 1/2

GCP(Google Cloud Platform)上で、Webサービスを立ち上げるポイントを整理します。オンプレミス(自前でネインターネット接続環境やサーバを調達してシステムを構築)でWebサービス …