https://d226lax1qjow5r.cloudfront.net/blog/blogposts/manage-a-pool-of-phone-numbers-with-node-js/Dev_Numbers_Node-js_1200x600.png

Node.jsで電話番号のプールを管理する

最終更新日 May 5, 2021

所要時間:1 分

いつもオフィスの電話の近くにいるとは限りませんし、そのような場合、顧客はあなたと連絡を取るのに苦労するかもしれません。このチュートリアルでは、電話番号管理APIを使用するアプリケーションを作成します。 番号管理APIを使用するアプリケーションを構築します。各番号は、自宅から使用できるプライベート携帯電話など、別の番号に電話をリダイレクトします。

また、Vonage APIアカウントのすべての番号ではなく、アプリケーションのユーザーが購入・管理した番号のみを見ることができるようにします。最後に、あなたが知っているユーザーだけがアクセスできるようにし、パスワードなしで公共のウェブからアクセスできないようにします。

The final application screenshot, showing a pool of phone numbers and management options including update and delete

このプロジェクトは今すぐ使えますか?

このプロジェクトの完成したコードはGlitchにある。あなたは プロジェクトをクリックし リミックスして編集するボタンをクリックし、あなた自身の認証情報を 🔑.envファイルに追加します。そして、右上の 表示ボタンをクリックすれば、すぐにプロジェクトを使うことができます。

完成したコードは GitHub.

前提条件

注:Nexmoは2016年に買収された後、最近Vonageにリブランドされました。このチュートリアルではNexmoのURLに電話をかけているので、心配しないでください。

Vonage API Account

To complete this tutorial, you will need a Vonage API account. If you don’t have one already, you can sign up today and start building with free credit. Once you have an account, you can find your API Key and API Secret at the top of the Vonage API Dashboard.

ベースプロジェクトの作成

定型文がある グリッチプロジェクトがあります。このアプリケーションには

  • 新規Expressプロジェクトで、Glitchのターミナルを開き、次のように入力する。 pnpm install express body-parser cors nedb-promises axios qs express-basic-auth.

  • 新しい nedbデータベースを .dataフォルダに新しいnedbデータベースを作成しました。このフォルダはあなたのアプリケーションのバージョンに固有のもので、他の人が見たりコピーしたりすることはできません。

  • 基本的なExpressアプリケーションを初期化し、プロジェクトのURLにアクセスすると views/index.htmlファイルを提供します。

  • Vue.jsとAxiosのライブラリを index.htmlファイルにVue.jsライブラリとAxiosライブラリを含め、新しいVue.jsアプリケーションを作成しました。 public/style.cssファイルに追加しました。

Glitchアカウントにログインしてください。 このリンクをクリックして(コピー)してください。

ゼロから始めるにせよ、当社の定型文を使うにせよ、Vonage API DashboardにアクセスしてAPIキーとシークレットを取得し、プロジェクトの 🔑.envファイルに入れる必要があります。これらの値は一般には公開されませんが、アプリケーション内で process.env.PROPERTY.

Numbersを購入するためのエンドポイントを構築する

このエンドポイントには countryを指定する必要があります。これは Number Management API が要求するものだからです。

アプリケーションの最終行の上に、以下のコードを記述する:

app.post('/numbers', async (req, res) => {
    try {
        const { NEXMO_API_KEY, NEXMO_API_SECRET } = process.env;
        const availableNumbers = await axios.get(`https://rest.nexmo.com/number/search?api_key=${NEXMO_API_KEY}&api_secret=${NEXMO_API_SECRET}&country=${req.body.country}&features=SMS,VOICE`);
        const msisdn = availableNumbers.data.numbers[0].msisdn;
        res.send(msisdn);
    } catch (err) {
        res.send(err);
    }
});

にPOSTリクエストを送ると、アプリケーションは番号管理APIにGETリクエストを行って利用可能なMSISDN(電話番号)を探し、最初の1つを返す。 /numbersにPOSTリクエストを送ると、アプリケーションは利用可能なMSISDN(電話番号)を見つけるために番号管理APIにGETリクエストを行い、最初のものを返す。

