How I Made a Math Mensuration API Using Node.js + Express π
Photo by Dan Cristian PΔdureΘ on Unsplash
Table of contents
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?
We're setting the
query parser
of our Express app to besimple
, for simple access of our query parameters in routing.We're giving the user some instructions when they access the
/
route, along with all our data fromformulas
so they can understand what parameters they need to pass. If you're wondering whether or not we're exposing thef
function to the user, we're not, because functions can't be parsed as strings by Express.The most important route is
/:func/:shape
. Ifformulas
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.Once the
func
andshape
pass our error checks, we're writing afor
loop to know whether or not the user has passed all the required parameters. Remember how we stored our parameters in theparams
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 calledparamsToPass
.Once the request has passed all the error checks, we return a JSON object with a key
answer
which is the result of callingj()
on the values ofparamsToPass
, 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: