Link shortener with the help of the management console
To create a link shortener using the Yandex Cloud management console:
- Get your cloud ready.
- Set up hosting for the URL shortener page.
- Create a service account.
- Create a database in Yandex Managed Service for YDB.
- Set up a function in Yandex Cloud Functions.
- Publish your URL shortener via Yandex API Gateway.
- Test the URL shortener.
If you no longer need the resources you created, delete them.
Get your cloud ready
Sign up for Yandex Cloud and create a billing account:
- Navigate to the management console
and log in to Yandex Cloud or create a new account. - On the Yandex Cloud Billing
page, make sure you have a billing account linked and it has theACTIVEorTRIAL_ACTIVEstatus. 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 create or select a folder for your infrastructure on the cloud page
Learn more about clouds and folders here.
Required paid resources
The infrastructure support cost for the URL shortener includes:
- Fee for data storage (see Yandex Object Storage pricing).
- Fee for YDB database operations and data storage (see Managed Service for YDB pricing).
- Fee for the number of function calls, computing resources allocated to run the function, and outgoing traffic (see Cloud Functions pricing).
- Fee for the number of requests to the API gateway and outgoing traffic (see API Gateway pricing).
Set up hosting for the URL shortener page
To create a bucket, upload your URL shortener's HTML page to it, and configure it for static website hosting:
-
In the management console
, select your working folder. -
Select Object Storage.
-
Click Create bucket.
-
On the bucket creation page:
-
Enter a name for the bucket.
Warning
Bucket names are unique throughout Object Storage, which means you cannot create two buckets with the same name, even in different folders belonging to different clouds.
-
Set the maximum size to
1 GB. -
Set object read access to
For all. -
Click Create bucket to complete the operation.
-
-
Copy the HTML code and paste it into
index.html:HTML code
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>URL shortener</title> <!-- warns against sending unnecessary GET requests to /favicon.ico --> <link rel="icon" href="data:;base64,iVBORw0KGgo="> </head> <body> <h1>Welcome</h1> <form action="javascript:shorten()"> <label for="url">Enter a link:</label><br> <input id="url" name="url" type="text"><br> <input type="submit" value="Shorten"> </form> <p id="shortened"></p> </body> <script> function shorten() { const link = document.getElementById("url").value fetch("/shorten", { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: link }) .then(response => response.json()) .then(data => { const url = data.url document.getElementById("shortened").innerHTML = `<a href=${url}>${url}</a>` }) .catch(error => { document.getElementById("shortened").innerHTML = `<p>${error} occurred. Try again.</p>` }) } </script> </html> -
Click the name of the bucket you created.
-
Click Upload objects.
-
Specify the
index.htmlfile you set up earlier. -
Click Upload.
-
In the left-hand panel, select the Website tab.
-
Select Hosting.
-
Specify the website's home page:
index.html. -
Click Save.
Create a service account
To create a service account to enable interaction between the shortener components:
-
Navigate to your working folder.
-
Go to Identity and Access Management.
-
Click Create service account.
-
Enter a name for the service account:
serverless-shortener. -
Click Add role and select
editor. -
Click Create.
-
Click the name of the service account you created.
Save the service account ID, as you will need it later.
Create a database in Managed Service for YDB
To create a database in Managed Service for YDB and configure it to store URLs:
-
Navigate to your working folder.
-
In the list of services, select Managed Service for YDB.
-
Click Create a database.
-
Enter the database name:
for-serverless-shortener. -
Select Serverless as the database type.
-
Click Create a database.
-
Wait for the database to start.
While being created, your database will have the
Provisioningstatus. Once it is ready for use, its status will change toRunning. -
Click the database name.
Save the Endpoint field value, as you will need it later.
-
In the left-hand panel, select the Navigation tab.
-
Select Create → Table on the right side of the page.
-
Configure the table:
- Name tables:
links. - Table type:
Row table.
- Name tables:
-
Add these columns:
- Column name:
id; data type:Utf8. Set Primary key. - Column name:
link; data type:Utf8.
- Column name:
-
Click Create table.
Set up a function in Cloud Functions
To create and set up a URL shortening function:
-
Navigate to your working folder.
-
In the list of services, select Cloud Functions.
-
Click Create function.
-
Enter the name:
for-serverless-shortener. -
Click Create.
-
From the Python drop-down list, select the
python312runtime environment. -
Click Continue.
-
Copy the function code and paste it into
index.pyunder Function code.Function code
import ydb import urllib.parse import hashlib import base64 import json import os import traceback def decode(event, body): is_base64_encoded = event.get('isBase64Encoded') if is_base64_encoded: body = str(base64.b64decode(body), 'utf-8') return body def response(statusCode, headers, isBase64Encoded, body): # Always returning string to body if not isinstance(body, str): body = json.dumps(body, ensure_ascii=False) return { 'statusCode': statusCode, 'headers': headers, 'isBase64Encoded': isBase64Encoded, 'body': body, } def get_config(): endpoint = os.getenv("endpoint") database = os.getenv("database") if endpoint is None or database is None: raise AssertionError("You need to specify both environment variables: endpoint and database") credentials = ydb.iam.MetadataUrlCredentials() return ydb.DriverConfig(endpoint, database, credentials=credentials) def execute(config, query, params): with ydb.Driver(config) as driver: try: driver.wait(timeout=5, fail_fast=True) except Exception as e: print("Connect failed to YDB:", e) print(driver.discovery_debug_details()) raise session = driver.table_client.session().create() prepared_query = session.prepare(query) return session.transaction(ydb.SerializableReadWrite()).execute( prepared_query, params, commit_tx=True ) def insert_link(id, link): config = get_config() query = """ DECLARE $id AS Utf8; DECLARE $link AS Utf8; UPSERT INTO links (id, link) VALUES ($id, $link); """ params = {'$id': id, '$link': link} execute(config, query, params) def find_link(id): config = get_config() query = """ DECLARE $id AS Utf8; SELECT link FROM links where id=$id; """ params = {'$id': id} result_set = execute(config, query, params) if not result_set or not result_set[0].rows: return None # Paying attention to the structure of the result from ydb return result_set[0].rows[0].link def shorten(event): try: body = event.get('body') if body is None: return response(400, {'Content-Type': 'application/json'}, False, {'error': 'No body in the request body'}) body = decode(event, body) # Attempting to parse JSON with a url key; otherwise, treating body as a plain string url_value = None try: parsed = json.loads(body) if isinstance(parsed, dict): url_value = parsed.get('url') else: # If not a JSON object is sent, ignoring url_value = None except Exception: # If body is not JSON, treating it as plain URL url_value = body if not url_value: return response(400, {'Content-Type': 'application/json'}, False, {'error': 'The url parameter was expected in the request body'}) # Cleaning the URL from eventual coded characters clean_url = urllib.parse.unquote(url_value).strip() if not clean_url: return response(400, {'Content-Type': 'application/json'}, False, {'error': 'Empty url'}) link_id = hashlib.sha256(clean_url.encode('utf8')).hexdigest()[:6] insert_link(link_id, clean_url) # Returning the relative path; the frontend will add the origin by themselves return response(200, {'Content-Type': 'application/json'}, False, {'url': f'/r/{link_id}'}) except Exception as e: print("Exception in shorten():", e) traceback.print_exc() return response(500, {'Content-Type': 'application/json'}, False, {'error': 'internal server error'}) def redirect(event): try: # Securely retrieving path params path_params = event.get('pathParams') or event.get('pathParameters') or {} link_id = path_params.get('id') if not link_id: # Probably, the full path was received in event['url'] or ['path'] url = event.get('url') or event.get('path') or '' if url and url.startswith('/r/'): link_id = url.split('/r/')[-1].split('?')[0] if not link_id: return response(400, {'Content-Type': 'application/json'}, False, {'error': 'No ID'}) redirect_to = find_link(link_id) if redirect_to: return response(302, {'Location': redirect_to}, False, '') return response(404, {'Content-Type': 'application/json'}, False, {'error': 'The link does not exist'}) except Exception as e: print("Exception in redirect():", e) traceback.print_exc() return response(500, {'Content-Type': 'application/json'}, False, {'error': 'internal server error'}) def get_result(url, event): if url == "/shorten" or url.startswith("/shorten"): return shorten(event) if url.startswith("/r/"): return redirect(event) return response(404, {'Content-Type': 'application/json'}, False, {'error': 'The path does not exist'}) def handler(event, context): url = event.get('url') or event.get('path') or '' if url: # Sometimes, the URL from the gateway comes with a question mark at the end if url.endswith('?'): url = url[:-1] return get_result(url, event) return response(404, {'Content-Type': 'application/json'}, False, {'error': 'The function must be called via API Gateway.'}) -
Under Function code, create a file named
requirements.txtand paste the following text into it:ydb -
Specify the entry point:
index.handler. -
Set the timeout to
5. -
Select the
serverless-shortenerservice account. -
Add these environment variables:
endpoint: Enter the first part of the previously saved Endpoint field value (preceding/?database=), e.g.,grpcs://ydb.serverless.yandexcloud.net:2135.database: Enter the second part of the previously saved Endpoint field value (following/?database=), e.g.,/ru-central1/r1gra875baom********/g5n22e7ejfr1********.
-
Click Save changes.
-
Under Overview, enable Public function.
Save the function ID, as you will need it later.
Publish your URL shortener via API Gateway
To publish your URL shortener via API Gateway:
-
Navigate to your working folder.
-
From the list of services, select API Gateway.
-
Click Create API gateway.
-
In the Name field, enter
for-serverless-shortener. -
Copy and paste the following code under Specification:
API gateway specification
openapi: 3.0.0 info: title: for-serverless-shortener version: 1.0.0 paths: /: get: x-yc-apigateway-integration: type: object_storage bucket: <bucket_name>#<-- bucket name object: index.html # <-- HTML file name presigned_redirect: false service_account: <service_account_id> # <-- service_account_ID operationId: static /shorten: post: x-yc-apigateway-integration: type: cloud_functions function_id: <function_id> # <-- function ID operationId: shorten /r/{id}: get: x-yc-apigateway-integration: type: cloud_functions function_id: <function_id> # <-- function ID operationId: redirect parameters: - description: id of the url explode: false in: path name: id required: true schema: type: string style: simpleEdit the specification code as follows:
- Replace
<service_account_id>with the ID of the service account you created. - Replace
<function_id>with the ID of the function you created. - Replace
<bucket_name>with the name of the bucket you created.
- Replace
-
Click Create.
-
Click the name of the API gateway you created.
-
Copy the
urlvalue from the specification.Use this URL to test the URL shortener.
Test the URL shortener
To make sure your shortener components interact properly:
-
In your browser, open the previously copied shortener URL.
-
In the input field, enter the URL you want to shorten.
-
Click Shorten.
You will see the shortened URL below.
-
Open the shortened URL. It should lead to the same page as the original link.
How to delete the resources you created
To stop incurring charges for the resources you created:
- Delete
for-serverless-shortener. - Delete the function in
for-serverless-shortener. - Delete the
for-serverless-shortenerYDB database. - Delete the bucket.