În acest articol vom analiza modul în care putem construi rapid și ușor un API cu TypeScript și Serverless.

Vom învăța apoi cum să folosim aws-sdk pentru a accesa alte servicii AWS și a crea un API de traducere automată.

Dacă preferați să vizionați și să învățați, puteți viziona videoclipul de mai jos:

Noțiuni de bază

Pentru a începe acest întreg proces, trebuie să ne asigurăm că avem Serverless Framework instalat și că avem un profil AWS configurat pe computerul nostru. Dacă nu ați făcut-o, atunci puteți vezi acest videoclip despre cum să configurați toate acestea.

Dacă doriți să urmați acest tutorial, puteți urma toți pașii sau descărcați codul de aici și urmați cu codul completat.

Acum am început să creăm proiectul și serverul nostru fără server. Trebuie să pornim dintr-un terminal și să executăm comanda pentru a crea noua noastră repo. Tot ce trebuie să faceți este să opriți {YOUR FOLDER NAME} pentru numele folderului.

serverless create --template aws-nodejs-typescript --path {YOUR FOLDER NAME}

Aceasta va crea un proiect fără server foarte de bază cu TypeScript. Dacă deschidem acest nou folder cu codul VS, atunci putem vedea ce ne-a dat șablonul.

Ghidul complet pentru construirea unui API cu TypeScript si AWS

Fișierele principale pe care dorim să le examinăm sunt serverless.ts fișier și handler.ts fişier.

serverless.ts fișierul este locul unde se păstrează configurația pentru implementare. Acest fișier spune cadrului fără server numele proiectului, limbajul de rulare al codului, lista de funcții și câteva alte opțiuni de configurare.

Ori de câte ori dorim să schimbăm arhitectura proiectului nostru, acesta este fișierul în care vom lucra.

Următorul fișier este handler.ts fişier. Aici avem exemplul de cod pentru un lambda dat de șablon. Este foarte simplu și returnează doar un răspuns API Gateway cu un mesaj și evenimentul de intrare. Vom folosi acest lucru mai târziu ca un bloc de pornire pentru propriul nostru API.

Creați-vă propriul Lambda

Acum că am văzut ce obținem cu șablonul, este timpul să adăugăm propriul nostru punct final Lambda și API.

Pentru început, vom crea un nou folder pentru a păstra tot codul nostru lambda și a-l apela lambdas. Acest lucru ajută la organizarea acestuia, mai ales când începeți să obțineți câteva lambda diferite într-un singur proiect.

În acel nou folder vom crea noul nostru lambda numindu-l getCityInfo.ts. Dacă deschidem acest fișier, putem începe să ne creăm codul. Putem începe prin copierea tuturor handler.ts cod ca punct de plecare.

Primul lucru pe care îl vom face este să schimbăm numele funcției în handler. Aceasta este o preferință personală, dar îmi place să numesc funcția care se ocupă de gestionarea evenimentelor.

Pe prima linie a acestei funcții trebuie să adăugăm un cod pentru a obține orașul pe care utilizatorul îl solicită. Putem obține acest lucru din calea URL folosind pathParameters.

const city = event.pathparameter?.city;

Un lucru pe care îl puteți observa este utilizarea ?. în declarația respectivă. Aceasta este opțională înlănțuire și este o caracteristică foarte interesantă. Eu

t înseamnă dacă parametrul cale este adevărat, atunci obțineți parametrul orașului, altfel reveniți nedefinit. Aceasta înseamnă dacă pathParameter nu era un obiect, acest lucru nu ar primi cannot read property city of undefined eroare care determină eroarea Runtime Node.

Acum, că avem orașul, trebuie să verificăm dacă orașul este valid și că avem date pentru orașul respectiv. Pentru aceasta avem nevoie de câteva date. Putem folosi codul de mai jos și îl putem lipi în partea de jos a fișierului.

interface CityData {
    name: string;
    state: string;
    description: string;
    mayor: string;
    population: number;
    zipCodes?: string;
}

