Door de jaren heen zijn onze diensten bij Kinsta altijd handmatig afgehandeld via het MyKinsta dashboard. Maar met de introductie van de Kinsta API en de voortdurende release van nieuwe API endpoints, kun je je workflow enorm verbeteren door een gepersonaliseerde methode te ontwikkelen waarmee je interactie hebt met de Kinsta diensten. Een van die manieren is het ontwikkelen van een Slackbot om activiteiten zoals het maken van sites te monitoren en beheren.

Wat je bouwt

Deze tutorial legt uit hoe je een Slackbot (of Slack applicatie) bouwt die interactie heeft met de Kinsta API om informatie op te halen en deze als realtime berichten af te leveren aan een aangewezen Slack kanaal met behulp van de Slack API Incoming Webhooks.

Om dit te doen maak je een Node.js applicatie met het Express framework om een UI te maken voor het maken van WordPress sites en deze te integreren met de Kinsta API. De applicatie gebruikt een formulier om configuratiegegevens voor je WordPress site te verzamelen en stuurt vervolgens een real-time update over de informatie van de site en details over hoe je de operationele status kunt controleren naar het opgegeven Slack kanaal.

Gif die de applicatie laat zien die gebouwd zou worden en hoe het real-time informatie naar Slack stuurt.
Gif die de applicatie laat zien die gebouwd zou worden en hoe het real-time informatie naar Slack stuurt.

Vereisten

Om dit project te kunnen volgen, moet je over het volgende beschikken:

De ontwikkelomgeving instellen

Om te beginnen maak je een nieuwe map aan voor je applicatie en initialiseer je deze met npm:

mkdir my-express-app 
cd my-express-app 
npm init -y

Na het uitvoeren van het commando npm init -y wordt er een nieuw package.json bestand aangemaakt in de map van je project met standaardwaarden. Dit bestand bevat belangrijke informatie over je project en de dependencies.

Installeer vervolgens de noodzakelijke dependencies voor je project. De volgende dependencies zijn essentieel:

  • ejs: EJS (Embedded JavaScript) is een templating engine waarmee je dynamische HTML content kunt genereren met JavaScript.
  • express: Express is een snel en minimalistisch webapplicatie framework voor Node.js. Het vereenvoudigt het bouwen van webapplicaties en API’s door essentiële features te bieden zoals routing, middleware-ondersteuning en het afhandelen van HTTP verzoeken en responses.
  • express-ejs-layouts: Express EJS layouts is een extensie voor Express die het gebruik van layouts of templates mogelijk maakt om een consistente structuur over meerdere views te behouden.

Voer het onderstaande commando uit om deze dependencies te installeren:

npm install ejs express express-ejs-layouts

Daarnaast moet je de volgende dev dependencies installeren om het bouwen en testen van je Node.js project te vergemakkelijken:

  • nodemon: Een waardevolle tool die je Node.js applicatie automatisch herstart wanneer er bestandswijzigingen in de directory worden gedetecteerd, wat zorgt voor een gestroomlijnde ontwikkelworkflow.
  • dotenv: Deze zero-dependency module speelt een cruciale rol bij het laden van omgevingsvariabelen uit een .env bestand.

Voer het onderstaande commando uit om deze dev dependencies te installeren:

npm install -D nodemon dotenv

Zodra je package.json is geïnitialiseerd en alle dependencies zijn geïnstalleerd, maak je een nieuw bestand, bijvoorbeeld app.js.

touch app.js

Hier is een standaardopzet voor je app.js bestand, waarin je de benodigde modules importeert en instelt dat het op een specifieke poort moet draaien:

// Import required modules
const express = require('express');
const app = express();

// Set up your routes and middleware here
// ...

// Start the server to listen on the specified port
app.listen(process.env.PORT || 3000, () => {
  console.log(`Server is running on port ${process.env.PORT || 3000}`);
});

Voer het volgende commando uit om je Node.js applicatie uit te voeren:

node app.js