ターミナルを開き、以下のコマンドを実行して新しいAPIエンドポイントをテストする: curl -H "Content-Type: application/json" -X POST -d '{"country": "GB"}' https://YOUR_GLITCH_PROJECT_NAME.glitch.me/numbersあなたのGlitchプロジェクト名を置き換えてください。成功すれば、利用可能な電話番号が返されるはずです。

次のように置き換える。 res.send(msisdn)を次のように置き換える:

await axios({
    method: 'POST',
    url: `https://rest.nexmo.com/number/buy?api_key=${NEXMO_API_KEY}&api_secret=${NEXMO_API_SECRET}`,
    data: qs.stringify({ country: req.body.country, msisdn }),
    headers: { 'content-type': 'application/x-www-form-urlencoded' }
});
await db.insert({ msisdn });
res.send('Number successfully bought');

これは、結果から最初のMSISDNを取り出し、利用可能なAccountクレジットからそれを購入し、MSISDNの新しいデータベースレコードを格納する。この qsパッケージはデータを x-www-form-encodedこれはNumbers Management APIが要求するものである。

チェックポイント!ターミナルからアプリケーションへのAPIコールを繰り返します。成功メッセージが表示され、Vonage APIアカウントで新しい番号にアクセスできるはずです。

注意 - あなたのアプリケーションでVonage APIコールが失敗する理由は複数あり、あなたのコードとは関係ありません。 番号管理APIを使ってあなたの国の番号を取得できるか確認してください。それでもうまくいかない場合は 住所Vonage API Dashboard経由で番号を取得する必要があります。

ナンバーズを購入するフロントエンドを構築する

あなたのPOSTリクエストエンドポイントは問題なく動作しているかもしれませんが、それを使用するためにもっとフレンドリーなフロントエンドを作成する時です。開く views/index.htmlを開き、HTMLに以下を追加します:

<div id="app">
    <h1>Number Manager</h1>
    <section>
        <h2>Buy New Number</h2>
        <input type="text" v-model="country" placeholder="Country Code" />
        <button @click="buyNumber">Buy new number</button>
    </section>
</div>

あなたの <script>を以下のように更新する:

const app = new Vue({
    el: '#app',
    data: {
        country: ''
    },
    methods: {
        async buyNumber() {
            try {
                if(this.country && confirm('Are you sure you would like to buy a number?')) {
                    await axios.post('/numbers', {
                        country: this.form.country
                    })
                    alert('Successfully bought new number');
                }
            } catch(err) {
                alert('Error buying new number', err);
            }
        }
    }
})

をクリックしてアプリケーションを開きます。 表示をクリックしてアプリケーションを開きます。ボックスに "GB "と入力し、"新しい番号を購入 "をクリックします。 confirm()をクリックすると、ポップアップ・ボックスが表示されます。このアプリケーションはVue.jsを使用していますが、HTTPリクエストを行うことができるアプリケーションであれば、どのようなアプリケーションでも構築することができます。

番号をリストアップするエンドポイントを構築する

最後の行の前に、Express アプリケーションに新しいエンドポイントを作成します:

app.get("/numbers", async (req, res) => {
    try {
        res.send('ok');
    } catch (err) {
        res.send(err);
    }
});

ブロックの先頭で tryブロックの先頭で、Vonage API用のVonage Number Management APIからすべてのローカルデータベースエントリとすべての番号を取得します。

const { NEXMO_API_KEY, NEXMO_API_SECRET } = process.env;
const dbNumbers = await db.find();
const vonageNumbers = await axios.get(`https://rest.nexmo.com/account/numbers?api_key=${NEXMO_API_KEY}&api_secret=${NEXMO_API_SECRET}`);

次に、新しい配列を作成する。 vonageNumbersを作成します。こうすることで、このアプリケーションで管理されている Vonage API アカウントの Numbers のみを返すようになります。

const numbersInBothResponses = vonageNumbers.data.numbers.filter(vonageNumber => {
    return dbNumbers.map(dbNumber => dbNumber.msisdn).includes(vonageNumber.msisdn)
});

次に、番号ごとに両方のデータソースを統合したオブジェクトを1つ作成する:

const combinedResponses = numbersInBothResponses.map(vonageNumber => {
    return {
        ...vonageNumber,
        ...dbNumbers.find(dbNumber => dbNumber.msisdn == vonageNumber.msisdn)
    }
})