const cityData: { [key: string]: CityData } = {
    newyork: {
        name: 'New York',
        state: 'New York',
        description:
            'New York City comprises 5 boroughs sitting where the Hudson River meets the Atlantic Ocean. At its core is Manhattan, a densely populated borough that’s among the world’s major commercial, financial and cultural centers. Its iconic sites include skyscrapers such as the Empire State Building and sprawling Central Park. Broadway theater is staged in neon-lit Times Square.',
        mayor: 'Bill de Blasio',
        population: 8399000,
        zipCodes: '100xx–104xx, 11004–05, 111xx–114xx, 116xx',
    },
    washington: {
        name: 'Washington',
        state: 'District of Columbia',
        description: `DescriptionWashington, DC, the U.S. capital, is a compact city on the Potomac River, bordering the states of Maryland and Virginia. It’s defined by imposing neoclassical monuments and buildings – including the iconic ones that house the federal government’s 3 branches: the Capitol, White House and Supreme Court. It's also home to iconic museums and performing-arts venues such as the Kennedy Center.`,
        mayor: 'Muriel Bowser',
        population: 705549,
    },
    seattle: {
        name: 'Seattle',
        state: 'Washington',
        description: `DescriptionSeattle, a city on Puget Sound in the Pacific Northwest, is surrounded by water, mountains and evergreen forests, and contains thousands of acres of parkland. Washington State’s largest city, it’s home to a large tech industry, with Microsoft and Amazon headquartered in its metropolitan area. The futuristic Space Needle, a 1962 World’s Fair legacy, is its most iconic landmark.`,
        mayor: 'Jenny Durkan',
        population: 744955,
    },
};

Diferența dintre acesta și JavaScript este că putem crea un interfață pentru a spune sistemului care trebuie să fie structura datelor. Acest lucru se simte ca o muncă suplimentară la început, dar vă va ajuta să faceți totul mai ușor mai târziu.

În interfața noastră definim cheile obiectului orașului; unele care sunt șiruri, un număr și apoi zipCodes este o proprietate opțională. Aceasta înseamnă că ar putea fi acolo, dar nu trebuie să fie.

Dacă dorim să ne testăm interfața, putem încerca să adăugăm o nouă proprietate la oricare dintre orașele din datele orașului nostru.

TypeScript ar trebui să vă spună instantaneu că noua dvs. proprietate nu există pe interfață. Dacă ștergeți una dintre proprietățile necesare, TypeScript se va plânge, de asemenea. Acest lucru vă asigură că aveți întotdeauna datele corecte și obiectele arată întotdeauna exact așa cum vă așteptați.

Acum, că avem datele, putem verifica dacă utilizatorul a trimis solicitarea de oraș corectă.

if (!city || !cityData[city]) {
    
}

Dacă această afirmație este adevărată, atunci utilizatorul a făcut ceva greșit, de aceea trebuie să returnăm un răspuns de 400.

Am putea tasta manual codul aici, dar vom crea un nou apiResponses obiectează cu metode pentru câteva dintre posibilele coduri de răspuns API.

const apiResponses = {
    _200: (body: { [key: string]: any }) => {
        return {
            statusCode: 200,
            body: JSON.stringify(body, null, 2),
        };
    },
    _400: (body: { [key: string]: any }) => {
        return {
            statusCode: 400,
            body: JSON.stringify(body, null, 2),
        };
    },
};

Acest lucru face mult mai ușor reutilizarea ulterioară în fișier. De asemenea, ar trebui să vedeți că avem o proprietate a body: { [key: string]: any }. Aceasta afirmă că această funcție are o proprietate a corpului care trebuie să fie un obiect. Acel obiect poate avea chei care au o valoare de orice tip.

Pentru că știm asta body va fi întotdeauna un șir pe care îl putem folosi JSON.stringify pentru a ne asigura că returnăm un corp de șir.

Dacă adăugăm această funcție la handlerul nostru, obținem acest lucru:

export const handler: APIGatewayProxyHandler = async (event, _context) => {
    const city = event.pathParameters?.city;

    if (!city || !cityData[city]) {
        return apiResponses._400({ message: 'missing city or no data for that city' });
    }

    return apiResponses._200(cityData[city]);
};

Dacă utilizatorul nu a renunțat la un oraș sau a renunțat la unul pentru care nu avem date, returnăm un 400 cu un mesaj de eroare. Dacă datele există, atunci returnăm un 200 cu un corp de date.