Het draaien van een applicatie als deze betekent echter dat je deze handmatig opnieuw moet starten elke keer dat je wijzigingen aanbrengt in je project. Om dit ongemak te omzeilen, kun je nodemon gebruiken, dat je al hebt geïnstalleerd. Configureer het in je package.json bestand door een script commando te maken:

  "scripts": {
    "dev": "nodemon app.js"
  },

Draai nu je Node.js applicatie met automatisch herstarten met het volgende commando:

npm run dev

Aan de slag met Express en EJS templating

In deze tutorial bouw je een Node.js applicatie die inhoud weergeeft in de browser. Om dit te bereiken wordt express.js gebruikt als webframework en EJS (Embedded JavaScript) als templating engine.

Om EJS in te stellen als je view engine, voeg je de volgende regel toe aan je app.js bestand. Dit maakt het mogelijk om .ejs bestanden uit te voeren:

// Use EJS as the view engine 
app.set('view engine', 'ejs');

Nu Express is geconfigureerd met EJS, definieer je routes. In webapplicaties bepalen routes hoe de applicatie reageert op verschillende HTTP verzoeken (zoals GET of POST) en specificeren ze de acties die moeten worden ondernomen wanneer een specifieke URL wordt opgevraagd.

Maak bijvoorbeeld een route die een specifieke pagina laadt wanneer een gebruiker naar de indexpagina navigeert (/). Gebruik hiervoor de GET verzoekmethode.

// Define a route for the homepage
app.get('/', (req, res) => {
  // Here, you can specify what to do when someone accesses the homepage
  // For example, render an EJS template or send some HTML content
});

In de bovenstaande code zal de server, wanneer een gebruiker de index van je applicatie opent, de callback-functie uitvoeren die als tweede parameter is opgegeven. Binnen deze callback-functie kun je de logica afhandelen om een EJS template te renderen of wat HTML content te sturen die op de homepage moet worden weergegeven.

Je kunt de methode res.render() gebruiken om een EJS template te renderen of res.send() gebruiken om eenvoudige HTML inhoud te versturen.

app.get('/', (req, res) => {
    res.send('Hello World');
});

Als je je applicatie uitvoert, wordt “Hello World” weergegeven op de index pagina.

EJS templating

Deze tutorial richt zich op de logica en bevat startbestanden, zodat je je geen zorgen hoeft te maken over het maken van templates vanaf nul. Volg deze stappen om aan de slag te gaan:

  1. Open het template op GitHub om een nieuwe repository aan te maken.
  2. Vink de optie Include all branches tijdens het aanmaken van de repository aan.
  3. Zodra de repository is aangemaakt, kloon je het project naar je computer met Git.
  4. Om toegang te krijgen tot de startercode, schakel je naar de starter-files branch in je lokale repository.

In de startcode hebben we twee belangrijke mappen: public en views. De public map bevat alle statische onderdelen (CSS bestanden en afbeeldingen). Ze worden aan de template toegevoegd als statische bestanden:

// Static files
app.use(express.static('/public'));
app.use('/css', express.static(__dirname + '/public/css'));
app.use('/images', express.static(__dirname + '/public/images'));

In de views map staat het layout.ejs bestand en twee mappen: pages en partials. Het layout.ejs bestand bevat de algemene layout van dit project, zodat je niet voor alle pagina’s een aantal terugkerende code hoeft te herhalen. Importeer de express-ejs-layouts bibliotheek in het app.js bestand en configureer het:

// Import
const expressEjsLayouts = require('express-ejs-layouts');

// Configure
app.use(expressEjsLayouts);

De map pages bevat de routebestanden (index.ejs en operation.ejs), terwijl de map partials componenten bevat (header.ejs en footer.ejs). Voeg ze als volgt toe aan de layout:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <link rel="icon" href="/images/favicon.ico" />
        <link rel="stylesheet" href="/css/index.css" />
        <title>Site Builder</title>
    </head>
    <body>
        <div class="app-container">
            <%- include('partials/header') %>
            <div class="container"><%- body %></div>
            <%- include('partials/footer') %>
        </div>
    </body>
</html>

