The reality of long URLs is supposed to be light years behind us. However, I still encounter them on almost every website. It is hard to figure out the best URL-shortener code that would also track how many times the link was opened.
Using Redwood JS Framework which combines some of the most popular and advanced frameworks, I am going to create such a service.
Initialise the project by following the official Get Started tutorial:
Initialize git
git init
The first step is to add a new data table to api/db/schema.prisma
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
binaryTargets = "native"
}
model WebLink {
id Int @id @default(autoincrement())
shortId String @unique
url String
viewedAmount Int @default(0)
}
Next, the Prisma framework can help us create a SQL migration for the sqlite. I recommend using postgresql in production.
yarn rw prisma migrate dev
Redwood did a great job with the scaffold feature that generates a CRUD (Create, Retrieve, Update, Delete) code for React by running just one command.
yarn rw g scaffold web-link
Run the development mode
yarn redwood dev
You will see that we have not finished the home page yet; however, four pages have already been generated:
/web-links/new
/web-links/{id:Int}/edit
/web-links/{id:Int}
/web-links
Let's open http://localhost:8910/web-links
and create a new data record.
Now we want to add a new query to the Graphql API:
api/src/graphql/webLinks.sdl.js
Add webLinkByShortId(shortId: String!): WebLink @requireAuth
to the SDL schema
export const schema = gql`
type WebLink {
id: Int!
shortId: String!
url: String!
viewedAmount: Int!
}
type Query {
webLinks: [WebLink!]! @requireAuth
webLink(id: Int!): WebLink @requireAuth
+ webLinkByShortId(shortId: String!): WebLink @requireAuth
}
input CreateWebLinkInput {
shortId: String!
url: String!
viewedAmount: Int!
}
input UpdateWebLinkInput {
shortId: String
url: String
viewedAmount: Int
}
type Mutation {
createWebLink(input: CreateWebLinkInput!): WebLink! @requireAuth
updateWebLink(id: Int!, input: UpdateWebLinkInput!): WebLink! @requireAuth
deleteWebLink(id: Int!): WebLink! @requireAuth
}
The next step is creating a function that will resolve this for us:
open api/src/services/webLinks/webLinks.js
import { db } from 'src/lib/db'
export const webLinks = () => {
return db.webLink.findMany()
}
export const webLink = ({ id }) => {
return db.webLink.findUnique({
where: { id },
})
}
export const createWebLink = ({ input }) => {
return db.webLink.create({
data: input,
})
}
export const updateWebLink = ({ id, input }) => {
return db.webLink.update({
data: input,
where: { id },
})
}
export const deleteWebLink = ({ id }) => {
return db.webLink.delete({
where: { id },
})
}
+ export const webLinkByShortId = async ({ shortId }) => {
+ await db.webLink.update({
+ where: { shortId },
+ data: { viewedAmount: { increment: 1 } },
+ })
+ return db.webLink.findUnique({ where: { shortId } })
+ }
I used the Redwood generator to generate a page.
yarn redwood g page Redirect
Update web/src/Routes.js
. Make sure that the new route is the last on the list since it uses dynamic params.
- <Route path="/redirect" page={RedirectPage} name="redirect" />
+ <Route path="/{shortId}" page={RedirectPage} name="redirect" />
Now we want to generate a cell. This is the way Redwood recommends working with data:
yarn redwood g cell redirect
Modify web/src/components/RedirectCell/RedirectCell.js
- export const QUERY = gql`
- query FindRedirectQuery($id: Int!) {
- redirect: redirect(id: $id) {
- id
- }
- }
- `
+ export const QUERY = gql`
+ query FindWebLink($shortId: String!) {
+ link: webLinkByShortId(shortId: $shortId) {
+ id
+ url
+ }
+ }
+ `
- export const Success = ({ redirect }) => {
- return <div>{JSON.stringify(redirect)}</div>
- }
+ export const Success = ({ link }) => {
+ React.useEffect(() => {
+ if (!link) return
+
+ window.location = link.url
+ }, [link])
+
+ return 'redirecting...'
+ }
As a result, if you click on [/kcuobL](http://localhost:8910/kcuobL)
, the link will take you to https://google.com
.
Voila. There is more than just a URL shortener in your hands. It is a marketing tool that can help your company's specialists to organise a huge part of their work online.
Top comments (1)
I think this approach does the opposite - having a short hash-style link instead of something readable would be less human-friendly, because then you have no chance to know where the link is going before you click on it. Rick-rolling, malware, tracking, etc. are all hidden behind shortened URLs.
I'd also not consider hiding your tracking behind shortened URLs to be the friendliest approach. People value privacy more and more as the world tries to take more of it away.
Finally, what you (and everyone else who makes shorteners) are doing is adding another point of failure. With tracking appended in the query string (like those ubiquitous
?utm=
fragments) or by using thereferer
value you don't need a third-party involved at all. If your URL shortener service has downtime, then anyone's links which use it also break. If your database gets corrupted, it's a mess for everyone.