How I Built a Serverless URL Shortener on AWS

2026-04-10

How I Built a Serverless URL Shortener on AWS

I built a serverless URL shortener that converts a long URL into a small, shareable link. The backend runs on AWS, while the user interface is integrated into my Next.js portfolio.

The project gave me a practical way to work with serverless architecture, API design, database operations, redirects, validation, and frontend integration.

What does the application do?

The application supports two main operations:

  1. A user submits a long URL and receives a unique short code.
  2. A visitor opens the short URL and is redirected to the original URL.

For example:

Long URL:
https://example.com/articles/serverless-applications/introduction

Short URL:
https://api.example.com/a1b2c3d4

AWS components used

Amazon API Gateway

API Gateway provides the public HTTP endpoints for the application. It receives requests from the portfolio and forwards them to the appropriate Lambda function.

The API has two important routes:

POST /shorten
GET /{code}

POST /shorten creates a short URL. GET /{code} handles the redirect when someone opens an existing short link.

AWS Lambda

Lambda contains the application logic. Because Lambda runs only when a request arrives, I do not need to create or maintain a continuously running server.

The shortening logic:

  • reads and validates the submitted URL;
  • generates a unique short code;
  • stores the code and original URL;
  • returns the generated code to the client.

The redirect logic:

  • reads the code from the request path;
  • finds its matching long URL;
  • returns an HTTP redirect response;
  • returns a not-found response if the code does not exist.

Amazon DynamoDB

DynamoDB stores the relationship between each short code and its original URL. A simplified item looks like this:

{
  "code": "dc547be2",
  "originalUrl": "https://example.com/a/long/url",
  "createdAt": "2026-06-25T10:30:00.000Z"
}

The short code works well as the partition key because the redirect Lambda can look up a link directly without scanning the table.

An optional expiry value can also be stored and used with DynamoDB Time to Live if links should be removed automatically.

Amazon CloudWatch

Lambda writes execution logs to CloudWatch. These logs are useful when an API request fails, a database lookup does not return an item, or a redirect behaves unexpectedly.

API Gateway rate limiting

I configured throttling in API Gateway to limit how many requests the public API accepts within a period of time. Large bursts of unnecessary requests can therefore be rejected before they invoke Lambda.

This helps protect the endpoint and keeps service usage and cost under control.

Lambda concurrency cap

I also configured a concurrency cap for the Lambda function. This limits how many instances of the function can run at the same time.

API Gateway throttling controls incoming traffic, while the Lambda concurrency cap protects the compute layer:

Incoming requests
  -> API Gateway rate limit
  -> Lambda concurrency cap
  -> application logic

Using both is useful for a public portfolio project because its expected traffic is small and unlimited scaling is unnecessary.

Architecture

The URL creation flow is:

User
  -> Next.js form
  -> Next.js API route
  -> API Gateway: POST /shorten
  -> Lambda
  -> DynamoDB
  -> short code returned to the user

The redirect flow is:

Visitor opens /{code}
  -> API Gateway
  -> Lambda
  -> DynamoDB lookup
  -> HTTP redirect to the original URL

Creating a short URL

The frontend sends a JSON request to the AWS create endpoint:

POST /shorten
Content-Type: application/json
{
  "url": "https://example.com/a/very/long/url"
}

The Lambda function returns the generated code and relative short path:

{
  "code": "dc547be2",
  "shortUrl": "/dc547be2"
}

Returning the code separately is useful for storage and debugging. Returning the relative path keeps the Lambda response independent of a particular custom domain.

Generating the short code

The code should be:

  • short enough to share easily;
  • difficult to guess accidentally;
  • unique within the DynamoDB table;
  • safe to place inside a URL path.

A simple implementation can generate a random identifier and check that it does not already exist before saving it. For a larger system, the code length and collision strategy should be selected based on expected traffic.

Handling redirects

When a visitor opens a short link, API Gateway passes the path code to the redirect Lambda. The function retrieves the matching item and returns a redirect response:

{
  "statusCode": 302,
  "headers": {
    "Location": "https://example.com/a/very/long/url"
  },
  "body": ""
}

The browser reads the Location header and navigates to the original URL.

If no DynamoDB item matches the code, the API returns 404 Not Found instead of redirecting.

Integrating AWS with my Next.js portfolio

The browser does not call the AWS endpoint directly. The URL-shortener form calls an internal Next.js route:

POST /api/shorten

That server-side route validates the URL and then sends it to API Gateway. The AWS configuration stays in environment variables:

AWS_SHORTENER_API_URL=https://api.example.com/shorten
AWS_SHORTENER_PUBLIC_URL=https://api.example.com

The API key variable is optional because this project does not require an API key.

Converting the relative response into a complete URL

My AWS endpoint returns:

{
  "shortUrl": "/dc547be2"
}

If the frontend uses that value directly, the browser interprets it relative to the portfolio domain. It might incorrectly open:

http://localhost:3000/dc547be2

The Next.js API route solves this by combining the returned path with the public AWS URL:

const shortUrl = new URL(
  response.shortUrl,
  process.env.AWS_SHORTENER_PUBLIC_URL,
).toString();

The user then receives the correct link:

https://api.example.com/dc547be2

Validation and error handling

The portfolio and AWS backend should both validate requests. The current integration checks that:

  • a URL was provided;
  • it can be parsed as a valid URL;
  • it uses the HTTP or HTTPS protocol;
  • the AWS request completed successfully;
  • the response includes a short URL or code.

The interface also displays loading, success, and error states. After a link is created, the user can open it or copy it to the clipboard.

At the infrastructure level, API Gateway rate limiting reduces excessive incoming traffic, and the Lambda concurrency cap prevents too many function instances from running simultaneously.

Why serverless was a good fit

This project does not need a server running continuously. API Gateway, Lambda, and DynamoDB provide a small set of managed services that scale with incoming requests.

The main benefits are:

  • no server maintenance;
  • automatic scaling;
  • usage-based pricing;
  • simple integration between AWS services;
  • independent frontend and backend deployment.

Improvements I can add next

The project can be extended with:

  • a custom short domain;
  • optional custom aliases;
  • link expiration using DynamoDB TTL;
  • click analytics;
  • protection against malicious destination URLs;
  • duplicate detection for previously shortened links;
  • automated deployment using AWS SAM or the AWS CDK.

What I learned

The most interesting part was not only generating a random code. The complete solution required the create route, redirect route, persistent storage, traffic controls, validation, error handling, and frontend response normalization to work together.

This project helped me understand how small serverless components can be combined into a useful production-style application.