Wanneer je je Node.js applicatie uitvoert, wordt de UI geladen, maar je moet logica aan deze applicatie toevoegen om de formuliergegevens naar de Kinsta API te sturen en informatie over de site naar Slack te sturen wanneer de operatie start.

Aan de slag met Slack Incoming Webhooks

Slack Incoming Webhooks bieden een eenvoudige manier om berichten van externe applicaties naar Slack te sturen. Om Slack Incoming Webhooks te gebruiken, maak en configureer je een Slack applicatie en kopieer je vervolgens je Webhook URL om berichten programmatisch naar Slack te sturen.

Een Slack app maken en Webhook URL verkrijgen

Maak een nieuwe Slack applicatie aan door deze stappen te volgen:

  1. Navigeer naar het Slack API dashboard.
  2. Klik op de knop Create New App, waarmee een modal wordt geopend.
  3. Selecteer de From Scratch optie om je app vanaf de grond op te bouwen.
  4. Geef een naam op voor je Slack app, bijvoorbeeld Kinsta Bot.
  5. Kies vervolgens de workspace waar je de app wilt installeren en klik op de knop Create app.

Zodra je Slack-applicatie is gemaakt, kun je inkomende Webhooks inschakelen door te navigeren naar Features en Incoming Webhooks te selecteren. Schakel de schakelaar in om Incoming Webhooks in te schakelen voor je app.

Scroll naar beneden naar de sectie Webhook URLs for your workspace en klik op Add new webhook to workspace. Je wordt gevraagd om een kanaal te kiezen waar de berichten naartoe worden gestuurd. Selecteer het gewenste kanaal en klik op Authorize.

Na autorisatie krijg je een Webhook URL voor het geselecteerde kanaal. Deze URL gebruik je om berichten programmatisch naar Slack te sturen. Zo ziet een Webhook URL eruit:

https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX

Deze Webhook is specifiek voor één gebruiker en één kanaal. Houd het veilig, want het fungeert als een authenticatietoken voor je app. Je kunt de unieke codes na /services/ opslaan in je .env bestand. Je wordt ook gevraagd om de app opnieuw te installeren op je workspace om de wijzigingen door te voeren.

Berichten naar Slack sturen met Node.js en Kinsta API

Nu de interface van je Node.js applicatie is ingesteld en de Slackbot met succes is aangemaakt (samen met je WebHook URL), is het tijd om de logica af te handelen.

Formuliergegevens ophalen in Node.js

Op de index pagina heb je een formulier dat gegevens naar de Kinsta API stuurt om een nieuwe WordPress site te maken. Om dit te laten werken, moet je een POST verzoek maken vanaf de index pagina. Zorg ervoor dat je formulier een methode POST heeft en dat de invoervelden een name attribuut hebben, dat zal worden gebruikt in het app.js bestand.

app.post('/', (req, res) => {
    // Perform the desired operation with the form data
});

Om gegevens van een formulier op te halen in Node.js, moet je de volgende middleware gebruiken:

app.use(express.json());
app.use(express.urlencoded({ extended: true }));

Nu kun je de waarden van je formulier opvragen met req.body.[form field name]. req.body.displayName geeft je bijvoorbeeld de weergavenaam die via het formulier is verzonden. Laten we de totale formuliergegevens loggen:

app.post('/', (req, res) => {
    console.log(req.body);
});

Als je je code uitvoert, worden de formuliergegevens weergegeven nadat je het formulier hebt ingevuld en op de knop Submit hebt geklikt.

Formuliergegevens opgehaald uit de req parameter in Node.js.
Formuliergegevens opgehaald uit de req parameter in Node.js.

Site maken met Kinsta API in Node.js

Om een WordPress site te maken met de Kinsta API in Node.js kun je de methode fetch() gebruiken, die nu wordt ondersteund en efficiënt werkt in de nieuwste Node.js versies.