combinedResponsesに置き換えてください。 res.send('ok');res.send(combinedResponses);.

ナンバーズをリストアップするフロントエンドの構築

ファイルに index.htmlファイルに、Express エンドポイントから Numbers を取得する新しいメソッドを作成します:

async getNumbers() {
    const { data } = await axios.get('/numbers')
    this.numbers = data;
}

オブジェクトを dataオブジェクトを以下のように更新する:

data: {
    numbers: [],
    country: ''
}

このデータをロードするには created()関数を dataオブジェクトのすぐ下に関数を追加する:

created() {
    this.getNumbers();
}

次のようにHTMLに追加して、Numbersを表示する:

<section>
    <h2>Current Numbers</h2>
    <div class="number" v-for="number in numbers" :key="number.msisdn">
        <h3>{{number.msisdn}}</h3>
        <label for="name">Friendly Name</label>
        <input type="text" v-model="number.name" placeholder="New name">
        <label for="forward">Forwarding Number</label>
        <input type="text" v-model="number.voiceCallbackValue" placeholder="Update forwarding number">
    </div>
</section>

チェックポイントクリック 表示をクリックし、フロントエンド・アプリケーションを開いてください。ロードされると、管理されている電話番号が表示されるはずです。

最後に、このセクションでは buyNumber()メソッドを更新して this.getNumbers();成功の後に alert().新しい番号を購入すると、ページを更新しなくてもリストが更新されるようになります。

数値を更新するためのエンドポイントとフロントエンドの構築

このアプリケーションがサポートする電話番号の更新には2種類あります。番号のフレンドリーネームを更新する場合は、ローカルデータベースのエントリーを編集することになり、転送番号を更新する場合は、番号管理APIを介して番号を更新することになります。私たちのエンドポイントは両方をサポートする必要があり、渡されたデータを使用してどちらを更新するかを決定します。次のように server.jsに以下を追加します:

app.patch("/numbers/:msisdn", async (req, res) => {
    try {
        const { NEXMO_API_KEY, NEXMO_API_SECRET } = process.env;
        if(req.body.name) {
            await db.update({ msisdn: req.params.msisdn }, { $set: { name: req.body.name } })
        }
        if(req.body.forward) {
            await axios({
                method: "POST",
                url: `https://rest.nexmo.com/number/update?api_key=${NEXMO_API_KEY}&api_secret=${NEXMO_API_SECRET}`,
                data: qs.stringify({ 
                    country: req.body.country, 
                    msisdn: req.params.msisdn,
                    voiceCallbackType: 'tel',
                    voiceCallbackValue: req.body.forward
                }),
                headers: { "content-type": "application/x-www-form-urlencoded" }
            })
        }
        res.send('Successfully updated')
    } catch(err) {
        res.send(err)
    }
})

このPATCHエンドポイントには、更新する電話番号が含まれている。ボディに nameプロパティが含まれている場合、ローカル・データベースが更新されます。 forwardを含む場合、番号の設定は番号管理APIを介して更新されます。

index.htmlで、以下のメソッドを作成する:

async updateNumber(number) {
    try {
        const { msisdn, country, name, voiceCallbackValue } = number
        const payload = { country }
        if(name) payload.name = name
        if(voiceCallbackValue) payload.forward = voiceCallbackValue
        await axios.patch(`/numbers/${msisdn}`, payload)
        alert('Successfully updated number');
        this.getNumbers(); 
    } catch(err) {
        alert('Error updating number', err);
    }
}

また、テンプレートからこのメソッドを呼び出す必要があります - ユーザーがテキスト入力のいずれかにフォーカスしているときにEnterを押したときに発生します。入力を以下のように更新します:

<label for="name">Friendly Name</label>
<input type="text" v-model="number.name" @keyup.enter="updateNumber(number)" placeholder="New name">
<label for="forward">Forwarding Number</label>
<input type="text" v-model="number.voiceCallbackValue" @keyup.enter="updateNumber(number)" placeholder="Update forwarding number">

チェックポイント番号のフレンドリーネームを更新する。その後、転送番号を更新してみてください(転送番号は有効な形式でなければならないことを覚えておいてください)。 有効なフォーマット)

