Комплетан водич за изградњу АПИ-ја са ТипеСцрипт-ом и АВС-ом

У овом чланку ћемо размотрити како можемо брзо и једноставно да направимо АПИ помоћу ТипеСцрипт-а и Серверлесс-а.

Затим ћемо научити како да користимо авс-сдк за приступ другим АВС услугама и креирамо АПИ за аутоматско превођење.

Ако више волите да гледате и учите, можете погледати видео испод:

Почетак

Да бисмо започели читав овај процес, морамо бити сигурни да имамо инсталиран Серверлесс Фрамеворк и да је на нашем рачунару постављен АВС профил. Ако нисте, можете погледати овај видео о томе како то све подесити.

Ако желите да пратите овај водич, можете следити све кораке или овде преузети код и следити га са попуњеним кодом.

Сада смо креирали наш пројекат и АПИ без сервера. Морамо започети у терминалу и покренути команду да креирамо наш нови репо. Све што треба да урадите је да искључите {YOUR FOLDER NAME}име вашег фолдера.

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

Ово ће створити врло основни пројекат без сервера са ТипеСцрипт-ом. Ако отворимо ову нову фасциклу са ВС кодом, тада можемо видети шта нам је предложак дао.

Главне датотеке које желимо да погледамо су serverless.tsдатотека и handler.tsдатотека.

serverless.tsДатотека је место где се одржава конфигурација за распоређивање. Ова датотека беспомоћном оквиру каже назив пројекта, рунтиме језик кода, списак функција и неколико других опција конфигурације.

Кад год желимо да променимо архитектуру нашег пројекта, ово је датотека у којој ћемо радити.

Следећа датотека је handler.tsдатотека. Овде имамо пример кода за ламбда који нам је дао образац. Веома је основно и само враћа АПИ Гатеваи одговор с поруком и улазним догађајем. Касније ћемо ово користити као почетни блок за наш сопствени АПИ.

Направите сопствену Ламбду

Сад кад смо видели шта добијамо са шаблоном, време је да додамо сопствену Ламбда и АПИ крајњу тачку.

За почетак ћемо направити нову фасциклу у којој ће бити сав наш ламбда код и позвати га lambdas. Ово помаже у организацији, нарочито када почнете да добијате неколико различитих ламбда у једном пројекту.

У тој новој фасцикли креираћемо нашу нову ламбда која ће је назвати getCityInfo.ts. Ако отворимо ову датотеку, можемо започети стварање нашег кода. Можемо започети копирањем читавог handler.tsкода као почетне тачке.

Прво што ћемо урадити је да променимо име функције у handler. Ово је лична преференција, али волим да именујем функцију која обрађује обрађивач догађаја.

У првом реду ове функције треба да додамо неки код да бисмо добили град који корисник захтева. То можемо добити из УРЛ путање помоћу pathParameters.

const city = event.pathparameter?.city;

Једна ствар коју можете приметити је употреба ?.те изјаве. То је опционално уланчавање и заиста је добра карактеристика. Ја

т значи ако је параметар пута истинит, тада узмите параметар цити, у супротном вратите ундефинед. То значи да ако pathParameterније објекат , ово не би добило грешку која узрокује грешку у извршавању чвора.cannot read property city of undefined

Сада када имамо град морамо да проверимо да ли је град валидан и да ли имамо податке за тај град. За ово су нам потребни неки подаци. Можемо користити доњи код и залепити га на дну датотеке.

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, }, };

Разлика између овог и ЈаваСцрипт-а је у томе што можемо створити интерфејс који ће систему рећи каква мора бити структура података. Ово вам се чини као додатни посао у старту, али ће вам касније олакшати.

Унутар нашег интерфејса дефинишемо кључеве градског објекта; неки су низови, један број, а затим zipCodesје необавезна особина. То значи да би могао бити тамо, али не мора бити.

Ако желимо да тестирамо наш интерфејс, можемо покушати да додамо ново својство било којем од градова у подацима о нашем граду.

ТипеСцрипт би вам одмах требао рећи да ваше ново својство не постоји на интерфејсу. Ако избришете једно од потребних својстава, ТипеСцрипт ће се такође жалити. Ово осигурава да увек имате тачне податке и да објекти изгледају тачно онако како се очекивало.

Сада када имамо податке можемо да проверимо да ли је корисник послао тачан градски захтев.

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

Ако је ова изјава тачна, онда је корисник учинио нешто погрешно, зато морамо да вратимо 400 одговора.

Овде бисмо могли само ручно да укуцамо код, али креираћемо нови apiResponsesобјекат са методама за неколико могућих кодова АПИ одговора.

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), }; }, };

Ово само олакшава поновну употребу касније у датотеци. Такође би требало да видите да имамо једно својство body: { [key: string]: any }. Ово наводи да ова функција има једно својство тела које треба да буде објекат. Тај објекат може имати кључеве који имају вредност било које врсте.

Јер знамо да ће bodyто увек бити низ који можемо користити JSON.stringifyда бисмо били сигурни да ћемо вратити тело низа.