Om een bewerking met de Kinsta API uit te voeren, moet je een API sleutel aanmaken. Om een API sleutel te genereren:

  1. Ga naar je MyKinsta dashboard.
  2. Navigeer naar de pagina API sleutels (Je naam > Bedrijfsinstellingen > API sleutels).
  3. Klik op API sleutel aanmaken.
  4. Kies een vervaldatum of stel een aangepaste begindatum in en het aantal uren dat de sleutel moet verlopen.
  5. Geef de sleutel een unieke naam.
  6. Klik op Genereren.

Zorg ervoor dat je de gegenereerde API sleutel kopieert en veilig opslaat, want hij zal alleen op dit moment zichtbaar zijn. Maak voor dit project een .env bestand aan in je root directory en sla de API sleutel op als KINSTA_API_KEY.

Om een WordPress site te maken met behulp van de Kinsta API, heb je bovendien je Bedrijfs-ID nodig (die je kunt vinden in MyKinsta onder Bedrijf > Factureringsgegevens > Bedrijfs-ID). Sla deze ID ook op in het .env bestand, zodat je toegang hebt tot deze omgevingsvariabelen via process.env. Om deze functionaliteit in te schakelen, moet je de dotenv dependency bovenaan je app.js bestand als volgt configureren:

require('dotenv').config();

Om verder te gaan met het maken van een WordPress site via de Kinsta API, stuur je een POST verzoek naar het /sites endpoint, met de benodigde gegevens in het req.body object:

const KinstaAPIUrl = 'https://api.kinsta.com/v2';

app.post('/', (req, res) => {
    const createSite = async () => {
        const resp = await fetch(`${KinstaAPIUrl}/sites`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                Authorization: `Bearer ${process.env.REACT_APP_KINSTA_API_KEY}`,
            },
            body: JSON.stringify({
                company: process.env.REACT_APP_KINSTA_COMPANY_ID,
                display_name: req.body.displayName,
                region: req.body.location,
                install_mode: 'new',
                is_subdomain_multisite: false,
                admin_email: req.body.email,
                admin_password: req.body.password,
                admin_user: req.body.username,
                is_multisite: false,
                site_title: req.body.siteTitle,
                woocommerce: false,
                wordpressseo: false,
                wp_language: 'en_US',
            }),
        });
        const data = await resp.json();
        console.log(data);
    };
    createSite();
});

Door de bovenstaande code uit te voeren, maak je een nieuwe WordPress site met Kinsta API. Maar dit is niet het hoofddoel. Het doel is om een bericht naar Slack te sturen met informatie over de site als het maken van de site is gelukt.

Een bericht sturen naar Slack met de Incoming Webhook URL

Om dit te doen, maak je een If statement om de respons-status van het API verzoek te controleren. Als deze 202 is, betekent dit “sitecreatie is gestart” en kun je een bericht naar Slack sturen met de inkomende Webhooks URL. Om dit te bereiken kun je de HTTP verzoekbibliotheek van je voorkeur gebruiken (bijv. Axios) of een methode om een POST verzoek naar Slack te sturen. Laten we de methode fetch() gebruiken:

if (data.status === 202) {
    fetch(
        `https://hooks.slack.com/services/${process.env.SLACK_WEBHOOK_ID}`,
        {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({
                text: 'Hello, world.',
            }),
        }
    );
}

Voer de bovenstaande code uit en vul het formulier voor het maken van de site in. Als het proces succesvol is, wordt er direct een bericht naar Slack gestuurd.

Hello World verzonden van Node.js naar Slack met Incoming Webhooks.
Hello World verzonden van Node.js naar Slack met Incoming Webhooks.

Slack berichten aanpassen

Het bovenstaande voorbeeld stuurt een eenvoudig tekstbericht, maar Slack Incoming Webhooks ondersteunen veel meer dan eenvoudige tekst. Je kunt je berichten aanpassen met bijlagen, links, afbeeldingen, knoppen en meer.

Een manier om Slack berichten aan te passen is door gebruik te maken van de Slack Block Kit Builder. De Block Kit is een UI framework van Slack waarmee je rijke en interactieve berichten kunt bouwen met verschillende contentelementen.

Voor deze tutorial is hier een blok gemaakt met de Block Kit Builder om het bericht goed op te maken en een aantal waarden toe te voegen uit het formulier en het antwoord voor het maken van de site:

