This repository was archived by the owner on May 20, 2025. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 17
Add url shortener guide for GO #689
Merged
Merged
Changes from 2 commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -84,6 +84,8 @@ roadmap | |
scaffolded | ||
scalable | ||
serverless | ||
shortener | ||
struct | ||
transcoding | ||
triages | ||
undeploy | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,277 @@ | ||
--- | ||
description: Use the Nitric framework to build and deploy a URL shortening service in Go. | ||
tags: | ||
- Key Value Store | ||
- API | ||
languages: | ||
- go | ||
start_steps: | | ||
git clone --depth 1 https://github.com/nitrictech/examples | ||
cd examples/v1/url-shortener | ||
go mod tidy | ||
nitric start | ||
published_at: 2024-12-20 | ||
--- | ||
|
||
# Building a URL shortener in Go with Nitric | ||
|
||
## What we'll be doing | ||
|
||
1. Use Nitric to create a simple URL shortener service. | ||
2. Expose an HTTP endpoint for shortening URLs. | ||
3. Store shortened URLs in a Key-Value (KV) store. | ||
4. Redirect users to the original URL when accessing the short code. | ||
5. Run locally for testing. | ||
6. Deploy to AWS. | ||
|
||
## Prerequisites | ||
|
||
- [Go](https://go.dev/dl/) | ||
- The [Nitric CLI](/get-started/installation) | ||
- An [AWS](https://aws.amazon.com) account (optional) | ||
|
||
## Getting started | ||
|
||
We'll start by creating a new project for our WebSocket application. The finished source can be found [here](https://github.com/nitrictech/examples/tree/main/v1/url-shortener). | ||
|
||
```bash | ||
nitric new url-shortener go-starter | ||
``` | ||
|
||
Next, open the project in your preferred editor: | ||
|
||
```bash | ||
cd url-shortener | ||
``` | ||
|
||
Make sure all dependencies are resolved: | ||
|
||
```bash | ||
go mod tidy | ||
``` | ||
|
||
The scaffolded project should have the following structure: | ||
|
||
```text | ||
+--services/ | ||
| +-- hello/ | ||
| +-- main.go | ||
+--nitric.yaml | ||
+--go.mod | ||
+--go.sum | ||
+--golang.dockerfile | ||
+--.gitignore | ||
+--README.md | ||
``` | ||
|
||
You can test the project to ensure everything is working as expected: | ||
|
||
```bash | ||
nitric start | ||
``` | ||
|
||
If everything is working as expected, you can now delete all files/folders in the `services/` folder. We'll create new services in this guide. | ||
|
||
### Resource Initialization | ||
|
||
We'll start by creating a `resources` package that defines and initializes the API and KV store from the Nitric SDK. At this point, we won't request any permissions like `get` or `set` for the KV store. We'll do that in the individual services to ensure least privilege provisioning. | ||
|
||
```go title:resources/main.go | ||
package resources | ||
|
||
import ( | ||
"sync" | ||
|
||
"github.com/nitrictech/go-sdk/nitric" | ||
"github.com/nitrictech/go-sdk/nitric/apis" | ||
"github.com/nitrictech/go-sdk/nitric/keyvalue" | ||
) | ||
|
||
type Resource struct { | ||
MainApi apis.Api | ||
UrlKvStore keyvalue.KvStore | ||
} | ||
|
||
var ( | ||
resource *Resource | ||
resourceOnce sync.Once | ||
) | ||
|
||
func Get() *Resource { | ||
resourceOnce.Do(func() { | ||
mainApi := nitric.NewApi("main") | ||
|
||
resource = &Resource{ | ||
MainApi: mainApi, | ||
UrlKvStore: nitric.NewKv("urls"), | ||
} | ||
}) | ||
|
||
return resource | ||
} | ||
``` | ||
|
||
## Building the URL Shortener Application | ||
|
||
Create a new folder called `shortener` within the `services` directory. Inside this folder, add a file named `main.go`, and include the following code: | ||
|
||
```go title:services/shortener/main.go | ||
package main | ||
|
||
import ( | ||
"math/rand" | ||
|
||
"github.com/nitrictech/go-sdk/nitric" | ||
"github.com/nitrictech/go-sdk/nitric/apis" | ||
"github.com/nitrictech/go-sdk/nitric/keyvalue" | ||
"github.com/nitrictech/templates/go-starter/resources" | ||
) | ||
|
||
func main() { | ||
// Initialize the resources defined in resources.go | ||
urlKvStore := resources.Get().UrlKvStore.Allow(keyvalue.KvStoreSet, keyvalue.KvStoreGet) | ||
|
||
// POST /shorten - Shorten a given URL | ||
resources.Get().MainApi.Post("/shorten", func(ctx *apis.Ctx) { | ||
// TODO: implement app logic | ||
}) | ||
|
||
// GET /:code - Redirect to the original URL associated with the short code | ||
resources.Get().MainApi.Get("/:code", func(ctx *apis.Ctx) { | ||
// TODO: implement app logic | ||
}) | ||
|
||
nitric.Run() | ||
} | ||
``` | ||
|
||
Here we're creating placeholders for our API routes and initializing our KV store with permissions so we can `GET` and `SET` values. | ||
|
||
Next, let's add a helper function to handle URL shortening and an inline struct to hold our URL data. | ||
|
||
```go title:services/shortener/main.go | ||
func generateShortCode() string { | ||
s := "" | ||
for i := 0; i < 6; i++ { | ||
s += string(rand.Intn(26) + 97) // Generate a lowercase letter | ||
} | ||
|
||
return s | ||
} | ||
|
||
var shortenData struct { | ||
Url string `json:"url"` | ||
} | ||
``` | ||
|
||
Let's implement our shorten route: | ||
|
||
```go title:services/shortener/main.go | ||
// POST /shorten - Shorten a given URL | ||
resources.Get().MainApi.Post("/shorten", func(ctx *apis.Ctx) { | ||
err := json.Unmarshal(ctx.Request.Data(), &shortenData) | ||
if err != nil || strings.TrimSpace(shortenData.Url) == "" { | ||
ctx.Response.Status = http.StatusBadRequest | ||
ctx.Response.Body = []byte("Invalid or missing URL") | ||
return | ||
} | ||
|
||
shortCode := generateShortCode() | ||
// Store the mapping of short code -> original URL | ||
err = urlKvStore.Set(context.Background(), shortCode, map[string]interface{}{ | ||
"url": shortenData.Url, | ||
}) | ||
if err != nil { | ||
ctx.Response.Status = 500 | ||
ctx.Response.Body = []byte("Error shortening URL") | ||
return | ||
} | ||
|
||
// Extract the origin from headers (for demonstration), then return the short URL | ||
origin := "" | ||
if val, ok := ctx.Request.Headers()["X-Forwarded-For"]; ok { | ||
origin = val[0] | ||
} else if val, ok := ctx.Request.Headers()["x-forwarded-for"]; ok { | ||
origin = val[0] | ||
} | ||
|
||
ctx.Response.Body = []byte(fmt.Sprintf("%s/%s", origin, shortCode)) | ||
}) | ||
``` | ||
|
||
Finally, we'll add a redirect handler to send users to the long URL when they use the short code: | ||
|
||
```go title:services/shortener/main.go | ||
// GET /:code - Redirect to the original URL associated with the short code | ||
resources.Get().MainApi.Get("/:code", func(ctx *apis.Ctx) { | ||
code := ctx.Request.PathParams()["code"] | ||
|
||
data, err := urlKvStore.Get(context.Background(), code) | ||
if err == nil { | ||
ctx.Response.Headers["Location"] = []string{data["url"].(string)} | ||
ctx.Response.Status = 301 | ||
} else { | ||
fmt.Println("Error getting URL: ", err) | ||
ctx.Response.Status = 404 | ||
} | ||
}) | ||
``` | ||
|
||
## Running locally | ||
|
||
Ensure all dependencies are resolved and start the project: | ||
|
||
```bash | ||
go mod tidy | ||
nitric start | ||
``` | ||
|
||
Nitric will run the service locally. You can test the endpoints using tools like `curl` or Postman. For example, to shorten a URL: | ||
|
||
```bash | ||
curl -X POST localhost:4001/shorten -d '{"url":"https://example.com"}' -H "Content-Type: application/json" | ||
``` | ||
|
||
You should receive a response containing the short code which you can use in a browser to test the redirect: | ||
|
||
``` | ||
http://localhost:4001/{shortcode} | ||
``` | ||
|
||
## Deploy to the Cloud | ||
|
||
When you're ready to deploy this to the cloud, Nitric makes it straightforward. In this example, we’ll deploy to AWS. First, set up your credentials and configuration for the [nitric/aws provider](/providers/pulumi/aws). | ||
|
||
### Create a Stack | ||
|
||
Create a `dev` stack that uses the `aws` provider: | ||
|
||
```bash | ||
nitric stack new dev aws | ||
``` | ||
|
||
This command creates a `nitric.dev.yaml` file defining your deployment target. Edit it to configure your preferred AWS region (e.g., `us-east-1`). | ||
|
||
### Deploying | ||
|
||
Deploy the stack to the cloud: | ||
|
||
```bash | ||
nitric up | ||
``` | ||
|
||
Once deployment is complete, you’ll have live endpoints accessible via AWS infrastructure. Test the endpoints using the new domain provided after deployment. | ||
|
||
To remove deployed resources: | ||
|
||
```bash | ||
nitric down | ||
``` | ||
|
||
## Summary | ||
|
||
In this guide, we’ve built a simple URL shortening service using Go and Nitric. We’ve shown how to: | ||
|
||
- Set up an API to shorten URLs and redirect users. | ||
- Use a Key-Value store to persist short code mappings. | ||
- Run and test the application locally before deploying it to AWS. |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.