Skip to content

Commit

Permalink
feat: Add a tutorial example for URL Shortner built using DiceDB (Dic…
Browse files Browse the repository at this point in the history
…eDB#1259)

Co-authored-by: Jyotinder <[email protected]>
  • Loading branch information
Prachi-Jamdade and JyotinderSingh authored Dec 5, 2024
1 parent 66a9f33 commit 0677ffb
Show file tree
Hide file tree
Showing 8 changed files with 492 additions and 4 deletions.
8 changes: 4 additions & 4 deletions docs/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,6 @@ export default defineConfig({
label: "Get started",
autogenerate: { directory: "get-started" },
},
// {
// label: 'Tutorials',
// autogenerate: { directory: 'tutorials' }
// },
{
label: "SDK",
autogenerate: { directory: "sdk" },
Expand All @@ -45,6 +41,10 @@ export default defineConfig({
label: "Connection Protocols",
autogenerate: { directory: "protocols" },
},
{
label: 'Tutorials',
autogenerate: { directory: 'tutorials' }
},
{
label: "Commands",
autogenerate: { directory: "commands" },
Expand Down
215 changes: 215 additions & 0 deletions docs/src/content/docs/tutorials/url-shortener.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
---
title: "Building a URL Shortener"
description: "Create a simple URL Shortener using DiceDB Go SDK."
---

This tutorial guides you through creating a URL shortener using DiceDB, a key-value store, with Go. We’ll set up endpoints to generate short URLs and redirect them to the original URLs.

## Prerequisites

1. Go (version 1.18 or later): [Download Go](https://golang.org/dl/)
2. DiceDB: A DiceDB server running locally. Refer to the [DiceDB Installation Guide](get-started/installation) if you haven't set it up yet.

## Setup

### 1. Install and Run DiceDB
Start a DiceDB server using Docker:
```bash
docker run -d -p 7379:7379 dicedb/dicedb
```

This command pulls the DiceDB Docker image and runs it, exposing it on port `7379`.

### 2. Initialize a New Go Project
Create a new directory for your project and initialize a Go module:
```bash
mkdir url-shortener
cd url-shortener
go mod init url-shortener
```

### 3. Install Required Packages
Install the DiceDB Go SDK and other dependencies:
```bash
go get github.com/dicedb/dicedb-go
go get github.com/gin-gonic/gin
go get github.com/google/uuid
```

## Understanding DiceDB Commands
We'll use the following DiceDB commands:
### `SET` Command
Stores a key-value pair in DiceDB.
- **Syntax**: `SET key value [expiration]`
- `key`: Unique identifier (e.g., short URL code)
- `value`: Data to store (e.g., serialized JSON)
- `expiration`: Optional; time-to-live in seconds (use `0` for no expiration)
### `GET` Command
Retrieves the value associated with a key.
- **Syntax**: `GET key`
- `key`: Identifier for the data to retrieve

## Writing the Code
Create a file named `main.go` and add the following code:

- `main.go`:
```go
package main

import (
"context"
"encoding/json"
"log"
"net/http"

"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/dicedb/dicedb-go" // DiceDB Go SDK
)

type URL struct {
ID string `json:"id"`
LongURL string `json:"long_url"`
ShortURL string `json:"short_url"`
}

var db *dicedb.Client

// Initialize DiceDB connection
func init() {
db = dicedb.NewClient(&dicedb.Options{
Addr: "localhost:7379",
})
}

// Creates a short URL from a given long URL
func CreateShortURL(c *gin.Context) {
var requestBody URL
if err := c.ShouldBindJSON(&requestBody); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"})
return
}

// Generate unique short ID and construct the short URL
shortID := uuid.New().String()[:8]
requestBody.ID = shortID
requestBody.ShortURL = "http://localhost:8080/" + shortID

// Serialize URL struct to JSON and store it in DiceDB
urlData, err := json.Marshal(requestBody)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save URL"})
return
}

if err := db.Set(context.Background(), shortID, urlData, 0).Err(); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save URL"})
return
}

c.JSON(http.StatusCreated, gin.H{"short_url": requestBody.ShortURL})
}

// Redirects to the original URL based on the short URL ID
func RedirectURL(c *gin.Context) {
id := c.Param("id")

// Retrieve stored URL data from DiceDB
urlData, err := db.Get(context.Background(), id).Result()
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "URL not found"})
return
}

// Deserialize JSON data back into URL struct
var url URL
if err := json.Unmarshal([]byte(urlData), &url); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to decode URL data"})
return
}

// Redirect user to the original long URL
c.Redirect(http.StatusFound, url.LongURL)
}

func main() {
router := gin.Default()

// Define endpoints for creating short URLs and redirecting
router.POST("/shorten", CreateShortURL)
router.GET("/:id", RedirectURL)

// Start the server on port 8080
if err := router.Run(":8080"); err != nil {
log.Fatal("Failed to start server:", err)
}
}
```

## Explanation

### 1. Initialize the DiceDB Client
We set up the DiceDB client in the `init` function:
```go
db = dicedb.NewClient(&dicedb.Options{
Addr: "localhost:7379",
})
```

### 2. Create Short URL Endpoint
- **Input Validation**: Ensures the `long_url` field is present.
- **Short ID Generation**: Uses `uuid` to create a unique 8-character ID.
- **Data Serialization**: Converts the `URL` struct to JSON.
- **Data Storage**: Saves the JSON data in DiceDB with the `Set` command.
- **Response**: Returns the generated short URL.

### 3. Redirect to Original URL Endpoint
- **Data Retrieval**: Fetches the URL data from DiceDB using the `Get` command.
- **Data Deserialization**: Converts JSON back to the `URL` struct.
- **Redirection**: Redirects the user to the `LongURL`.

### 4. Start the Server
The `main` function sets up the routes and starts the server on port `8080`.

## Running the Application

### 1. Start the Go Application
```bash
go run main.go
```
This will start the application server on port 8080 by default, you should see output similar to
```bash
[GIN-debug] Listening and serving HTTP on :8080
```

### 2. Ensure DiceDB is Running
Ensure your DiceDB server is up and running on port `7379`.

## Testing the application

### 1. Shorten URL:
**Using `curl`:**
```bash
curl -X POST -H "Content-Type: application/json" -d '{"long_url": "https://example.com"}' http://localhost:8080/shorten
```

**Response:**
```json
{
"short_url": "http://localhost:8080/<short ID generated by the server>"
}
```

### 2. Redirect to Original URL:
**Using `curl`:**
```bash
curl -L http://localhost:8080/abcd1234
```

**Using a Browser:**
Navigate to:
```
http://localhost:8080/abcd1234
```
You should be redirected to `https://example.com`.
28 changes: 28 additions & 0 deletions examples/url-shortener/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Build Stage
FROM golang:1.23 AS builder

WORKDIR /app

# Copy go.mod and go.sum to download dependencies
COPY go.mod go.sum ./
RUN go mod download

# Copy application source code and build
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o server .

# Runtime Stage
FROM alpine:latest

# Install runtime dependencies
RUN apk --no-cache add ca-certificates

# Copy the built application
WORKDIR /root/
COPY --from=builder /app/server .

# Expose the application port
EXPOSE 8080

# Start the application
CMD ["./server"]
17 changes: 17 additions & 0 deletions examples/url-shortener/compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
services:
dicedb:
image: dicedb/dicedb:latest
ports:
- "7379:7379"

url-shortener:
build:
context: .
dockerfile: Dockerfile
ports:
- "8000:8000"
depends_on:
- dicedb
environment:
- DICEDB_HOST=dicedb
- DICEDB_PORT=7379
41 changes: 41 additions & 0 deletions examples/url-shortener/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
module url-shortener

go 1.23.2

require github.com/gin-gonic/gin v1.10.0

require (
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
)

require (
github.com/bytedance/sonic v1.11.6 // indirect
github.com/bytedance/sonic/loader v0.1.1 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/dicedb/dicedb-go v0.0.0-20241026093718-570de4575be3
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.20.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/google/uuid v1.6.0
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
golang.org/x/arch v0.8.0 // indirect
golang.org/x/crypto v0.23.0 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/sys v0.20.0 // indirect
golang.org/x/text v0.15.0 // indirect
google.golang.org/protobuf v1.34.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading

0 comments on commit 0677ffb

Please sign in to comment.