How I Made a Math Mensuration API Using Node.js + Express πŸ“

Β·

6 min read

Mensuration is the calculation of lengths, areas and volumes of 2D and 3D shapes. I decided to build an API using Node.js and Express to simplify these calculations, improve my backend skills and hopefully enhance other developers' workflow. Here's the link to my GitHub repo for the source code of this API, which also contains a few example links to demonstrate its powers. ✨

I'll be implementing a RESTful API in this case. If you don't know what that is, here's the answer ChatGPT gave me:

A RESTful API (Representational State Transfer) is a set of guidelines for creating web services that allow different software applications to communicate over the Internet. It uses standard HTTP methods (GET, POST, PUT, DELETE) to perform operations on resources (data) and follows principles of statelessness, uniform interface, and client-server interaction.

Hope that helped :)

Setup

Make sure you have Node.js installed on your computer. For testing the API whilst building it, I recommend using Postman.

In your project folder, hit npm init and complete the installation process. Then run the following command to install Express. That's the only NPM package we need!

npm install express

Create two files, index.js and formulas.js. Also, in your package.json file, make sure to add an additional property called type and set its value to module. This will allow you to write ES6 code. type has a default value of commonjs.

In index.js, add the following code to setup your app:

import express from 'express'
import formulas from './formulas.js'
const app = express()

app.get('/', (req, res) => {
    res.json({msg: 'hi'})
})

app.use((req, res, next) => {
    res.status(404).json({
        error: "Wrong route, go to '/' for more details"
    })
})

app.listen(8000, () => {
    const date = new Date()
    console.log(`Server started: ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`)
})

If you run node index.js in your terminal and then go to localhost:8000 on your browser, you should be able to see the JSON. If you go to, say, localhost:8000/v you should see the error message too.

Now that we have our app up and running, let's add the business logic.

The Logic 🧠

So ideally, we want the user to be able to navigate to the / route, get instructions, and then be able to navigate them to the nested routes for their calculations. Let's add all the formulas into formulas.js:

const round = (x) => Math.round(1000*x)/1000

const formulas = {
    area: {
        square: {
            params: ['s'],
            f: (s) => round(s**2)
        },
        rectangle: {
            params: ['l', 'b'],
            f: (l, b) => round(l*b)
        },
        triangle: {
            params: ['b', 'h'],
            f: (b, h) => round(b*h/2)
        },
        circle: {
            params: ['r'],
            f: (r) => round(22/7*(r**2))
        }
    },
    volume: {
        cube: {
            params: ['s'],
            f: (s) => round(s**3)
        },
        cuboid: {
            params: ['l', 'b', 'h'],
            f: (l, b, h) => round(l*b*h)
        },
        cylinder: {
            params: ['r', 'h'],
            f: (r, h) => round(h*(22/7*(r**2)))
        },
        sphere: {
            params: ['r'],
            f: (r) => round(4/3*(22/7)*(r**3))
        },
        cone: {
            params: ['r', 'h'],
            f: (r, h) => round((r**2)*h/3*(22/7))
        }
    }
}

export default formulas

The function round() simply rounds any given number to the nearest 3 decimal places. In our formulas object, we have two nested objects: area and volume. If you notice, in the square object inside area, we have two keys, params and a function f.

params defines the list of strings that we need as parameters from our Express routes for any calculation. For example, square and rectangle have different parameters. The function f rounds off the value obtained using the appropriate formula. Go through all the formulas to recollect middle school math!

The tougher aspect of this application is to use these formulas and parameters in the actual routing. Change the routes to the following, I'll explain it after. Also, don't forget to import in our formulas by import formulas from './formulas.js'.

app.set('query parser', 'simple')

app.get('/', (req, res) => {
    res.status(200).json({
        instructions: 'In the url, append /function/shape/params and send a GET request. Below is an object having the functions and the shapes nested inside. A few examples: /area/triangle?b=2&h=5   |||||   /volume/sphere?r=7',
        formulas
    })
})

app.get('/:func/:shape', (req, res) => {
    const { func, shape } = req.params
    if (!formulas[func]) {
        res.status(400).json({
            error: "Invalid function, go to root route '/' for more details"
        })
        return
    }
    if(!formulas[func][shape]) {
        res.status(400).json({
            error: "Invalid shape, go to root route '/' for more details"
        })
        return
    }
    const requiredParams = formulas[func][shape].params
    const paramsToPass = []
    for (let i = 0; i < requiredParams.length; i++) {
        const param = Number(req.query[requiredParams[i]])
        if (isNaN(param)) {
            res.status(400).json({
                error: `'${requiredParams[i]}' parameter not passed or given invalid value`
            })
            return
        }
        paramsToPass.push(param)
    }
    res.status(200).json({
        answer: formulas[func][shape].f(...paramsToPass)
    })
})

What's this code doing?

  1. We're setting the query parser of our Express app to be simple, for simple access of our query parameters in routing.

  2. We're giving the user some instructions when they access the / route, along with all our data from formulas so they can understand what parameters they need to pass. If you're wondering whether or not we're exposing the f function to the user, we're not, because functions can't be parsed as strings by Express.

  3. The most important route is /:func/:shape. If formulas doesn't have the corresponding keys for the function or the shape, then we tell the user that they have accessed the wrong function or shape.

  4. Once the func and shape pass our error checks, we're writing a for loop to know whether or not the user has passed all the required parameters. Remember how we stored our parameters in the params of every shape? We give the user an error if they haven't passed any particular parameter. If they have, we pass all the values into an array called paramsToPass.

  5. Once the request has passed all the error checks, we return a JSON object with a key answer which is the result of calling j() on the values of paramsToPass, which we're spreading using the ES6 spread operator.

That's all it is! Pretty minimalistic, I know, and not many formulas. I know there are many more shapes like hemispheres, prisms and many functions too like the total surface area of our 3D shapes. If you'd like to add these to the API and become a contributor, feel free to submit a PR to my GitHub repo!

Shameless Plug

Thanks for reading! I genuinely hope you learnt something new from this article. If you did, a click on the favourite button and a follow wouldn't hurt, would it? 😏

Here are some of my other popular articles that will surely help you:

Β