U ovom ćemo članku razmotriti kako možemo brzo i jednostavno izgraditi API s TypeScriptom i bez poslužitelja.
Zatim ćemo naučiti kako koristiti aws-sdk za pristup drugim AWS uslugama i stvoriti API za automatsko prevođenje.
Ako više volite gledati i učiti, možete pogledati video u nastavku:
Početak rada
Da bismo započeli cijeli ovaj postupak, moramo biti sigurni da imamo instaliran Serverless Framework i da je na našem računalu postavljen AWS profil. Ako niste, možete pogledati ovaj videozapis o tome kako to sve postaviti.
Ako želite slijediti ovaj vodič, možete slijediti sve korake ili ovdje preuzeti kôd i slijediti ga s ispunjenim kodom.
Sada krećemo u izradu našeg projekta i API-ja bez poslužitelja. Moramo započeti u terminalu i pokrenuti naredbu za stvaranje našeg novog repo-a. Sve što trebate učiniti je isključiti {YOUR FOLDER NAME}
ime svoje mape.
serverless create --template aws-nodejs-typescript --path {YOUR FOLDER NAME}
To će stvoriti vrlo osnovni projekt bez poslužitelja s TypeScriptom. Ako otvorimo ovu novu mapu s VS kodom, tada možemo vidjeti što nam je predložak dao.

Glavne datoteke koje želimo pogledati su serverless.ts
datoteka i handler.ts
datoteka.
serverless.ts
Datoteka u kojoj se održava konfiguracija za implementaciju. Ova datoteka bespomoćnom okviru kaže naziv projekta, runtime jezik koda, popis funkcija i nekoliko drugih mogućnosti konfiguracije.
Kad god želimo promijeniti arhitekturu našeg projekta, ovo je datoteka u kojoj ćemo raditi.
Sljedeća datoteka je handler.ts
datoteka. Ovdje imamo primjer koda za lambdu koji nam je dao predložak. Vrlo je osnovno i samo vraća odgovor API pristupnika s porukom i ulaznim događajem. Kasnije ćemo to koristiti kao početni blok za naš vlastiti API.
Stvorite vlastiti Lambda
Sad kad smo vidjeli što dobivamo s predloškom, vrijeme je da dodamo vlastitu krajnju točku Lambda i API.
Za početak ćemo napraviti novu mapu koja će sadržavati sav naš lambda kôd i nazvati ga lambdas
. To vam pomaže u organizaciji, pogotovo kad počnete uzimati nekoliko različitih lambda u jednom projektu.
U toj novoj mapi napravit ćemo našu novu lambda koja će je nazvati getCityInfo.ts
. Ako otvorimo ovu datoteku, možemo početi stvarati naš kod. Možemo započeti kopiranjem cijelog handler.ts
koda kao početne točke.
Prvo što ćemo učiniti je promijeniti naziv funkcije u handler
. Ovo je osobna preferencija, ali volim imenovati funkciju koja obrađuje rukovatelj događajima.
U prvi redak ove funkcije moramo dodati neki kod da bismo dobili grad koji korisnik traži. To možemo dobiti iz URL puta koristeći pathParameters
.
const city = event.pathparameter?.city;
Jedna stvar koju ćete primijetiti je upotreba ?.
te izjave. To je neobavezno ulančavanje i zaista je super značajka. Ja
t znači ako je parametar puta istinit, tada dobijte parametar grada, u suprotnom vratite se nedefinirano. To znači da ako pathParameter
nije objekt , ovo ne bi dovelo do pogreške koja uzrokuje pogrešku u izvršavanju čvora.cannot read property city of undefined
Sad kad imamo grad, moramo provjeriti je li grad valjan i imamo li podatke za taj grad. Za to su nam potrebni neki podaci. Možemo koristiti donji kod i zalijepiti ga na dno datoteke.
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, }, };
Razlika između ovog i JavaScript-a je u tome što možemo stvoriti sučelje koje će sustavu reći kakva mora biti struktura podataka. To vam se čini kao dodatni posao u početku, ali kasnije će vam olakšati.
Unutar našeg sučelja definiramo tipke gradskog objekta; neki su nizovi, jedan broj, a zatim zipCodes
je neobavezno svojstvo. To znači da bi mogao biti tamo, ali ne mora biti.
Ako želimo testirati naše sučelje, možemo pokušati dodati novo svojstvo bilo kojem gradu u podacima o našem gradu.
TypeScript bi vam odmah trebao reći da vaše novo svojstvo ne postoji na sučelju. Ako izbrišete jedno od potrebnih svojstava, TypeScript će se također žaliti. To osigurava da uvijek imate točne podatke i da objekti uvijek izgledaju točno onako kako se očekivalo.
Sad kad imamo podatke možemo provjeriti je li korisnik poslao ispravan gradski zahtjev.
if (!city || !cityData[city]) { }
Ako je ova izjava istinita, tada je korisnik učinio nešto pogrešno, stoga moramo vratiti 400 odgovora.
Ovdje bismo mogli samo ručno upisati kod, ali stvorit ćemo novi apiResponses
objekt s metodama za nekoliko mogućih kodova API odgovora.
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), }; }, };
To samo olakšava ponovnu upotrebu kasnije u datoteci. Također biste trebali vidjeti da imamo jedno svojstvo body: { [key: string]: any }
. Ovo govori da ova funkcija ima jedno svojstvo tijela koje treba biti objekt. Taj objekt može imati ključeve koji imaju vrijednost bilo koje vrste.
Jer znamo da će body
to uvijek biti niz koji možemo koristiti JSON.stringify
da bismo osigurali da vratimo tijelo niza.
Ako ovu funkciju dodamo našem obrađivaču, dobit ćemo sljedeće:
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]); };
Ako korisnik nije propustio grad ili prošao grad za koji nemamo podataka, vratit ćemo 400 s porukom o pogrešci. Ako podaci postoje, vratit ćemo 200 s tijelom podataka.
Dodavanje novog API-ja za prijevod
U prethodnom smo odjeljku postavili svoj TypeScript API repo i stvorili lambda koji je upravo koristio tvrdo kodirane podatke.
Ovaj će vas dio naučiti kako koristiti aws-sdk za izravnu interakciju s drugim AWS uslugama kako biste stvorili stvarno moćan API.
Za početak moramo dodati novu datoteku za naš prijevodni API. Stvorite novu datoteku u lambdas
mapi pod nazivom translate.ts
. Ovu datoteku možemo započeti s nekim osnovnim kodom. Ovo je početni kôd za TypeScript API Lambda.
import { APIGatewayProxyHandler } from 'aws-lambda'; import 'source-map-support/register'; export const handler: APIGatewayProxyHandler = async (event) => { };
Sada moramo dobiti tekst koji korisnik želi prevesti i jezik na koji želi prevesti. Te podatke možemo dobiti iz tijela zahtjeva.
Jedna dodatna stvar koju ovdje moramo učiniti je raščlamba tijela. Prema zadanim postavkama, API pristupnik stringificira bilo koji JSON proslijeđen u tijelu. Tada možemo destrukturirati tekst i jezik iz tijela.
const body = JSON.parse(event.body); const { text, language } = body;
Sada moramo provjeriti je li korisnik prenio tekst i jezik.
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
If you enjoyed this article and want to learn more about Serverless and AWS, then I have a Youtube Channel with over 50 videos on all of this. I'd recommend watching the videos you find most interesting in my Serverless and AWS playlist.