const message = {
    blocks: [
        {
            type: 'section',
            text: {
                type: 'mrkdwn',
                text: `Hello, your new site (${req.body.displayName}) has started building. It takes minutes to build. You can check the operation status intermittently via https://site-builder-nodejs-xvsph.kinsta.app/operation/${req.body.displayName}/${data.operation_id}.`,
            },
        },
        {
            type: 'divider',
        },
        {
            type: 'section',
            text: {
                type: 'mrkdwn',
                text: "_Here are your site's details:_",
            },
        },
        {
            type: 'section',
            text: {
                type: 'mrkdwn',
                text: `1. *Site URL:* http://${req.body.displayName}.kinsta.cloud/n2. *WP Admin URL:* http://${req.body.displayName}.kinsta.cloud/wp-admin/`,
            },
        },
    ],
};

In deze code maak je een berichtobject met een array van blokken. Elk blok vertegenwoordigt een specifiek deel van het Slack bericht en kan verschillende soorten inhoud hebben.

  1. Section blok: Dit type blok wordt gebruikt om een gedeelte van de tekst weer te geven. Je gebruikt de type: 'section' om aan te geven dat het om een sectieblok gaat. Binnen het sectieblok wordt de text property gebruikt met type: 'mrkdwn' om aan te geven dat de inhoud van de tekst moet worden geïnterpreteerd als Markdown format. De daadwerkelijke inhoud van de tekst staat in de text property, en we gebruiken template-literals om dynamische waarden van het formulier en de site creation response op te nemen, zoals req.body.displayName en data.operation_id.
  2. Divider blok: Dit bloktype wordt gebruikt om een horizontale lijn toe te voegen om delen van het bericht te scheiden. We gebruiken type: 'divider' om het scheidingsblok te maken.

Wanneer dit bericht naar Slack wordt verzonden met de Incoming Webhook, genereert het een visueel aantrekkelijk en informatief bericht in je Slack kanaal. Het bericht bevat dynamische waarden uit het formulier (zoals de naam van de site) en informatie uit het antwoord op het maken van de site, waardoor het een zeer aangepast en gepersonaliseerd bericht wordt.

Om dit aangepaste bericht te versturen, vervang je het object in de body van de fetch() door de inhoud van de berichtvariabele:

if (data.status === 202) {
    fetch(
        `https://hooks.slack.com/services/${process.env.SLACK_WEBHOOK_ID}`,
        {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify(message),
        }
    );
}
Slack bericht aangepast met Slack block Kit Builder.
Slack bericht aangepast met Slack block Kit Builder.

Site-aanmaak afhandelen met Kinsta API

In het bericht dat naar Slack wordt gestuurd, wordt een link gemaakt met de ID van de operatie en de weergavenaam. Je kunt een nieuwe route maken voor de Operations pagina om deze gegevens te gebruiken om de status van de operatie te controleren.

In Express kun je URL parameters opvragen met de parameter req. Om bijvoorbeeld het ID van de operatie op te vragen, gebruik je req.params.operationId.

const KinstaAPIUrl = 'https://api.kinsta.com/v2';

app.get('/operation/:displayName/:operationId', (req, res) => {
    const checkOperation = async () => {
        const operationId = req.params.operationId;
        const resp = await fetch(`${KinstaAPIUrl}/operations/${operationId}`, {
            method: 'GET',
            headers: {
                Authorization: `Bearer ${process.env.REACT_APP_KINSTA_API_KEY}`,
            },
        });
        const data = await resp.json();
        res.render('pages/operation', {
            operationID: req.params.operationId,
            displayName: req.params.displayName,
            operationMessage: data.message,
        });
    };
    checkOperation();
});

Met de bovenstaande code zal klikken op de link in Slack een verzoek doen aan de Kinsta API om de operationele status van je site te controleren. Werk het operation.ejs bestand bij om dynamische gegevens toe te voegen:

