fcm

package module
v1.2.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 20, 2025 License: MIT Imports: 13 Imported by: 78

README

go-fcm

English | 繁體中文 | 简体中文

GoDoc Lint and Testing Go Report Card

Forked from github.com/edganiukov/fcm
Firebase Cloud Messaging Official Documentation


Table of Contents


Features

Feature Supported Description
Single Device Messaging Send messages to a single device
Multiple Device Messaging Send messages to multiple devices
Topic Messaging Send messages to a specific topic
Condition Messaging Support for FCM condition syntax
Custom HTTP Client Custom timeout, proxy, and transport config
Multiple Message Formats Data, Notification, Multicast
Unit Test & Mock Support Easy unit testing with mock client

Supported Message Types

Type Description
Data Custom data messages, handled by the app
Notification System notification messages, shown in notification bar
Multicast Send to up to 500 device tokens at once
Topic Send to all devices subscribed to a topic
Condition Send to devices matching a logical condition

Quick Start

Install go-fcm:

go get github.com/appleboy/go-fcm

Authentication and Credentials

It is recommended to use Google Application Default Credentials (ADC) for authentication.
Download the JSON key from Firebase Console > Settings > Service Accounts and set the environment variable:

export GOOGLE_APPLICATION_CREDENTIALS="/path/to/serviceAccountKey.json"

Alternatively, specify the key path directly in your code.


Usage Example

package main

import (
  "context"
  "fmt"
  "log"

  "firebase.google.com/go/v4/messaging"
  fcm "github.com/appleboy/go-fcm"
)

func main() {
  ctx := context.Background()
  client, err := fcm.NewClient(
    ctx,
    fcm.WithCredentialsFile("path/to/serviceAccountKey.json"),
    // fcm.WithServiceAccount("my-client-id@my-project-id.iam.gserviceaccount.com"),
  )
  if err != nil {
    log.Fatal(err)
  }

  // Send to a single device
  token := "YOUR_DEVICE_TOKEN"
  resp, err := client.Send(
    ctx,
    &messaging.Message{
      Token: token,
      Data: map[string]string{
        "foo": "bar",
      },
    },
  )
  if err != nil {
    log.Fatal(err)
  }
  fmt.Println("Success:", resp.SuccessCount, "Failure:", resp.FailureCount)

  // Send to a topic
  resp, err = client.Send(
    ctx,
    &messaging.Message{
      Data: map[string]string{
        "foo": "bar",
      },
      Topic: "highScores",
    },
  )
  if err != nil {
    log.Fatal(err)
  }

  // Send with condition
  condition := "'stock-GOOG' in topics || 'industry-tech' in topics"
  message := &messaging.Message{
    Data: map[string]string{
      "score": "850",
      "time":  "2:45",
    },
    Condition: condition,
  }
  resp, err = client.Send(ctx, message)
  if err != nil {
    log.Fatal(err)
  }

  // Send to multiple devices
  registrationToken := "YOUR_REGISTRATION_TOKEN"
  messages := []*messaging.Message{
    {
      Notification: &messaging.Notification{
        Title: "Price drop",
        Body:  "5% off all electronics",
      },
      Token: registrationToken,
    },
    {
      Notification: &messaging.Notification{
        Title: "Price drop",
        Body:  "2% off all books",
      },
      Topic: "readers-club",
    },
  }
  resp, err = client.Send(ctx, messages...)
  if err != nil {
    log.Fatal(err)
  }

  // Multicast messaging
  registrationTokens := []string{
    "YOUR_REGISTRATION_TOKEN_1",
    "YOUR_REGISTRATION_TOKEN_2",
    // ...
  }
  msg := &messaging.MulticastMessage{
    Data: map[string]string{
      "score": "850",
      "time":  "2:45",
    },
    Tokens: registrationTokens,
  }
  resp, err = client.SendMulticast(ctx, msg)
  if err != nil {
    log.Fatal(err)
  }
  fmt.Printf("%d messages were sent successfully\n", resp.SuccessCount)
  if resp.FailureCount > 0 {
    var failedTokens []string
    for idx, resp := range resp.Responses {
      if !resp.Success {
        failedTokens = append(failedTokens, registrationTokens[idx])
      }
    }
    fmt.Printf("List of tokens that caused failures: %v\n", failedTokens)
  }
}

Advanced Configuration

