So for anyone who thought that I was going to write image manipulations by hand and discuss that… sorry, I used a package.
It’s been a while since I spoke about the endpoints so a quick refresher. Instead of having a single endpoint that will take the image and the name of a manipulation, I split each manipulation into its own endpoint so it will handle its own validation and I felt it would be easier for people to use.
I have since come to learn that this is not generally how things are done and that using a single endpoint makes it more ‘resourceful’. I am seriously contemplating redesigning the endpoints to work in this way, making it version 2 of the API. I also think it would be good practice to learn how to implement versioning in an API.
So the package that I used for the image manipulations is Sharp which made doing the manipulations much easier for me. After getting an image decoded, all I had to do was pass it into Sharp and use the specific manipulation I wanted (and add options if I needed it) and then do what I want with the manipulated image. Sharp has two main ways of getting back the image. First, you can get back an image buffer and do with that what you will and second, you can write the file to disk. For the latter, sharp allows you to pass a callback function which runs upon the write being completed so you don’t necessarily have to try and await sharp if you need to use the file that was written to disk. I only know this because I had initially designed the API to use the files written to disk rather than passing around a buffer and I had trouble locating the file because when I tried to return it, it would try to return it before the write was completed. Was a bit of a pain. I eventually solved the problem by passing Express’ request object into the manipulation function so that I could send the request in the callback function of the Sharp’s write file function.
By the way, I hope you cringed when you read that cause that wasn’t a great way of solving the problem but it was what I knew at the time. I eventually was able to write a much cleaner version of the manipulation functions but I’ll talk about that in a later article.
There are 5 manipulations I implemented in order the complete the app:
Resize (change the image’s size)
Blur (add a blur to the image)
Transform (transform the image based on parameters)
Greyscale (Made the image mostly gray colored)
Negate (reverse all the colors in the image)
The manipulations Blur, Greyscale and Negate have relatively the same implementation and are relatively short so I’ll talk about them first. Honestly, the ‘hardest’ part of these manipulations would have been decoding the base64 image. It was as simple as passing the image to the function, getting what it returned and then doing what I needed with that result. Resize was a bit more complicated because it had a parameter for the resizing. First of all, I enforced that an image can only be made smaller, not bigger. In my documentation, I joked that the Blood Contract I signed by attending an art school and learning digital art disallows me from knowingly making pixelated images. How I did the resizing was through a percentage which the user would pass. In Sharp, I could get the size of an image that I passed into Sharp and then use the scale the user gave me to get the new width and height and pass that into Sharp’s resizing function. A bit of work trying to figure out how to get the width and the height but not too much work.
Transformation was significantly harder.
So first of all, Transformation is not a single manipulation but potentially a collection of manipulations. In my head I figured that the user could specify all, some or only one of the manipulations and get back a result. To keep things simple, the manipulations I allowed in transformation was resize (since I’ve done it before) which is a number, rotation which is also a number, xAxisFlip which is a boolean and yAxisFlip which is a boolean. The hardest part was the validation for all these parameters. What I wanted was that if at least one of the parameters were present that you could use the manipulation and get a result. Not only did this make validation complicated but it also made running the manipulations a bit more complicated. I learned a bit more about how Sharp works in writing this function because what I ended up doing was doing the manipulation and just passing it back into the variable it was in. Now if a parameter wasn’t present, I’d just skip that manipulation but if it was present, I’d run the manipulation and pass in the values if needed.
Took a couple of days to get everything working but I was pretty happy with it. One of my prouder parts of working on this application.
Here’s a commit with my completed APIs: https://github.com/chadstewart/you-go-backend-project/tree/b19f5b1b0c1d951ad1836e4a7d71754b3222199c/nodejs/src/controllers/image-controllers
In the next article in the series, I’ll talk about writing tests for the API.
Top comments (0)