Numbersをキャンセルするためのエンドポイントとフロントエンドの構築

番号が不要になった場合、その番号をキャンセルすることで、アカウントから即座に解除することができます。これがバーチャル電話番号プールを管理する最後の重要な部分です。最後の server.jsコードの最終行の上に以下を追加します:

app.delete("/numbers/:msisdn", async (req, res) => {
    try {
        const { NEXMO_API_KEY, NEXMO_API_SECRET } = process.env;
        await axios({
            method: "POST",
            url: `https://rest.nexmo.com/number/cancel?api_key=${NEXMO_API_KEY}&api_secret=${NEXMO_API_SECRET}`,
            data: qs.stringify({ 
                country: req.body.country, 
                msisdn: req.params.msisdn
            }),
            headers: { "content-type": "application/x-www-form-urlencoded" }
        })
        res.send('Successfully cancelled')
    } catch(err) {
        res.send(err)
    }
})

index.htmlメソッドを追加します。 deleteNumber()メソッドを追加します:

async deleteNumber(number) {
    try {
        if(confirm('Are you sure you would like to delete this number?')) {
            const { msisdn, country } = number
            await axios.delete(`/numbers/${msisdn}`, { data: { country } })
            alert('Successfully deleted number')
            this.getNumbers()
        }
    } catch(err) {
        alert('Error deleting number', err);
    }
}

最後に、テンプレートに転送番号入力のすぐ下にボタンを追加する:

<button @click="deleteNumber(number)">Delete number</button>

チェックポイント番号を削除する

ローカルデータベースから番号を削除していないことにお気づきかもしれません。これを実装することもできますが、GET numbersエンドポイントはVonage APIアカウントとローカルデータベースの両方に存在する番号のみを返すため、削除された番号は返されません。

ハウスキーピング

このアプリケーションはほぼ完成しているが、いくつかハウスキーピングが残っている。

フロントエンドからのAPIコールのみを許可する

現時点では、誰でも自分の端末を開き、許可なくあなたのNumbersを管理することができる。の上部付近 server.jsの一番上、 app.use()の文のすぐ下に、以下を追加する:

app.use(cors({ origin: `https://${process.env.PROJECT_NAME}.glitch.me` }));

process.env.PROJECT_NAMEはGlitchが提供する環境変数で、このプロジェクトの名前と同じです。この設定はGlitch URLからのリクエストのみを許可します。

基本認証の追加

たとえ人々が自分のアプリケーションからAPIにアクセスできなくても、あなたのライブサイトに出くわす可能性はある。幸いなことに、基本的なHTTP認証のセットアップには2つのステップしかない。

まず、パスフレーズを 🔑.envファイルにパスフレーズを追加する。次に、以下の行を app.use()ステートメントの一番下に次の行を追加します:

app.use(basicAuth({ users: { admin: process.env.ADMIN_PASSWORD }, challenge: true }));

アプリケーションをロードする際に、ユーザー名として adminをユーザー名とパスワードとして入力する必要があります。

次はどうする?

このシンプルなアプリケーションで、ほとんどのチームの要求に対応できるが、いくつか改善すべき点があるのは確かだ:

  • 特定のユーザーにのみNumbersの購入権限を与える

  • 購入前に各番号の料金を確認

  • ローカルデータベースの各番号にさらにデータを追加する

  • エラー処理の改善

このプロジェクトの完成したコードも GitHub.

Vonage APIsのNumbers Management APIについては、以下のドキュメントをご覧ください。 ドキュメントまた、追加サポートが必要な場合は、お気軽に弊社の VonageデベロッパーTwitterアカウントまたは Vonage コミュニティ Slack.

シェア:

https://a.storyblok.com/f/270183/400x400/c822f15b89/kevinlewis.png
Kevin Lewisヴォネージの卒業生

Vonageの元デベロッパー・アドボケイトで、ロンドンの地元テック・コミュニティをサポートする役割を担っていた。彼は経験豊富なイベントオーガナイザーであり、ボードゲーマーであり、ムーというかわいい犬のパパでもある。また、You Got This(幸せで健康的な仕事生活に必要なコア・スキルに関するイベント・ネットワーク)のリード・オーガナイザーでもある。