Custom HTTP Client
import (
  "crypto/tls"
  "net"
  "net/http"
  "time"
  "golang.org/x/net/http2"
)

func main() {
  httpTimeout := 5 * time.Second
  tlsTimeout := 5 * time.Second

  transport := &http2.Transport{
    DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
      return tls.DialWithDialer(&net.Dialer{Timeout: tlsTimeout}, network, addr, cfg)
    },
  }

  httpClient := &http.Client{
    Transport: transport,
    Timeout:   httpTimeout,
  }

  ctx := context.Background()
  client, err := fcm.NewClient(
    ctx,
    fcm.WithCredentialsFile("path/to/serviceAccountKey.json"),
    fcm.WithHTTPClient(httpClient),
  )
}
Proxy Support
func main() {
  ctx := context.Background()
  client, err := fcm.NewClient(
    ctx,
    fcm.WithCredentialsFile("path/to/serviceAccountKey.json"),
    fcm.WithHTTPProxy("http://localhost:8088"),
  )
}
Unit Testing and Mock
import (
  "context"
  "net/http"
  "net/http/httptest"
  "testing"

  "firebase.google.com/go/v4/messaging"
  fcm "github.com/appleboy/go-fcm"
  "google.golang.org/api/option"
)

func TestMockClient(t *testing.T) {
  server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
    w.Header().Set("Content-Type", "application/json")
    _, _ = w.Write([]byte(`{"name": "q1w2e3r4"}`))
  }))
  defer server.Close()

  client, err := fcm.NewClient(
    context.Background(),
    fcm.WithEndpoint(server.URL),
    fcm.WithProjectID("test"),
    fcm.WithCustomClientOption(option.WithoutAuthentication()),
  )
  if err != nil {
    t.Fatalf("unexpected error: %v", err)
  }
  resp, err := client.Send(
    context.Background(),
    &messaging.Message{
      Token: "test",
      Data: map[string]string{
        "foo": "bar",
      },
    })
  if err != nil {
    t.Fatalf("unexpected error: %v", err)
  }
  // Check response
  if resp.SuccessCount != 1 {
    t.Fatalf("expected 1 successes, got: %d", resp.SuccessCount)
  }
  if resp.FailureCount != 0 {
    t.Fatalf("expected 0 failures, got: %d", resp.FailureCount)
  }
}

Best Practices

[!TIP]

  • For batch messaging, limit each batch to no more than 500 tokens.
  • Prefer using Topics to manage device groups instead of direct token management.
  • Set your credentials file as read-only and store it securely.

Troubleshooting

Error Code Possible Cause & Solution
UNREGISTERED Token is invalid or expired, remove from DB
INVALID_ARGUMENT Invalid message format, check your payload
QUOTA_EXCEEDED FCM quota exceeded, try again later
UNAUTHORIZED Invalid credentials, check your key and access
INTERNAL FCM server error, retry the request

Architecture Diagram

flowchart TD
    A[Your Go Server] -->|go-fcm| B[FCM API]
    B --> C[Android/iOS/Web Device]
    B -.-> D[Topic/Condition Routing]

FAQ

  • Q: How do I obtain an FCM device token?
  • Q: How do I set up multi-language notifications?
  • Q: How do I track message delivery status?

For more, see the Firebase Official FAQ


License

This project is licensed under the MIT License.


Documentation

Overview

Package fcm provides Firebase Cloud Messaging functionality for Golang

Here is a simple example illustrating how to use FCM library:

func main() {
	ctx := context.Background()
	client, err := fcm.NewClient(
		ctx,
		fcm.WithCredentialsFile("path/to/serviceAccountKey.json"),
	)
	if err != nil {
		log.Fatal(err)
	}

// Send to a single device
token := "test"
resp, err := client.Send(

ctx,
&messaging.Message{
	Token: token,
	Data: map[string]string{
		"foo": "bar",
	},
},

)

	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("success count:", resp.SuccessCount)
	fmt.Println("failure count:", resp.FailureCount)
	fmt.Println("message id:", resp.Responses[0].MessageID)
	fmt.Println("error msg:", resp.Responses[0].Error)
}

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client abstracts the interaction between the application server and the FCM server via HTTP protocol. The developer must obtain an API key from the Google APIs Console page and pass it to the `Client` so that it can perform authorized requests on the application server's behalf. To send a message to one or more devices use the Client's Send.

