Do you want to learn to build a TypeScript API that works seamlessly with other AWS services? If you do then this article is for you!

In the previous article we set up our TypeScript API repo and created a lambda which just used hard coded data. This article is going to teach you how to use the aws-sdk to interact directly with other AWS services to create a really powerful API.

To start we need to add a new file for our translation API. Create a new file under the lambdas folder called translate.ts. We can start this file out with some basic boilerplate code. This is the starting code for a TypeScript API Lambda.

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

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

Now we need to get the text that the user wants translated and the language that they want to translate to. We can get these from the body of the request. One extra thing we have to do here is to parse the body. By default, API Gateway stringifies any JSON passed in the body. We can then destructure the text and language off the body.

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

We now need to check that the user has passed up text and language.

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

In the last article 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 clicking on the function.

As you can see the translateText function takes a params 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 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"
  }
}