Ако ову функцију додамо нашем обрађивачу, добићемо ово:

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]); };

Ако корисник није пропустио град или град за који немамо података, вратит ћемо 400 са поруком о грешци. Ако подаци постоје, вратит ћемо 200 са тијелом података.

Додавање новог АПИ-ја за превођење

У претходном одељку смо поставили наш ТипеСцрипт АПИ репо и креирали ламбда који је управо користио тврдо кодиране податке.

Овај део ће вас научити како да користите авс-сдк за директну интеракцију са другим АВС услугама како бисте створили заиста моћан АПИ.

За почетак морамо додати нову датотеку за наш АПИ за превођење. Направите нову датотеку у lambdasфасцикли под називом translate.ts. Ову датотеку можемо започети неким основним кодом. Ово је почетни код за ТипеСцрипт АПИ Ламбда.

import { APIGatewayProxyHandler } from 'aws-lambda'; import 'source-map-support/register'; export const handler: APIGatewayProxyHandler = async (event) => { };

Сада морамо добити текст који корисник жели да преведе и језик на који жели да преведе. Ово можемо добити из тела захтева.

Једна додатна ствар коју овде морамо да урадимо је да рашчланимо тело. Подразумевано, АПИ мрежни пролаз стрингификује било који ЈСОН прослеђен у телу. Тада можемо деструктурирати текст и језик из тела.

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

Сада морамо да проверимо да ли је корисник пренео текст и језик.

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

In the last part we created the 400 response as a function in the file. As we're going to be using these API responses across multiple files, it is a good idea to pull them out to their own common file.

Create a new folder under lambdas called common. This is where we are going to store all common functions.

In that folder create a new file called apiResponses.ts. This file is going to export the apiResponses object with the _200 and _400 methods on it. If you have to return other response codes then you can add them to this object.

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;

We can now import that object into our code and use these common methods in our code. At the top of our translate.ts file we can now add this line:

import apiResponses from './common/apiResponses';

and update our text and language checks to call the _400 method on that object:

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

With that completed we know that we have the text to translate and a language to translate into, so we can start the translation process.

Using the aws-sdk is almost always an async task so we're going to wrap it in a try/catch so that our error handling is easier.

try { } catch (error) { }

The first thing we need to do is to import the aws-sdk and create a new instance of the translate service.

To do that we need to install the aws-sdk and then import it. First run npm install --save aws-sdk and then add this code to the top of your translate file:

import * as AWS from 'aws-sdk'; const translate = new AWS.Translate();

With this we can start to write our translation code. We're going to start with the line that does the translation first. Add this in the try section.

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

One thing that some of you may have noticed is that we're passing in translateParams without having defined it yet. That is because we're not sure what type it is yet.

To find this out we can use a tool in VS Code called go to definition. This allows us to jump to where the function if defined so we can find out what the type of the parameters is. You can either right click and select go to definition or hold Ctrl and click on the function.

As you can see the translateText function takes a param of Translate.Types.TranslateTextRequest.

Another way to find this out is to use intelisense by mousing over the translateText function. You should see this, where you can see that params: AWS.Translate.TranslateTextRequest:

With this we can create our translate params above the translate request we made earlier. We can then populate it based on the type we are setting it as. This makes sure we're passing up the correct fields.

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

Now that we have the parameters and are passing them into the translate.translateText function, we can start creating our response. This is just going to be a 200 response with the translated message.

return apiResponses._200({ translatedMessage });

With that all done we can move onto the catch section. In here we just want to log out the error and then return a 400 response from the common file.

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

With that completed we're done with our lambda code, so need to move into our severless.ts file to add this new API endpoint and give it the permissions it needs.

In the serverless.ts file we can scroll down to the functions section. In here we need to add a new function to the object.

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

The main difference between this and the previous endpoint is that the endpoint is now a POST method. This means if you try and do a GET request to this URL path, you'll get an error response.

The last thing to do is to give the lambdas permission to use the Translate service. With almost all of the AWS Services, you'll need to add extra permissions to be able to use the from within a lambda.

To do this we add a new field onto the provider section called iamRoleStatements. This is an array of allow or deny statements for different services and resources.

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

With this added in we have everything we need set up so we can run sls deploy to deploy our new API.

Once this has deployed, we can get the API URL and use a tool like postman or postwoman.io to make a request to that URL. We just need to pass up a body of:

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

and then we should get a 200 response of:

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

Summary

In this article we've learnt how to:

  • Set up a new TypeScript repo with severless create --template aws-nodejs-typescript
  • Add our own Lambda that returns a selection of hardcoded data
  • Added that Lambda as an API endpoint
  • Added another Lambda which will automatically translate any text passed to it
  • Added an API endpoint and gave the Lambda the permissions it needed to work

Ако вам се свидео овај чланак и желите да сазнате више о Серверлесс и АВС, онда имам Иоутубе канал са преко 50 видео записа о свему овоме. Препоручио бих да погледате видео записе који су вам најзанимљивији на мојој плејлисти без сервера и АВС.