If the `HTTP` field is nil, a zeroed http.Client will be allocated and used to send messages.

Authorization Scopes Requires one of the following OAuth scopes: - https://www.googleapis.com/auth/firebase.messaging

func NewClient

func NewClient(ctx context.Context, opts ...Option) (*Client, error)

NewClient creates new Firebase Cloud Messaging Client based on API key and with default endpoint and http client.

func (*Client) Send

func (c *Client) Send(ctx context.Context, message ...*messaging.Message) (*messaging.BatchResponse, error)

SendWithContext sends a message to the FCM server without retrying in case of service unavailability. A non-nil error is returned if a non-recoverable error occurs (i.e. if the response status is not "200 OK"). Behaves just like regular send, but uses external context.

func (*Client) SendDryRun added in v1.0.0

func (c *Client) SendDryRun(ctx context.Context, message ...*messaging.Message) (*messaging.BatchResponse, error)

SendDryRun sends the messages in the given array via Firebase Cloud Messaging in the dry run (validation only) mode.

func (*Client) SendMulticast added in v1.0.0

func (c *Client) SendMulticast(ctx context.Context, message *messaging.MulticastMessage) (*messaging.BatchResponse, error)

SendEachForMulticast sends the given multicast message to all the FCM registration tokens specified.

func (*Client) SendMulticastDryRun added in v1.0.0

func (c *Client) SendMulticastDryRun(ctx context.Context, message *messaging.MulticastMessage) (*messaging.BatchResponse, error)

SendEachForMulticastDryRun sends the given multicast message to all the specified FCM registration tokens in the dry run (validation only) mode.

func (*Client) SubscribeTopic added in v1.0.0

func (c *Client) SubscribeTopic(ctx context.Context, tokens []string, topic string) (*messaging.TopicManagementResponse, error)

SubscribeToTopic subscribes a list of registration tokens to a topic.

The tokens list must not be empty, and have at most 1000 tokens.

func (*Client) UnsubscribeTopic added in v1.0.0

func (c *Client) UnsubscribeTopic(ctx context.Context, tokens []string, topic string) (*messaging.TopicManagementResponse, error)

UnsubscribeFromTopic unsubscribes a list of registration tokens from a topic.

The tokens list must not be empty, and have at most 1000 tokens.

type Option

type Option func(*Client) error

Option configurates Client with defined option.

func WithCredentialsFile added in v1.0.0

func WithCredentialsFile(filename string) Option

WithCredentialsFile returns a ClientOption that authenticates API calls with the given service account or refresh token JSON credentials file.

func WithCredentialsJSON added in v1.0.0

func WithCredentialsJSON(data []byte) Option

WithCredentialsJSON returns a ClientOption that authenticates API calls with the given service account or refresh token JSON credentials.

func WithCustomClientOption added in v1.2.0

func WithCustomClientOption(opts ...option.ClientOption) Option

WithCustomClientOption is an option function that allows you to provide custom client options. It appends the provided custom options to the client's options list. The custom options are applied when sending requests to the FCM server. If no custom options are provided, this function does nothing.

Parameters:

  • opts: The custom client options to be appended to the client's options list.

Returns:

  • An error if there was an issue appending the custom options to the client's options list, or nil otherwise.

func WithDebug added in v1.1.0

func WithDebug(debug bool) Option

WithDebug returns Option to configure debug mode.

func WithEndpoint

func WithEndpoint(endpoint string) Option

WithEndpoint returns Option to configure endpoint.

func WithHTTPClient

func WithHTTPClient(httpClient *http.Client) Option

WithHTTPClient returns Option to configure HTTP Client.

func WithHTTPProxy added in v0.1.6

func WithHTTPProxy(proxyURL string) Option

WithHTTPProxy returns Option to configure HTTP Client proxy.

func WithProjectID added in v1.0.0

func WithProjectID(projectID string) Option

WithProjectID returns Option to configure project ID.

func WithServiceAccount added in v1.0.0

func WithServiceAccount(serviceAccount string) Option

WithServiceAccount returns Option to configure service account.

func WithTokenSource added in v1.0.0

func WithTokenSource(s oauth2.TokenSource) Option

WithTokenSource returns a ClientOption that specifies an OAuth2 token source to be used as the basis for authentication.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL