# Deploying a web application using the Java Servlet API


Learn how to use the serverless technology and Java Servlet API to create a simple web application for managing a to-do list.

To create a web app:

1. [Get your cloud ready](#before-begin).
1. [Set up your environment](#prepare).
1. [Create a Yandex Object Storage bucket](#create-bucket).
1. [Create a YDB](#create-db).
1. [Create Yandex Cloud Functions](#create-functions).
1. [Create an API gateway](#create-api-gw).
1. [Test your application](#test).

If you no longer need the resources you created, [delete them](#clear-out).

## Getting started {#before-begin}

Sign up for Yandex Cloud and create a [billing account](../../billing/concepts/billing-account.md):
1. Navigate to the [management console](https://console.yandex.cloud) and log in to Yandex Cloud or create a new account.
1. On the **[Yandex Cloud Billing](https://center.yandex.cloud/billing/accounts)** page, make sure you have a billing account linked and it has the `ACTIVE` or `TRIAL_ACTIVE` [status](../../billing/concepts/billing-account-statuses.md). If you do not have a billing account, [create one](../../billing/quickstart/index.md) and [link](../../billing/operations/pin-cloud.md) a cloud to it.

If you have an active billing account, you can create or select a [folder](../../resource-manager/concepts/resources-hierarchy.md#folder) for your infrastructure on the [cloud page](https://console.yandex.cloud/cloud).

[Learn more about clouds and folders here](../../resource-manager/concepts/resources-hierarchy.md).

### Required paid resources {#paid-resources}

The web application support cost includes:

* Fee for the number of requests to the API gateway and outgoing traffic (see [Yandex API Gateway pricing](../../api-gateway/pricing.md)).
* Fee for YDB operations and data storage (see [Yandex Managed Service for YDB pricing](../../ydb/pricing/serverless.md)).
* Fee for the number of function calls, computing resources allocated to run a function, and outgoing traffic (see [Cloud Functions pricing](../pricing.md)).

## Set up your environment {#prepare}

1. [Create](../../iam/operations/sa/create.md#create-sa) a service account and [assign](../../iam/operations/roles/grant.md#cloud-or-folder) it the `editor` role for your folder.
1. Clone the [repository](https://github.com/yandex-cloud-examples/yc-serverless-servlet) with the project:

    ```bash
    git clone https://github.com/yandex-cloud-examples/yc-serverless-servlet
    ```
1. Add the contents of the `yc-serverless-servlet` local repository you cloned to the `servlet.zip` archive. You will need this archive later to create functions in Yandex Cloud Functions.


## Create an Object Storage bucket {#create-bucket}

Create a [bucket](../../storage/concepts/bucket.md) and upload `index.html` to it:

{% list tabs group=instructions %}

- Management console {#console}

  1. In the [management console](https://console.yandex.cloud), select the folder where you want to create a bucket.
  1. Navigate to **Object Storage**.
  1. Click **Create bucket**.
  1. On the bucket creation page:
     1. Enter a name for the bucket by following the [naming conventions](../../storage/concepts/bucket.md#naming).
     1. Limit the maximum bucket size, if required.
     1. In the **Read objects**, **Read object list**, and **Read settings** fields, select `With authorization`.
     1. Select the default [storage class](../../storage/concepts/storage-class.md).
     1. Click **Create bucket** to complete the operation.
  1. Select the bucket you created.
  1. Click **Upload** and select `src/main/resources/index.html` in the project directory.
  1. Select the [storage class](../../storage/concepts/storage-class.md) for the file and click **Upload**.

{% endlist %}

## Create a database in Managed Service for YDB {#create-db}

1. Create a database in [serverless mode](../../ydb/concepts/serverless-and-dedicated.md#serverless):

   {% list tabs group=instructions %}

   - Management console {#console}

     1. In the [management console](https://console.yandex.cloud), select the folder where you created the bucket.
     1. Navigate to **Managed Service for&nbsp;YDB**.
     1. Click **Create a database**.
     1. Specify a **Name** for the database. The naming requirements are as follows:

        * Length: between 3 and 63 characters.
        * It can only contain lowercase Latin letters, numbers, and hyphens.
        * It must start with a letter and cannot end with a hyphen.

     1. Under **Database type**, select `Serverless`.
     1. Click **Create a database**.
     1. Wait for the database to start. While being created, your database will have the `Provisioning` status. Once it is ready for use, its status will change to `Running`.
     1. Select the created database.
     1. Under **Connection**, find the **Endpoint** field and save its value. You will need it when creating functions.

   {% endlist %}

1. Create a table named `Tasks`:

   {% list tabs group=instructions %}

   - Management console {#console}

     1. In the [management console](https://console.yandex.cloud), select the folder where you created the database.
     1. Navigate to **Managed Service for&nbsp;YDB**.
     1. Select the database on the **Databases** page.
     1. To open the database root directory, navigate to the **Navigation** tab.
     1. To query your database, click **New SQL query** in the top-right corner. This will open the **Query** page.
     1. In the **Query** field, enter:

        ```sql
        CREATE TABLE Tasks (
          TaskId Utf8,
          Name Utf8,
          Description Utf8,
          CreatedAt Datetime,
          PRIMARY KEY (TaskId)
        );
        ```

     1. Click **Run**.

   - CLI {#cli}

     Run this query:

     ```bash
     ydb -e grpcs://<YDB_endpoint> -d <DB_name> \
     scripting yql -s \
     "CREATE TABLE Tasks
     (
       TaskId Utf8,
       Name Utf8,
       Description Utf8,
       CreatedAt Datetime,
       PRIMARY KEY (TaskId)
     );"
     ```

   {% endlist %}

## Create Cloud Functions {#create-functions}

Create a [function](../concepts/function.md) for each servlet:

{% list tabs group=instructions %}

- Management console {#console}

  1. In the [management console](https://console.yandex.cloud), navigate to the folder where you created the bucket and the database.
  1. Navigate to **Cloud Functions**.
  1. Click **Create function**.
  1. Enter a name, e.g., `add-task`, and description for the function.
  1. Click **Create**.
  1. Under **Editor**, select `java21` as the runtime, disable **Add files with code examples**, and click **Continue**.
  1. Prepare the function code. To do this, select `ZIP archive` in the **Code source** field.
  1. In the **File** field, click **Attach file** and select `servlet.zip` you created earlier.
  1. In the **Entry point** field, enter `yandex.cloud.examples.serverless.todo.AddTaskServlet`.
  1. Set **Timeout** to `10`.
  1. In the **Service account** field, specify the account you created when [setting up your environment](#prepare).
  1. Add these environment variables:
     * `ENDPOINT`: Enter the first part of the **Endpoint** field value you saved when [creating your YDB](#create-db) (the one between `grpcs://` and `/?database=`), e.g., `ydb.serverless.yandexcloud.net:2135`.
     * `DATABASE`: Enter the second part of the **Endpoint** field value you saved when [creating your YDB](#create-db) (the one following `/?database=`), e.g., `/ru-central1/r1gra875baom********/g5n22e7ejfr1********`.
  1. Click **Save changes**.
  1. On the **Overview** page, enable **Public function**.
  1. Repeat steps 3 through 14 to create a function named `list-tasks` with the `yandex.cloud.examples.serverless.todo.ListTasksServlet` entry point.
  1. Repeat steps 3 through 14 to create a function named `delete-task` with the `yandex.cloud.examples.serverless.todo.DeleteTaskServlet` entry point.

- CLI {#cli}

  1. Create a function named `add-task`:

     ```bash
     yc serverless function create --name=add-task
     ```

     Result:

     ```text
     id: d4ejb1799eko********
     folder_id: aoek49ghmknn********
     created_at: "2021-03-08T14:07:32.134Z"
     name: add-task
     log_group_id: eolm8aoq9vcp********
     http_invoke_url: https://functions.yandexcloud.net/d4ejb1799eko********
     status: ACTIVE
     ```

  1. Create a version of the `add-task` function:

     ```bash
     yc serverless function version create \
       --function-name=add-task \
       --runtime java21 \
       --entrypoint yandex.cloud.examples.serverless.todo.AddTaskServlet \
       --memory 128m \
       --execution-timeout 10s \
       --source-path ./servlet.zip \
       --environment DATABASE=<DB_name>,ENDPOINT=<YDB_endpoint>
     ```

     Where:

     * `--function-name`: Name of the function whose version you want to create.
     * `--runtime`: Runtime environment.
     * `--entrypoint`: Entry point in `<function_file_name>`.`<handler_name>` format.
     * `--memory`: Amount of RAM.
     * `--execution-timeout`: Maximum function running time before timeout.
     * `--source-path`: Path to the previously created `servlet.zip` archive with the function code and required dependencies.
     * `--environment`: Environment variables in `key=value` format.

     Result:

     ```text
     done (1s)
     id: d4evvn8obisa********
     function_id: d4ejb1799eko********
     ...
     tags:
     - $latest
     log_group_id: ckg3qh8h363p********
     ```

  1. Make the function public:

     ```bash
     yc serverless function allow-unauthenticated-invoke add-task
     ```

     Result:

     ```text
     done (1s)
     ```

  1. Repeat steps 1 through 3 to create a function named `list-tasks` with the `yandex.cloud.examples.serverless.todo.ListTasksServlet` entry point.
  1. Repeat steps 1 through 3 to create a function named `delete-task` with the `yandex.cloud.examples.serverless.todo.DeleteTaskServlet` entry point.

- API {#api}

  Use the [create](../functions/api-ref/Function/create.md), [createVersion](../functions/api-ref/Function/createVersion.md), and [setAccessBindings](../functions/api-ref/Function/setAccessBindings.md) API methods for the [Function](../functions/api-ref/Function/index.md) resource.


{% endlist %}

## Create an API gateway {#create-api-gw}

To enable interaction between services, create an API gateway:

{% list tabs group=instructions %}

- Management console {#console}

  1. In the [management console](https://console.yandex.cloud), select the folder where you created your bucket, database, and functions.
  1. Navigate to **API Gateway**.
  1. Click **Create API gateway**.
  1. Enter a name and description for the gateway.
  1. In the **Specification** field, add this specification:

     ```yaml
     openapi: 3.0.0
     info:
       title: ToDo list
       version: 1.0.0
     paths:
       /:
         get:
           x-yc-apigateway-integration:
             type: object-storage
             bucket: <bucket>
             object: index.html
             presigned_redirect: false
             service_account_id: <service_account_ID>
           operationId: static
       /add:
          post:
            x-yc-apigateway-integration:
              type: cloud-functions
              function_id: <add-task_ID>
            operationId: addTask
       /list:
         get:
           x-yc-apigateway-integration:
             type: cloud-functions
             function_id: <list-tasks_ID>
           operationId: listTasks
       /delete:
         delete:
           x-yc-apigateway-integration:
             type: cloud-functions
             function_id: <delete-task_ID>
           operationId: deleteTask
       ```

       Where:

       * `bucket`: Name of the bucket containing `index.html`.
       * `service_account_id`: ID of the service account you created when [setting up your environment](#prepare).
       * `/add` section, `function_id` parameter: `add-task` function ID.
       * `/list` section, `function_id` parameter: `list-tasks` function ID.
       * `/delete` section, `function_id` parameter: `delete-task` function ID.

  1. Click **Create**.

- CLI {#cli}

  1. Save the following specification to `todo.yaml`:

     ```yaml
     openapi: 3.0.0
     info:
       title: ToDo list
       version: 1.0.0
     paths:
       /:
         get:
           x-yc-apigateway-integration:
             type: object-storage
             bucket: <bucket>
             object: index.html
             presigned_redirect: false
             service_account_id: <service_account_ID>
           operationId: static
       /add:
         post:
           x-yc-apigateway-integration:
             type: cloud-functions
             function_id: <add-task_ID>
           operationId: addTask
       /list:
         get:
           x-yc-apigateway-integration:
             type: cloud-functions
             function_id: <list-tasks_ID>
           operationId: listTasks
       /delete:
         delete:
           x-yc-apigateway-integration:
             type: cloud-functions
             function_id: <delete-task_ID>
           operationId: deleteTask
     ```

     Where:

     * `bucket`: Name of the bucket containing `index.html`.
     * `service_account_id`: ID of the service account you created when [setting up your environment](#prepare).
     * `/add` section, `function_id` parameter: `add-task` function ID.
     * `/list` section, `function_id` parameter: `list-tasks` function ID.
     * `/delete` section, `function_id` parameter: `delete-task` function ID.

  1. Create an API gateway:

     ```bash
     yc serverless api-gateway create \
       --name todo-list \
       --spec=todo.yaml \
       --description "simple todo list"
     ```

     Result:

     ```text
     done (41s)
     id: d5delqeel34q********
     folder_id: b1g86q4m5vej********
     created_at: "2021-03-08T14:07:32.134Z"
     name: todo-list
     description: simple todo list
     status: ACTIVE
     domain: d5dm1lba80md********.i9******.apigw.yandexcloud.net
     log_group_id: ckg2hdmevnvc********
     ```


{% endlist %}

## Test the application {#test}

To open the application, follow the link in the **Default domain** field of the API gateway you created.

## How to delete the resources you created {#clear-out}

To stop paying for the resources you created:

* [Delete the bucket](../../storage/operations/buckets/delete.md).
* [Delete the database](../../ydb/operations/manage-databases.md#delete-db).
* [Delete the functions](../operations/function/function-delete.md).
* [Delete the API gateway](../../api-gateway/operations/api-gw-delete.md).