<div class="container-title">
    <h1 class="title">Check Site Operation Status</h1>
    <p>
        Check the status of your site tools operation via the id. Feel free to copy
        the ID and check in few seconds.
    </p>
</div>
<div class="form-container">
    <div class="input-div">
        <input class="form-control" value="<%= operationID %>" readOnly />
    </div>
    <button class="btn" type="submit" onclick="window.location.reload()">
        Refresh Operation Status
    </button>
</div>
<div class="services">
    <div class="details">
        <p><%= operationMessage %>..</p>
    </div>
</div>
<div class="services">
    <p class="description">
        If message above indicates that "Operation has successfully finished", use
        the links below to access your WP admin and the site itself.
    </p>
    <div class="details">
        <a
            href="http://<%= displayName %>.kinsta.cloud/wp-admin/"
            target="_blank"
            rel="noreferrer"
            class="detail-link"
        >
            <p>Open WordPress admin</p>
            <FiExternalLink />
        </a>
        <a
            href="http://<%= displayName %>.kinsta.cloud/"
            target="_blank"
            rel="noreferrer"
            class="detail-link"
        >
            <p>Open URL</p>
            <FiExternalLink />
        </a>
    </div>
</div>
Operation pagina met operation ID en site details.
Operation pagina met operation ID en site details.

Tot slot: je kunt de redirect-methode gebruiken om naar de operations pagina te navigeren als het proces voor het maken van een site start:

if (data.status === 202) {
    fetch(
        `https://hooks.slack.com/services/${process.env.SLACK_WEBHOOK_ID}`,
        {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify(message),
        }
    );
    res.redirect(`/operation/${req.body.displayName}/${data.operation_id}`);
}

De volledige sourcecode voor dit project is beschikbaar op de main branch van deze GitHub repository.

Je Node.js applicatie deployen naar Kinsta

Je kunt deze Node.js applicatie eenvoudig deployen naar Kinsta’s Applicatie Hosting platform. Het enige wat je hoeft te doen is je code pushen naar de Git provider van je voorkeur (Bitbucket, GitHub of GitLab). Volg dan deze stappen:

  1. Log in op je Kinsta account op het MyKinsta dashboard.
  2. Klik op Dienst toevoegen.
  3. Selecteer Applicatie in het dropdown menu.
  4. Kies in de popup die verschijnt de repository die je wilt deployen. Als je meerdere branches hebt, kun je de gewenste branch selecteren en een naam geven aan je applicatie.
  5. Selecteer een van de beschikbare datacenterlocaties. Kinsta zal de dependencies van je app detecteren en installeren vanuit package.json en vervolgens bouwen en deployen.

Tenslotte is het niet veilig om API sleutels naar publieke hosts zoals je Git provider te sturen. Bij het hosten kun je ze als omgevingsvariabelen toevoegen met dezelfde variabelennaam en waarde die in het .env bestand zijn gespecificeerd.

Stel omgevingsvariabelen in op DevKinsta bij het deployen.
Stel omgevingsvariabelen in op DevKinsta bij het deployen.

Zodra je de deployment van je applicatie start, zal deze normaal gesproken binnen een paar minuten gebouwd en gedeployd worden. Je krijgt een link naar je nieuwe applicatie, die er als volgt uitziet: https://site-builder-nodejs-xvsph.kinsta.app.

Samenvatting

In deze tutorial heb je geleerd hoe je berichten naar Slack kunt sturen vanuit een Node.js applicatie met behulp van Incoming Webhooks en hoe je Slack berichten kunt aanpassen met de Block Kit Builder.

De mogelijkheden met Slack en de Kinsta API zijn enorm, en deze tutorial is nog maar het begin. Door deze tools te integreren kun je een naadloze workflow creëren die je team altijd up-to-date houdt en de productiviteit verhoogt.

Hoe gebruik jij de Kinsta API? Welke features zou je als volgende toegevoegd/belicht willen zien?

Joel Olawanle Kinsta

Joel is a Frontend developer working at Kinsta as a Technical Editor. He is a passionate teacher with love for open source and has written over 200 technical articles majorly around JavaScript and it's frameworks.