Developing CRUD APIs for movie services
With serverless technology, you can create a CRUD
The CRUD API runs inside a container in Yandex Serverless Containers for accessing the movie database deployed in Yandex Managed Service for YDB.
The container is configured in the OpenAPI 3.0
The container will communicate with Managed Service for YDB and handle external HTTP requests via an API gateway using the Amazon DynamoDB
To deploy a project:
- Set up your environment.
- Initialize Terraform.
- Create a Managed Service for YDB database.
- Implement CRUD operations.
- Develop a REST API.
- Test the new CRUD API.
If you no longer need the resources you created, delete them.
Getting started
Sign up in Yandex Cloud and create a billing account:
- Navigate to the management console
and log in to Yandex Cloud or register a new account. - On the Yandex Cloud Billing
page, make sure you have a billing account linked and it has theACTIVE
orTRIAL_ACTIVE
status. If you do not have a billing account, create one and link a cloud to it.
If you have an active billing account, you can navigate to the cloud page
Learn more about clouds and folders.
Required paid resources
The cost of CRUD API resources includes:
- Fee for YDB operations and data storage (see Managed Service for YDB pricing for serverless mode).
- Fee for container invocation count, computing resources allocated to run the application, and outbound traffic (see Serverless Containers pricing).
- Fee for the number of requests to the API gateway and outbound traffic (see API Gateway pricing).
Set up your environment
- Install WSL
to run a Linux environment. - Run the Linux subsystem (by default, Ubuntu).
- Configure the environment as described in this tutorial for Linux.
Note
If you are using a distribution other than Ubuntu, install the specified tools using your package manager.
-
Install the following tools in the specified order by running the relevant commands in your terminal:
-
sudo apt-get install curl git -y
-
WebStorm
or any other TypeScript-enabled IDE :sudo snap install webstorm --classic
-
Node.js
16.9.1
or higher:curl --silent --location https://deb.nodesource.com/setup_16.x | sudo -E bash sudo apt-get install nodejs node -v npm -v
-
sudo npm install -g typescript
-
curl https://storage.yandexcloud.net/yandexcloud-yc/install.sh | bash exec -l $SHELL yc version
-
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" --output "awscliv2.zip" unzip awscliv2.zip sudo ./aws/install
-
sudo apt-get update sudo apt-get install apt-transport-https ca-certificates curl gnupg lsb-release -y curl --fail --silent --show-error --location https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt-get update sudo apt-get install docker-ce docker-ce-cli containerd.io -y sudo docker run hello-world
-
Terraform
1.0.8
or higher:sudo apt-get update && sudo apt-get install -y gnupg software-properties-common curl curl --fail --silent --show-error --location https://apt.releases.hashicorp.com/gpg | sudo apt-key add - sudo apt-add-repository "deb [arch=$(dpkg --print-architecture)] https://apt.releases.hashicorp.com $(lsb_release -cs) main" sudo apt-get update && sudo apt-get install terraform -y terraform version
-
-
Create a Yandex Cloud CLI profile with basic settings.
-
Set up the AWS CLI.
-
Set up
Docker so that a non-root user can manage it:sudo groupadd docker sudo usermod -aG docker $USER newgrp docker docker run hello-world
-
Install the following tools in the specified order by running the relevant commands in your terminal:
-
/bin/bash -c "$(curl --fail --silent --show-error --location https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
-
brew install curl git
-
WebStorm
or any other TypeScript-enabled IDE :brew install --cask webstorm
-
Node.js
16.9.1
or higher:brew install node node -v npm -v
-
npm install -g typescript
-
curl https://storage.yandexcloud.net/yandexcloud-yc/install.sh | bash exec -l $SHELL yc version
-
curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" --output "AWSCLIV2.pkg" sudo installer -pkg AWSCLIV2.pkg -target /
-
brew install --cask docker
-
Terraform
1.0.8
or higher:brew tap hashicorp/tap brew install hashicorp/tap/terraform terraform version
-
-
Create a profile with basic settings.
-
Set up the AWS CLI.
Initiate Terraform
-
Clone a repository with source files for the CRUD API project:
git clone https://github.com/yandex-cloud-examples/yc-practicum-serverless-web-application-movie-website.git
Open the project directory in WebStorm and review the source files.
-
Go to the
deploy
directory:cd <path_to_deploy_directory>
-
Find out the name of the
ACTIVE
profile of the Yandex Cloud CLI. In the terminal, run this command:yc config profile list
-
Get the active profile properties:
yc config profile get <profile_name>
-
Copy these properties to provider.tf
:token
: OAuth token.cloud-id
: Cloud ID.folder-id
: Folder ID.
-
Export the folder ID to the environment variable:
export FOLDER_ID=<folder_ID> echo $FOLDER_ID
-
Run the Terraform initialization command:
terraform init
Note
Run all Terraform commands in the
deploy
directory.
Create a Managed Service for YDB database
Our project uses a YDB database in serverless mode. This database consists of two tables: movies
to keep movie data and votes
to keep user ratings. Each table entry contains the ID and fixed attributes.
-
The Terraform configuration for database creation is described in ydb.tf
. Create a database:terraform apply -target=yandex_ydb_database_serverless.movies_database
Confirm creating the resources by typing
yes
in the terminal and pressing Enter.You will see these variables in the command output:
movies_database_document_api_endpoint
: Document API endpoint of the database.movies_database_path
: Relative path to the database.
Use the management console
or theyc ydb database list
CLI command to check that themovies-database
DB has been successfully created. -
Export the
movies_database_document_api_endpoint
andmovies_database_path
values from the previous command output to environment variables:export DOCUMENT_API_ENDPOINT=<DB_Document_API_endpoint> echo $DOCUMENT_API_ENDPOINT export MOVIES_DATABASE_PATH=<relative_path_to_database> echo $MOVIES_DATABASE_PATH
-
Create tables named
movies
andvotes
inmovies-database
:aws dynamodb create-table \ --table-name movies \ --attribute-definitions \ AttributeName=id,AttributeType=N \ AttributeName=title,AttributeType=S \ AttributeName=type,AttributeType=S \ AttributeName=original_title,AttributeType=S \ AttributeName=original_language,AttributeType=S \ AttributeName=release_date,AttributeType=S \ AttributeName=poster_path,AttributeType=S \ AttributeName=popularity,AttributeType=N \ AttributeName=video,AttributeType=S \ AttributeName=vote_count,AttributeType=N \ AttributeName=vote_average,AttributeType=N \ AttributeName=genres,AttributeType=S \ AttributeName=backdrop_path,AttributeType=S \ AttributeName=adult,AttributeType=S \ AttributeName=overview,AttributeType=S \ --key-schema \ AttributeName=id,KeyType=HASH \ --global-secondary-indexes \ "[ { \"IndexName\": \"PopularityIndex\", \"KeySchema\": [{\"AttributeName\":\"type\",\"KeyType\":\"HASH\"}, {\"AttributeName\":\"popularity\",\"KeyType\":\"RANGE\"}], \"Projection\":{ \"ProjectionType\":\"ALL\" } } ]" \ --endpoint ${DOCUMENT_API_ENDPOINT} aws dynamodb create-table \ --table-name votes \ --attribute-definitions \ AttributeName=id,AttributeType=S \ AttributeName=user_id,AttributeType=S \ AttributeName=movie_id,AttributeType=N \ AttributeName=value,AttributeType=N \ --key-schema \ AttributeName=id,KeyType=HASH \ --global-secondary-indexes \ "[ { \"IndexName\": \"MovieIndex\", \"KeySchema\": [{\"AttributeName\":\"movie_id\",\"KeyType\":\"HASH\"}], \"Projection\":{ \"ProjectionType\":\"ALL\" } } ]" \ --endpoint ${DOCUMENT_API_ENDPOINT}
-
Make sure the tables were successfully created:
aws dynamodb describe-table \ --table-name movies \ --endpoint ${DOCUMENT_API_ENDPOINT} aws dynamodb describe-table \ --table-name votes \ --endpoint ${DOCUMENT_API_ENDPOINT}
Each table supports two indexes:
- The
movies
table supports an index to quickly find a movie by its ID and an index to sort movies by popularity. - The
votes
table supports an index to locate a user's rating for a movie and an index to get all ratings for a movie.
- The
Implement CRUD operations
Every time you create, read, update, or delete data, the database layer is involved. These actions are collectively called CRUD operations.
In the Document API, you access the database using the AWS SDK for JavaScript v3
- model.ts
defines theMovie
andVote
models via the TypeScript interface. - repository.ws
implements CRUD operations for using these entities.
All data-related operations use IAM tokens for authorization. Any operation begins by calling the metadata service to get an IAM token.
Create a service account
-
The Terraform configuration for creating a service account is described in sa.tf
. Create a service account:terraform apply -target=yandex_iam_service_account.movies_api_sa
Confirm creating the resources by typing
yes
in the terminal and pressing Enter. -
In the command output, the
movies_api_sa_id
variable contains the ID of the new service account. Export it to the environment variable:export MOVIES_API_SA_ID=<service_account_ID> echo $MOVIES_API_SA_ID
-
Assign roles to the service account:
yc resource-manager folder add-access-binding ${FOLDER_ID} \ --role ydb.admin \ --subject serviceAccount:${MOVIES_API_SA_ID} yc resource-manager folder add-access-binding ${FOLDER_ID} \ --role container-registry.images.puller \ --subject serviceAccount:${MOVIES_API_SA_ID} yc resource-manager folder add-access-binding ${FOLDER_ID} \ --role serverless.containers.invoker \ --subject serviceAccount:${MOVIES_API_SA_ID}
Where:
--role
: Role you want to assign.--subject serviceAccount
: Service account ID.
The service account gets the roles for the following purposes:
- Invoking a container in Serverless Containers.
- Running operations in YDB.
Make sure to assign the roles for the entire folder rather than an individual resource.
Compile the application's TypeScript source code
-
Go to the repository root directory and install required dependencies with this command:
cd <path_to_sls-web-application_directory> npm ci
After running it, your project will include the
node_modules
directory with all required dependencies. -
Run your project build:
npm run build
After running it, your project will include the
dist
directory with the compiled JS files.
Develop a REST API
The openapi/api.yaml
To implement the service according to the specification, you will use OpenAPI Backend
Deploy your application in Serverless Containers
Build your application as a Docker image and run it in Serverless Containers:
-
In the OpenAPI specifications
(api.yaml
), enter the ID of the previously created service account in thex-yc-apigateway.service_account_id
field. -
The container-registry.tf
file defines the configuration of the registry and repository that will store the application Docker image. Go to thedeploy
directory and create resources in Yandex Container Registry:cd <path_to_deploy_directory> terraform apply -target=yandex_container_registry.default terraform apply -target=yandex_container_repository.movies_api_repository
Confirm creating the resources by typing
yes
in the terminal and pressing Enter. -
In the command output, the
movies_api_repository_name
variable contains the name of the repository to upload the Docker image to. Export it to the environment variable:export MOVIES_API_REPOSITORY_NAME=<repository_name> echo $MOVIES_API_REPOSITORY_NAME
-
Configure Docker to push images to the created repository:
yc container registry configure-docker
-
The Dockerfile
describes the configuration for building the Docker image. Build the image and push it to the repository:docker build -t ${MOVIES_API_REPOSITORY_NAME}:0.0.1 . docker push ${MOVIES_API_REPOSITORY_NAME}:0.0.1
-
Create a container in Serverless Containers:
yc sls container create \ --name movies-api-container \ --folder-id ${FOLDER_ID}
Where:
--name
: Container name.--folder-id
: Folder ID.
-
The command output gives the new container ID. Export it to the environment variable:
export MOVIES_API_CONTAINER_ID=<container_ID> echo $MOVIES_API_CONTAINER_ID
-
Create a container revision from Docker image
0.0.1
:yc sls container revisions deploy \ --folder-id ${FOLDER_ID} \ --container-id ${MOVIES_API_CONTAINER_ID} \ --memory 512M \ --cores 1 \ --execution-timeout 5s \ --concurrency 4 \ --environment AWS_ACCESS_KEY_ID=FAKE_AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY=FAKE_AWS_SECRET_ACCESS_KEY,DOCUMENT_API_ENDPOINT=${DOCUMENT_API_ENDPOINT} \ --service-account-id ${MOVIES_API_SA_ID} \ --image ${MOVIES_API_REPOSITORY_NAME}:0.0.1
Where:
--folder-id
: Folder ID.--container-id
: Container ID.--memory
: Memory available for the container.--cores
: Number of vCPUs available to the container.--execution-timeout
: Execution timeout.--concurrency
: Maximum number of concurrent container invocations. If the number of container invocations exceeds the--concurrency
value, Serverless Containers scales the container up by running its additional instances.--environment
: Environment variables. The application reads the Document API endpoint from theDOCUMENT_API_ENDPOINT
environment variable.--service-account-id
: Service account ID.--image
: Repository name.
Deploy your API in API Gateway
-
In the OpenAPI specifications
(api.yaml
), replace${MOVIES_API_CONTAINER_ID}
with the ID of the container you created. -
The api-gateway.tf
file describes the Terraform configuration for creating an API gateway. Deploy an API gateway:terraform apply -target=yandex_api_gateway.movies_api_gateway
Confirm creating the resources by typing
yes
in the terminal and pressing Enter. -
In the command output, the
movies_api_gateway_domain
variable contains the domain name of the API gateway. Export it to the environment variable:export MOVIES_API_GATEWAY_DOMAIN=<API_gateway_domain_name> echo $MOVIES_API_GATEWAY_DOMAIN
Test the new CRUD API
To test the new CRUD API, run the following HTTP requests using the curl
command:
-
Retrieve a movie list:
curl "${MOVIES_API_GATEWAY_DOMAIN}/movies?limit=10"
The response must return an empty list (
[]
) since there are no entries in the database yet. -
Add movie details:
curl \ --location \ --request POST 'https://${MOVIES_API_GATEWAY_DOMAIN}/movies' \ --header 'Content-Type: application/json' \ --data-raw '{ "id": "301", "title": "The Matrix", "release_date": 1999 }'
-
Retrieve movie details:
curl \ --location \ --request GET 'https://${MOVIES_API_GATEWAY_DOMAIN}/movies/301'
-
Add details for another movie:
curl \ --location \ --request POST 'https://${MOVIES_API_GATEWAY_DOMAIN}/movies' \ --header 'Content-Type: application/json' \ --data-raw '{ "id": "299", "title": "The Matrix Reloaded", "release_date": 2003 }'
-
Retrieve a movie list:
curl \ --location \ --request GET 'https://${MOVIES_API_GATEWAY_DOMAIN}/movies?from=1&limit=5'
You can also upload the specification to Postman${MOVIES_API_GATEWAY_DOMAIN}
variable to the servers
section. This will enable you to easily make REST API calls.
View diagnostic information about the container. In the management console
You can also view monitoring logs and charts on the API gateway page.
How to delete the resources you created
To stop paying for the resources you created with Terraform, delete them. In the terminal, run this command:
terraform destroy
Confirm the resource deletion by typing yes
in the terminal and pressing Enter.