Building a Profile Picture Maker API with the Massless Media API

2021 July 1st

In this post we're going to use the Massless Media API to build a new Profile Picture Maker API. See the Getting Started article to get your API key and let's get going! The code for this post is available on our GitHub examples repository.

Customizable profile pictures for your social app

Let's imagine you're developing a social web application and you would like to offer customisable profile pictures to your users.

We're going to create a new backend route that:

  • Takes a selfie photo submited by a form post
  • Removes the background
  • Applys a greyscale filter
  • Resizes it
  • Converts it to JPG
  • Compresses it
  • And creates a public URL

This will demonstrate how to pass Massless Media objects between API calls, and use some of the functionality of the API to create new an interesting applications.

Create the Node.js express server

Let's install the node packages we'll need and create the Node.js express server for the project.

1$ mkdir profile-picture-maker
2$ cd profile-picture-maker
3$ npm init -y
4$ npm install express axios multer form-data dotenv

Make a .env file in the directory and add your Massless API Key

.env

1MEDIA_API_KEY = <YOUR-API-KEY>

Copy this starter code to the index.js file

index.js

1require('dotenv').config({ path: __dirname + '/.env' });
2
3const axios = require('axios');
4const express = require('express');
5const multer = require('multer');
6var FormData = require('form-data');
7
8const app = express();
9const port = 3000;
10const upload = multer();
11
12const headers = { 'x-api-key': process.env.MEDIA_API_KEY };
13
14app.post('/', upload.single('file'), async (req, res) => {
15 // Forward the file form data
16 const form = new FormData();
17 form.append('file', req.file.buffer, { filename: req.file.originalname });
18
19 // Call Media API
20 const response = await axios.post('https://api.massless.io/v1/image/filter/remove-background', form, {
21 responseType: 'arraybuffer',
22 headers: { ...headers, ...form.getHeaders() }
23 });
24
25 // Respond with processed image
26 const image = Buffer.from(response.data, 'base64');
27 res.end(image);
28});
29
30app.listen(port, () => {
31 console.log(`Example app listening at http://localhost:${port}`);
32});

This starter project

  1. Accepts a POST request with multipart/form data containing a file
  2. Constructs a new form data to forward the file
  3. Calls the Media API to remove the image background
  4. Responds with the image

Let's run the server and use Postman to test the new API.

1$ node index.js

In Postman

  • Go to File > New and create a new HTTP request
  • Set the verb to POST
  • Enter the request URL as localhost:8080
  • Go to the "Body" tab
  • Select "form-data" as the body type
  • In the "Key" field write "file"
  • In the "Value" field click "Select Files" and select a headshot

Submitting the following image

jack

Gives this result in Postman

postman-remove-bg

Nice!

Using media objects for multiple operations

So far we have used the form-data interface of the Media API. This is convenient for single operations, but if we want to perform multiple operations then we don't want to be uploading and download the media each time. Instead we can take the input, construct a Massles Media object with a unique mediaId, and then pass this mediaId the next API call.

Let's look at an example where we

  • Host the media to get a mediaId
  • Call Remove background with this mediaId
  • Pass the resulting mediaId to greyscale
  • Fetch the final image mediaId

The POST request now looks like

1app.post('/', upload.single('file'), async (req, res) => {
2 // Forward the file from the form data
3 const form = new FormData();
4 form.append('file', req.file.buffer, { filename: req.file.originalname });
5
6 // Host the media to get a mediaId
7 const {
8 data: { mediaId }
9 } = await axios.post('https://api.massless.io/v1/media', form, {
10 headers: { ...headers, ...form.getHeaders() }
11 });
12
13 // Remove background
14 const {
15 data: { mediaId: rbgId }
16 } = await axios.post(
17 'https://api.massless.io/v1/image/ml/remove-background',
18 { mediaId },
19 {
20 headers
21 }
22 );
23
24 // Greyscale
25 const {
26 data: { mediaId: greyId }
27 } = await axios.post(
28 'https://api.massless.io/v1/image/filter/greyscale',
29 { mediaId: rbgId },
30 {
31 headers
32 }
33 );
34
35 // Fetch the image
36 const response = await axios.get(`https://api.massless.io/v1/media/${greyId}`, {
37 responseType: 'arraybuffer',
38 headers
39 });
40
41 // Respond with processed image
42 const image = Buffer.from(response.data, 'base64');
43 res.end(image);
44});

rbg-grey

Let's perform the final steps in our image processing and resize, convert to jpg, then compress, and finally publish the image to get a public URL for our social app.

Our final post request becomes

1app.post('/', upload.single('file'), async (req, res) => {
2 // Forward the file from the form data
3 const form = new FormData();
4 form.append('file', req.file.buffer, { filename: req.file.originalname });
5
6 // Host the media to get a mediaId
7 const {
8 data: { mediaId }
9 } = await axios.post('https://api.massless.io/v1/media', form, {
10 headers: { ...headers, ...form.getHeaders() }
11 });
12
13 // Remove background
14 const {
15 data: { mediaId: rbgId }
16 } = await axios.post(
17 'https://api.massless.io/v1/image/ml/remove-background',
18 { mediaId },
19 {
20 headers
21 }
22 );
23
24 // Greyscale
25 const {
26 data: { mediaId: greyId }
27 } = await axios.post(
28 'https://api.massless.io/v1/image/filter/greyscale',
29 { mediaId: rbgId },
30 {
31 headers
32 }
33 );
34
35 // Resize to 64 px wide
36 const {
37 data: { mediaId: resizeId }
38 } = await axios.post(
39 'https://api.massless.io/v1/image/resize',
40 { mediaId: greyId, width: 64 },
41 {
42 headers
43 }
44 );
45
46 // Compress
47 const {
48 data: { mediaId: compressId }
49 } = await axios.post(
50 'https://api.massless.io/v1/image/compress',
51 { mediaId: resizeId, quality: 85 },
52 {
53 headers
54 }
55 );
56
57 // Publish
58 const {
59 data: { url }
60 } = await axios.post(
61 'https://api.massless.io/v1/media/publish',
62 { mediaId: compressId },
63 {
64 headers
65 }
66 );
67
68 // Respond with url
69 res.json({ url });
70});

Which responds with

1{
2 "url": "https://massless.media/vGOFVIoKvwSAbVe2Qz7O"
3}

Giving the image

profile-pic

Perfect for our app!

Extending the functionality

In this example we started with a square image, but we could imagine extending this to crop rectangular images so they become square. We could even use the face detection operation in the Massless Media API to make sure the crop is centered about the face.

What do you think?

The Media API is currently in Alpha release and we would love to hear what you think and what features you would like to see. Email us at api@massless.io or join us on Discord.