Adăugarea unei noi Traduceri API

În secțiunea anterioară, am configurat repoarea API TypeScript și am creat un lambda care tocmai a folosit date codificate.

Această parte vă va învăța cum să utilizați aws-sdk să interacționeze direct cu alte servicii AWS pentru a crea un API cu adevărat puternic.

Pentru a începe, trebuie să adăugăm un fișier nou pentru API-ul nostru de traducere. Creați un fișier nou sub lambdas dosar numit translate.ts. Putem începe acest fișier cu un cod de bază pentru cazan. Acesta este codul de pornire pentru un API TypeScript Lambda.

import { APIGatewayProxyHandler } from 'aws-lambda';
import 'source-map-support/register';

export const handler: APIGatewayProxyHandler = async (event) => {
    
};

Acum trebuie să obținem textul pe care utilizatorul îl dorește tradus și limba în care dorește să traducă. Le putem obține din corpul cererii.

Un lucru în plus pe care trebuie să-l facem aici este să analizăm corpul. În mod implicit, API Gateway strânge orice JSON transmis în corp. Putem distruge textul și limbajul din corp.

const body = JSON.parse(event.body);
const { text, language } = body;

Acum trebuie să verificăm dacă utilizatorul a transmis textul și limba.

if (!text) {
    // retrun 400
}
if (!language) {
    // return 400
}

În ultima parte am creat răspunsul 400 ca funcție în fișier. Întrucât vom folosi aceste răspunsuri API pe mai multe fișiere, este o idee bună să le scoatem în cont propriu uzual fişier.

Creați un folder nou sub lambdas numit common. Aici vom stoca toate funcțiile comune.

În acel folder creați un fișier nou numit apiResponses.ts. Acest fișier va exporta fișierul apiResponses obiect cu _200 și _400 metode pe el. Dacă trebuie să returnați alte coduri de răspuns, le puteți adăuga la acest obiect.

const apiResponses = {
    _200: (body: { [key: string]: any }) => {
        return {
            statusCode: 200,
            body: JSON.stringify(body, null, 2),
        };
    },
    _400: (body: { [key: string]: any }) => {
        return {
            statusCode: 400,
            body: JSON.stringify(body, null, 2),
        };
    },
};

export default apiResponses;

Acum putem importa acel obiect în codul nostru și putem folosi aceste metode comune în codul nostru. În partea de sus a noastră traduce.ts fișier putem adăuga acum această linie:

import apiResponses from './common/apiResponses';

și actualizați verificările de text și limbă pentru a apela la _400 metoda pe acel obiect:

if (!text) {
    return apiResponses._400({ message: 'missing text fom the body' });
}
if (!language) {
    return apiResponses._400({ message: 'missing language from the body' });
}

Odată finalizat, știm că avem textul de tradus și o limbă în care să traducem, astfel încât să putem începe procesul de traducere.

Folosirea aws-sdk este aproape întotdeauna o sarcină asincronizată, așa că o vom înfășura într-un încearcă să prinzi astfel încât gestionarea erorilor noastre să fie mai ușoară.

try {
    
} catch (error) {
    
}

Primul lucru pe care trebuie să-l facem este să importăm aws-sdk și să creăm o nouă instanță a serviciului de traducere.

Pentru a face acest lucru, trebuie să instalăm aws-sdk și apoi să îl importăm. Prima alergare npm install --save aws-sdk și apoi adăugați acest cod în partea de sus a fișierului dvs. de traducere:

import * as AWS from 'aws-sdk';

const translate = new AWS.Translate();

Cu aceasta putem începe să scriem codul nostru de traducere. Vom începe cu linia care face traducerea mai întâi. Adăugați acest lucru în încerca secțiune.

const translatedMessage = await translate.translateText(translateParams).promise();

Un lucru pe care unii dintre voi l-ați observat este că trecem translateParams fără să-l fi definit încă. Asta pentru că nu suntem siguri de ce tip este încă.

Pentru a afla acest lucru, putem folosi un instrument din codul VS numit go to definition. Acest lucru ne permite să saltăm acolo unde funcția este definită, astfel încât să putem afla care este tipul parametrilor. Puteți face clic dreapta și selecta go to definition sau țineți Ctrl și faceți clic pe funcție.

