4 min read

Elegant way to send firebase push notifications from Spring Boot.

September 10, 2020

Mobile notifications play a major role in user engagement. With firebase’s new SDK for java things got a lot easier. In this example, We will try to send a simple notification to a topic.

Usually this was done using a rest call to https://fcm.googleapis.com/fcm/send with a json request. Even though this seem simple, once you span accross iOS and web-push, things can get out of hand pretty soon. For this reason, Firebase SDK comes with predefined methods that can help clear this mess.

To include the Firebase-SDK you need to add the following maven dependency to your project.

        <dependency>
            <groupId>com.google.firebase</groupId>
            <artifactId>firebase-admin</artifactId>
            <version>7.0.0</version>
        </dependency>

The next thing you have to do is to generate a service-account.json. For this open the Service Accounts in firebase console and click Generate Private Key.

Generate private Key

This will download a file in the form of <firebase-project>-firebase-adminsdk-<hash>.json. Save this file src/java/resources as firebase-service-account.json.

In our spring boot application, Let’s create an object for FirebaseMessaging and register it as a bean. This bean will be used to send notifications.

@Bean
FirebaseMessaging firebaseMessaging() throws IOException {
    GoogleCredentials googleCredentials = GoogleCredentials
            .fromStream(new ClassPathResource("firebase-service-account.json").getInputStream());
    FirebaseOptions firebaseOptions = FirebaseOptions
            .builder()
            .setCredentials(googleCredentials)
            .build();
    FirebaseApp app = FirebaseApp.initializeApp(firebaseOptions, "my-app");
    return FirebaseMessaging.getInstance(app);
}

Here new ClassPathResource("firebase-service-account.json").getInputStream() is an easier way to create an InputStream for a file that is in classpath. With this we created a FirebaseApp object from which we got our FirebaseMessaging instance.

I wrote a simple FirebaseMessagingService by using the bean that we just created. In a very basic use case, the class would look like this.

@Service
public class FirebaseMessagingService {

    private final FirebaseMessaging firebaseMessaging;

    public FirebaseMessagingService(FirebaseMessaging firebaseMessaging) {
        this.firebaseMessaging = firebaseMessaging;
    }


    public String sendNotification(Note note, String token) throws FirebaseMessagingException {

        Notification notification = Notification
                .builder()
                .setTitle(note.getSubject())
                .setBody(note.getContent())
                .build();

        Message message = Message
                .builder()
                .setToken(token)
                .setNotification(notification)
                .putAllData(note.getData())
                .build();

        return firebaseMessaging.send(message);
    }

}

Here, The Notification object captures what Title and Body to show on the mobile notification.

The Messageobject has multiple builder methods to deal with various scenarios.

  • You can send a message to a specific recipient by using setToken(String) or you can send it to anyone who is subscribed to a topic by using setTopic(String)
  • You can pass Additional data to the mobile applications using putData(String,String) or putAllData(Map<String,String>).
  • You can customize options specific to Android, iOS and web clients using the below builder methods respectively.

    • setAndroidConfig()
    • setApnsConfig()
    • setWebpushConfig()
  • Each of these methods are backed by a Builder specific to their type.

Once you have created a message object with appropriate values, then you can call firebaseMessaging.send(Message) to submit the request. The method will return a String which represents the message ID that was sent during the api call. There is no need to store this value. Note that there can be a FirebaseMessagingException if there is an error while processing the message while sending. So make sure you handle it.

Let’s write a simple test API call.

@Data
public class Note {
    private String subject;
    private String content;
    private Map<String, String> data;
    private String image;
}

/*******************************/

@RequestMapping("/send-notification")
@ResponseBody
public String sendNotification(@RequestBody Note note,
                               @RequestParam String token) throws FirebaseMessagingException {
    return firebaseService.sendNotification(note, token);
}

A simple curl command with a sample post date yeilds the following result.

$ curl -X POST "http://localhost:8080/send-notification?topic=gold" \
  -H "Content-Type: application/json" \
  -d '{
    "subject": "some subject",
    "content": "Some long content",
    "image": "https://somedomain.com/example.jpg",
    "data": {
      "key1": "Value 1",
      "key2": "Value 2",
      "key3": "Value 3",
      "key4": "Value 4"
    }
  }'

HTTP/1.1 200 
Content-Type: text/plain;charset=UTF-8
Content-Length: 53
Date: Thu, 10 Sep 2020 14:21:50 GMT
Keep-Alive: timeout=60
Connection: keep-alive

projects/spring-demo/messages/2282285117414704507

Notice how the response is projects/spring-demo/messages/2282285117414704507 which is the message ID of the fired message to FCM. This message can be used to debug the mobile client code.

Note that in the github example, You will have to replace the firebase-service-account.json downloaded from your FCM project.

Raja Anbazhagan

About the author

Raja is a Software Engineer with over 7 years of experience in working with Enterprise Java applications. Lately, He is focused in cloud-based Java applications and serverless technologies. He spends his spare time in stackoverflow.

Browse Categories