The first part of building a small web app which interacts with the npm API and generates nice charts Data is beautiful. And with modern technologies it is crazy easy to visualize your data and create great experiences. In this quick how to, we cover how to interact with the npm 💘 API to get download statistics of a package and generate a chart from this data with Chart.js ⚡ Quickstart We will build and will be using following tools: npm-stats.org with Vue.js vue-router Chart.js vue-chartjs vue-cli axios With Vue.js we will build the basic interface of the app and and routing with . And we scaffold our project with which creates our basic project structure. For the chart generation we will be using Chart.js and as a wrapper for Vue, . As we need to interact with an API, we’re using to make the http requests. However feel free to swap that one out with any other lib. vue-router vue-cli vue-chartjs axios 🔧 Install & Setup At first we need to install vue-cli to scaffold our project. I hope you have a current version of node and npm already installed! 🙏 Even better if you have installed! If not, you really should! If you don’t want, just swap out the yarn commands with the npm equivalents. yarn $ npm install -g vue-cli Then we can scaffold our project with vue-cli. If you want to can enable the unit and e2e tests, however we will not cover them.🔥 But you need to check vue-router! $ vue init webpack npm-stats Then we cd in our project folder and install the dependencies with . So our basic project dependencies are installed. Now we need to add the one for our app. cd npm-stats && yarn install $ yarn add vue-chartjs chart.js axios Just a quick check if everything is running with . Now we should see the boilerplate page of vue. yarn run dev Aaaand we’re done! 👏 💪 Time to build Just a small disclaimer here, I will not focus on the styling. I guess you’re able to make the site look good by your own 💅 so we only cover the javascript related code. And another disclaimer, this is rather a small MVP then super clean code right now. I will refactor some of it in later stages. Like in the real world. Components Let’s think about what components we need. As we’re looking at the screenshot we see an input field for the package name you’re looking for and a button. Maybe a header and footer and the chart itself. You totally could make the button and input field a component however as we don’t build a complex app, why bother? Make it simple. Make it work! So I ended up with following components: components/Footer.vue components/Header.vue components/LineChart.vue pages/Start.vue I will skip the and as they only contain the logo and some links. Nothing special here. The and page are the important ones. Header Footer LineChart Start LineChart The LineChart component will be our chart.js instance which renders the chart. We need to import the Line component and extend it. We create two props for now. One for the data which is the number of downloads and the labels which are for example the days, weeks, years. props: {chartData: {type: Array,required: false},chartLabels: {type: Array,required: true}}, As we want all our charts to look the same, we define some of the Chart.js styling options in a data model which get passed as to the method. options renderChart() And as we will have only one dataset for now, we can just build up the dataset array and bind the labels and data. 📺 Our start page As we have our LineChart component up and working. It’s time to build the rest. We need an input field and button to submit the package name. Then request the data and pass the data to our chart component. So, let’s first think about what data we need and what states / data models. First of all we need a data model, which we will use with in our input field. We also want to display the name of the package as a headline. So would be good. Then our two arrays for the requested data and and as we’re requesting a time period we need to set the . But, maybe the request goes wrong so we need and . And last but not least as we want to show the chart only after the request is made. package v-model packageName downloads labels period errorMessage showError loaded npm API There are various endpoints to get the downloads of a package. One is for example GET https://api.npmjs.org/downloads/point/{period}[/{package}] However this one gets only a point value. So the total downloads. But to draw our cool chart, we need more data. So we need the range endpoint. GET https://api.npmjs.org/downloads/range/{period}[/{package}] The period can be defined as for example or or a specific date range But to keep it simple we set the default value to . Later in Part II we can then add some date input fields so the user can set a date range. last-day last-month 2017-01-01:2017-04-19 last-month So our data models are looking like this: data () {return {package: null,packageName: ‘’,period: ‘last-month’,loaded: false,downloads: [],labels: [],showError: false,errorMessage: ‘Please enter a package name’}}, 💅 Template Now it’s time to build up the template. We need 5 things: Input Field Button to trigger the search Error message output Headline with the package name Our Chart. <inputclass=”Search__input” .enter=”requestData”placeholder=”npm package name”type=”search” name=”search”v-model=”package” @keyup <button class=”Search__button” =”requestData”>Find</button> @click <div class="error-message" v-if="showError">{{ errorMessage }}</div> <h1 class="title" v-if="loaded">{{ packageName }}</h1><line-chart v-if="loaded" :chart-data="downloads" :chart-labels="labels"></line-chart> Just ignore the css classes for now. We have our Input Field which has an keyup event on enter. So if you press enter you trigger the method. And we bind to requestData() v-model package For the potential error we have a condition, only if is we show the message. There are two types or errors that can occur. The one is, someone try to search for a package without entering any name or he’s entering a name that does not exist. showError true For the first case, we have our default errorMessage, for the second case we will grab the error message that comes from the request. So our full template will look like this: 🤖 Javascript Now it’s time for the coding. First we will do our method. It is rather simple. We need to make a request to our endpoint and then map the data that comes in. In our we have some information about the package: requestData() response.data response.data Like the start data, end date, the package name and then the downloads array. However the structure for the downloads array is something like this: downloads: [{day: ‘2017–03–20’, downloads: ‘3’},{day: ‘2017–03–21’, downloads: ‘2’},{day: ‘2017–03–22’, downloads: ‘10’},] But we need to separate the downloads and days, because for chart.js we need one array only with the data (downloads) and one array with the labels (day). This is an easy job for map requestData () {axios.get(` ).then(response => {this.downloads = response.data.downloads.map(download => download.downloads)this.labels = response.data.downloads.map(download => download.day)this.packageName = response.data.packagethis.loaded = true}).catch(err => {this.errorMessage = err.response.data.errorthis.showError = true})} https://api.npmjs.org/downloads/range/${this.period}/${this.package}` Now if we enter a package name, like vue and hit enter, the request is made, the data mapped and the chart rendered! But, wait. You don’t see anything. Because we need to tell vue-router to set the index to our start page. Under we import or page and tell the router to use it router/index.js import Vue from ‘vue’import Router from ‘vue-router’import StartPage from ‘@/pages/Start’ Vue.use(Router) export default new Router({routes: [{path: ‘/’,name: ‘Start’,component: StartPage},]}) 💎 Polish But, we are not done yet. We have some issues, right? First our app breaks if we don’t enter any name. And we have problems if you enter a new package and hit enter. And after an error the message does not disappear. Well, it’s time to clean up a bit. First let’s create a new method to reset our state. resetState () {this.loaded = falsethis.showError = false}, Which we call in our method before the axios api call. And we need a check for the package name. requestData() if (this.package === null|| this.package === ‘’|| this.package === ‘undefined’) {this.showError = truereturn} Now if we try to search an empty package name, we get or default errorMessage. I know, we covered a lot, but let’s add another small cool feature. We have vue-router, but not really using it. At our root we see the starting page with the input field. And after a search we stay at our root page. But it would be cool if we could share our link with the stats, wouldn’t it be? / So after a valid search, we add the package name to our url. npm-stats.org/#/vue-chartjs And if we click on that link, we need to grab the package name and use it to request our data. Let’s create a new method to set our url setURL () {history.pushState({ info: `npm-stats ${this.package}`}, this.package, `/#/${this.package}`)} We need to call in our response promise. Now after the request is made, we add the package name to our URL. But, if we open a new browser tab and call it, nothing happens. Because we need to tell vue-router that everything after our will also point to the start page and define the string as a query param. Which is super easy. this.setURL() / In our we just need to set another path in the routes array. We call the param package. router/index.js {path: ‘/:package’,component: StartPage} Now if you go to you will get the start page. But without a chart. Because we need to grab the param and do our request with it. localhost:8080/#/react-vr Back in our Start.vue we grab the param in the mounted hook. mounted () {if (this.$route.params.package) {this.package = this.$route.params.packagethis.requestData()}}, And thats it! You can check out the full source at and view the demo page at 📺 GitHub http://npm-stats.org/ Improvements But hey, there is still room for improvements. We could add more charts. Like monthly statistics, yearly statistics and add date fields to set the period and many more things. I will cover some of them in ! So stay tuned! Part II Part 2: https://hackernoon.com/lets-build-a-web-app-with-vue-chart-js-and-an-api-part-ii-39781b1d5acf
Share Your Thoughts