1611182468 453 Ghidul complet pentru construirea unui API cu TypeScript si AWS

După cum puteți vedea translateText funcția are un parametru de Translate.Types.TranslateTextRequest.

O altă modalitate de a afla acest lucru este de a utiliza intelisense prin trecerea mouse-ului peste translateText funcţie. Ar trebui să vedeți acest lucru, unde puteți vedea asta params: AWS.Translate.TranslateTextRequest:

1611182468 814 Ghidul complet pentru construirea unui API cu TypeScript si AWS

Cu aceasta ne putem crea parametrii de traducere peste cererea de traducere pe care am făcut-o mai devreme. Putem apoi să-l populăm în funcție de tipul în care îl setăm. Acest lucru ne asigură că trecem câmpurile corecte.

const translateParams: AWS.Translate.Types.TranslateTextRequest = {
    Text: text,
    SourceLanguageCode: 'en',
    TargetLanguageCode: language,
};

Acum că avem parametrii și îi trecem în translate.translateText funcție, putem începe să ne creăm răspunsul. Acesta va fi doar un răspuns 200 cu mesajul tradus.

return apiResponses._200({ translatedMessage });

Cu toate acestea, putem trece pe captură secțiune. Aici vrem doar să deconectăm eroarea și apoi să returnăm un răspuns 400 din fișierul comun.

console.log('error in the translation', error);
return apiResponses._400({ message: 'unable to translate the message' });

După ce am terminat, am terminat cu codul nostru lambda, așa că trebuie să ne mutăm în al nostru severless.ts fișier pentru a adăuga acest nou punct final API și a-i da permisiunile de care are nevoie.

În serverless.ts fișier putem derula în jos la functions secțiune. Aici trebuie să adăugăm o nouă funcție obiectului.

translate: {
    handler: 'lambdas/translate.handler',
    events: [
        {
            http: {
                path: 'translate',
                method: 'POST',
                cors: true,
            },
        },
    ],
},

Principala diferență între acesta și punctul final anterior este că punctul final este acum un POST metodă. Aceasta înseamnă că dacă încercați să faceți un OBȚINE la această cale URL, veți primi un răspuns la eroare.

Ultimul lucru de făcut este să acordați permisiunea lambdas de a utiliza serviciul Traducere. Cu aproape toate serviciile AWS, va trebui să adăugați permisiuni suplimentare pentru a putea folosi din lambda.

Pentru a face acest lucru, adăugăm un nou câmp pe provider secțiunea numită iamRoleStatements. Aceasta este o serie de permite sau nega declarații pentru diferite servicii și resurse.

iamRoleStatements: [
    {
        Effect: 'Allow',
        Action: ['translate:*'],
        Resource: '*',
    },
],

Cu acest lucru adăugat, avem tot ceea ce avem nevoie configurat, astfel încât să putem rula sls deploy pentru a implementa noul nostru API.

Odată ce acest lucru a fost implementat, putem obține adresa URL a API-ului și putem folosi un instrument precum poștașul sau poștașă.io pentru a face o solicitare către adresa URL respectivă. Trebuie doar să renunțăm la un corp de:

{
    "text": "This is a test message for translation",
    "language": "fr"
}

și atunci ar trebui să obținem un răspuns de 200 de:

{
  "translatedMessage": {
    "TranslatedText": "Ceci est un message de test pour la traduction",
    "SourceLanguageCode": "en",
    "TargetLanguageCode": "fr"
  }
}

rezumat

În acest articol am învățat cum să:

  • Configurați un nou depozit TypeScript cu severless create --template aws-nodejs-typescript
  • Adăugați propriul nostru Lambda care returnează o selecție de date codificate
  • A fost adăugat Lambda ca punct final API
  • S-a adăugat un alt Lambda care va traduce automat orice text transmis acestuia
  • A adăugat un punct final API și i-a dat Lambda permisiunile de care avea nevoie pentru a funcționa

Dacă ți-a plăcut acest articol și vrei să afli mai multe despre Serverless și AWS, atunci am un canal Youtube cu peste 50 de videoclipuri despre toate acestea. Aș recomanda să vizionați videoclipurile pe care vi le considerați cele mai interesante Listă de redare fără server și AWS.