We have completed a good portion of our application since we are now able to generate the short URLs and store them into our database. In this section, we will publish the short links on dashboard, integrate a middleware in our project to refrain the short links from hitting our React application, redirect users to the original link whenever a request is made and maintain the link counts.
Publication and Subscription of Links
We will now write our logic to display the short links on the dashboard. We need to set up the publication of the links from the server side. For implementing this, make sure you have removed the default autopublish package as:
$ meteor remove autopublish
Let’s setup the publication inside server/main.js :
server/main.js
import { Meteor } from 'meteor/meteor';import { Links } from '../imports/api/links';Meteor.startup(() => {Meteor.publish('links',function(){return Links.find({});});});
Now, we need to subscribe to this publication. We will create a new component to display the list of all the links. First, let’s install the packages needed to setup tracker for subscription the same way as we did in the previous app. To do so, run the following commands:
$ meteor npm install --save react-addons-pure-render-mixin$ meteor add react-meteor-data
With the packages installed, we will write down the code for subscription and tables to display the data on the browser. Create a new file link_list.js within components directory and create a LinkList component along with a tracker to detect the changes in the subscribed data as:
imports/ui/components/link_list.js
import React, {Component} from 'react';import { withTracker } from 'meteor/react-meteor-data';import { Links } from '../../api/links';import { Meteor } from 'meteor/meteor';class LinkList extends Component{renderRows(){return this.props.links.map(link =>{const {url, clicks, token} = link; //ES6 destructuringconst shortLink = `http://localhost:3000/${token}`;return(<tr key={token}><td>{url}</td><td><a href={shortLink} target="_blank">{shortLink}</a></td><td>{clicks}</td></tr>)})}render(){return(<table className="table"><thead><tr><th>URL</th><th>Address</th><th>Clicks</th></tr></thead><tbody>{this.renderRows()}</tbody></table>);}}export default withTracker(()=>{Meteor.subscribe('links');return {links: Links.find({}).fetch()};}) (LinkList);
We have created a helper function renderRows for better modularity and have displayed the short link as `http://localhost:3000/${token}` so that users always hit our server whenever they open the short link. In case, you are hosting the application to some online server, change the short link with your URL name. For eg, `https://shorten-any-link.com/${token}`, or, `https://shorten.heroku.com/${token}`, etc.
Note: Do not forget to add a key property to each row so that React can render it efficiently or you will get a warning message on the web console. Here, we used token as key property.
We will finally import the component inside client/main.js to display the component on the web page.
client/main.js
//Code executes only at the client side//Any JavaScript code here will automatically run for usimport React from 'react';import ReactDOM from 'react-dom';import { Meteor } from 'meteor/meteor';import './main.html'; //Importing the html fileimport Header from '../imports/ui/components/header';import LinkCreate from '../imports/ui/components/link_create';import LinkList from '../imports/ui/components/link_list';import { Links } from '../imports/api/links';const App = ()=>{return (<div><Header /><LinkCreate /><LinkList /></div>);};Meteor.startup(()=>{ReactDOM.render(<App/>, document.querySelector('.render-target'));});
You can enhance the styling later. As of now, we are done with the frontend which looks as:
Meteor Middleware for Intercepting Requests
Our app looks great but the key functionality of redirecting the users from short links to long links is still missing. Firstly, we should understand that whenever the user tries to visit a short link, we don’t want them to hit our React application so as to keep the redirection as fast as possible. Have a look at this mock upto understand the flow.
When any http request comes into our meteor application, it falls through a series of middlewares. Middlewares are snippets of code or function that executes for any request that comes into our application. Middlewares are also used to modify or process request in a different way. We will create the middleware that will detect if the user is visiting the URL with some token in it and if they are, we will redirect them to appropriate destination. As you might have guessed, we will be writing the middleware code in server/main.js because incoming requests are handled by servers.
We will use a method (WebApp.connectHandlers.use) provided by Meteor to add the middleware to our application. WebApp is a Meteor object which handles the incoming requests. To understand this clearly, write this code and visit a URL with any token value (for eg, http://localhost:3000/anything), and check the output on your terminal/command prompt.
server/main.js
import { Meteor } from 'meteor/meteor';import { Links } from '../imports/api/links';import { WebApp } from 'meteor/webapp';Meteor.startup(() => {Meteor.publish('links',function(){return Links.find({});});});WebApp.connectHandlers.use(req => {console.log(req);});
You will get all the details of the request on your console. Hence, this is definitely the right spot to add our middleware. We will also be using another library to help us parse URL and perform appropriate operations on it. So, let’s install the library as:
$ meteor npm install --save connect-route
Once it is installed, use it as:
server/main.js
import { Meteor } from 'meteor/meteor';import { Links } from '../imports/api/links';import { WebApp } from 'meteor/webapp';import ConnectRoute from 'connect-route';Meteor.startup(() => {Meteor.publish('links',function(){return Links.find({});});});// localhost:3000 -- NO MATCH// localhost:3000/absc/xasdccv -- NO MATCH// localhost:3000/abcd -- MATCHconst middleware = ConnectRoute(function(router){router.get('/:token', (req) => console.log(req));})WebApp.connectHandlers.use(middleware);
Again, if you navigate to this URL (http://localhost:3000/anything), you will find a params object in addition to the previous console message. The params also has a property { token: 'anything' } which is the one we defined inside ConnectRoute function. We can access it as req.params.token. This shows that if any http request has a URL in the format ('/:token'), the value is captured in token variable and the function defined in the second parameter gets executed.
Redirecting Users to Long Link
We have setup a middleware to figure out if user is trying to visit a long URL or our React application. It is the time to write the logic for redirecting the user to his long URL.
server/main.js
import { Meteor } from 'meteor/meteor';import { Links } from '../imports/api/links';import { WebApp } from 'meteor/webapp';import ConnectRoute from 'connect-route';Meteor.startup(() => {Meteor.publish('links',function(){return Links.find({});});});// executed when user visits valid links like// 'localhost:3000/abcd'function onRouteRedirect(req, res, next){// extract the token from the url and try to find the// matching url in the links collectionconst link = Links.findOne({token : req.params.token});if(link){// if we find a link object, redirect the user to long URLres.writeHead(307,{'Location':link.url});res.end();}else{// if we don't find the link object,// redirect the user to normal React pagenext();}}// localhost:3000 -- NO MATCH// localhost:3000/absc/xasdccv -- NO MATCH// localhost:3000/abcd -- MATCHconst middleware = ConnectRoute(function(router){router.get('/:token', onRouteRedirect);})WebApp.connectHandlers.use(middleware);
Here, we have defined a helper function onRouteRedirect for code modularity with function parameters as request, response and next. We find the token from the database. If the token exists, we will respond the request with response code 307 which redirect to the location link.url Once the user gets redirected, we ended the request with res.end() . In case the token is not found, we simply passed the request to next middleware to continue the process and end up with React application on the browser.
You are now able to successfully redirect the user from the short links to actual links.
Note: Most of the concepts such as router, request, middleware, etc are NodeJS concepts. They are not usually required in Meteor applications. So, do not worry if you find them difficult to understand.
Updating Links Count
We have finished up everything except for incrementing the link count on every successful redirection. We can update the link count in the database using the links.update method. Further, we need to use Mongo Modifiers to update the collections of a MongoDB database. Mongo Modifiers are JavaScript objects that define a specific operation to update database. The following example increments the clicks count by one: {$inc: {clicks:1}} here, $inc represents increment. For more examples and extra information about Mongo Modifiers, refer to this documentation.
Here is the final server side code after updating the link count:
server/main.js
import { Meteor } from 'meteor/meteor';import { Links } from '../imports/api/links';import { WebApp } from 'meteor/webapp';import ConnectRoute from 'connect-route';Meteor.startup(() => {Meteor.publish('links',function(){return Links.find({});});});// executed when user visits valid links like// 'localhost:3000/abcd'function onRouteRedirect(req, res, next){// extract the token from the url and try to find the// matching url in the links collectionconst link = Links.findOne({token : req.params.token});if(link){// if we find a link object, redirect the user to long URLLinks.update(link, {$inc: {clicks:1}});res.writeHead(307,{'Location':link.url});res.end();}else{// if we don't find the link object,// redirect the user to normal React pagenext();}}// localhost:3000 -- NO MATCH// localhost:3000/absc/xasdccv -- NO MATCH// localhost:3000/abcd -- MATCHconst middleware = ConnectRoute(function(router){router.get('/:token', onRouteRedirect);})WebApp.connectHandlers.use(middleware);
And we are done. Give your application a final shot on the browser.
Congratulations, you’ve just created your own URL Shortener.
The final code is also available here https://github.com/anant90/meteor-react-url-shortener-app for your reference.