A Facebook Graph API SDK In Golang
This is a Go package that fully supports the Facebook Graph API with file upload, batch request and marketing API. It can be used in Google App Engine.
API documentation can be found on godoc.
Feel free to create an issue or send me a pull request if you have any "how-to" question or bug or suggestion when using this package. I'll try my best to reply to it.
Install
If go mod
is enabled, install this package with go get github.com/ga-commerce/facebook/v2
. If not, call go get -u github.com/huandu/facebook
to get the latest master branch version.
Note that, since go1.14, incompatible versions are omitted unless specified explicitly. Therefore, it's highly recommended to upgrade the import path to github.com/ga-commerce/facebook/v2
when possible to avoid any potential dependency error.
Usage
Quick start
Here is a sample that reads my Facebook first name by uid.
package main
import (
"fmt"
fb "github.com/ga-commerce/facebook/v2"
)
func main() {
res, _ := fb.Get("/538744468", fb.Params{
"fields": "first_name",
"access_token": "a-valid-access-token",
})
fmt.Println("Here is my Facebook first name:", res["first_name"])
}
The type of res
is fb.Result
(a.k.a. map[string]interface{}
).
This type has several useful methods to decode res
to any Go type safely.
var first_name string
res.DecodeField("first_name", &first_name)
fmt.Println("Here's an alternative way to get first_name:", first_name)
type User struct {
FirstName string
}
var user User
res.Decode(&user)
fmt.Println("print first_name in struct:", user.FirstName)
If a type implements the json.Unmarshaler
interface, Decode
or DecodeField
will use it to unmarshal JSON.
res := Result{
"create_time": "2006-01-02T15:16:17Z",
}
var tm time.Time
res.DecodeField("create_time", &tm)
Read a graph user
object with a valid access token
res, err := fb.Get("/me/feed", fb.Params{
"access_token": "a-valid-access-token",
})
if err != nil {
if e, ok := err.(*Error); ok {
fmt.Printf("facebook error. [message:%v] [type:%v] [code:%v] [subcode:%v] [trace:%v]",
e.Message, e.Type, e.Code, e.ErrorSubcode, e.TraceID)
return
}
if e, ok := err.(*UnmarshalError); ok {
fmt.Printf("facebook error. [message:%v] [err:%v] [payload:%v]",
e.Message, e.Err, string(e.Payload))
return
}
return
}
fmt.Println("My latest feed story is:", res.Get("data.0.story"))
Read a graph search
for page and decode slice of maps
res, _ := fb.Get("/pages/search", fb.Params{
"access_token": "a-valid-access-token",
"q": "nightlife,singapore",
})
var items []fb.Result
err := res.DecodeField("data", &items)
if err != nil {
fmt.Printf("An error has happened %v", err)
return
}
for _, item := range items {
fmt.Println(item["id"])
}
Use App
and Session
It's recommended to use App
and Session
in a production app. They provide more control over all API calls. They can also make code clearer and more concise.
var globalApp = fb.New("your-app-id", "your-app-secret")
globalApp.RedirectUri = "http://your.site/canvas/url/"
session, _ := globalApp.SessionFromSignedRequest(signedRequest)
session := globalApp.Session(token)
err := session.Validate()
res, _ := session.Get("/me/feed", nil)
By default, all requests are sent to Facebook servers. If you wish to override the API base URL for unit-testing purposes - just set the respective Session
field.
testSrv := httptest.NewServer(someMux)
session.BaseURL = testSrv.URL + "/"
Facebook returns most timestamps in an ISO9601 format which can't be natively parsed by Go's encoding/json
.
Setting RFC3339Timestamps
true
on the Session
or at the global level will cause proper RFC3339 timestamps to be requested from Facebook.
RFC3339 is what encoding/json
natively expects.
fb.RFC3339Timestamps = true
session.RFC3339Timestamps = true
Setting either of these to true will cause date_format=Y-m-d\TH:i:sP
to be sent as a parameter on every request. The format string is a PHP date()
representation of RFC3339.
More info is available in this issue.
Use paging
field in response
Some Graph API responses use a special JSON structure to provide paging information. Use Result.Paging()
to walk through all data in such results.
res, _ := session.Get("/me/home", nil)
paging, _ := res.Paging(session)
var allResults []Result
allResults = append(allResults, paging.Data()...)
for {
noMore, err := paging.Next()
if err != nil {
panic(err)
}
if noMore {
break
}
allResults = append(allResults, paging.Data()...)
}
Read Graph API response and decode result in a struct
The Facebook Graph API always uses snake case keys in API response.
This package can automatically convert from snake case to Go's camel-case-style style struct field names.
For instance, to decode the following JSON response...
{
"foo_bar": "player"
}
One can use the following struct.
type Data struct {
FooBar string
}
The decoding of each struct field can be customized by the format string stored under the facebook
key or the "json" key in the struct field's tag. The facebook
key is recommended as it's specifically designed for this package.
Following is a sample that shows all possible field tags.
type FacebookFeed struct {
Id string `facebook:",required"`
Story string
FeedFrom *FacebookFeedFrom `facebook:"from"`
CreatedTime string `facebook:"created_time,required"`
Omitted string `facebook:"-"`
}
type FacebookFeedFrom struct {
Name string `json:"name"`
Id string `facebook:"id" json:"shadowed"`
}
var feed FacebookFeed
res, _ := session.Get("/me/feed", nil)
res.DecodeField("data.0", &feed)
Send a batch request
params1 := Params{
"method": fb.GET,
"relative_url": "me",
}
params2 := Params{
"method": fb.GET,
"relative_url": uint64(100002828925788),
}
results, err := fb.BatchApi(your_access_token, params1, params2)
if err != nil {
return
}
batchResult1, _ := results[0].Batch()
batchResult2, _ := results[1].Batch()
var id string
res := batchResult1.Result
res.DecodeField("id", &id)
contentType := batchResult1.Header.Get("Content-Type")
Using with Google App Engine
Google App Engine provides the appengine/urlfetch
package as the standard HTTP client package.
For this reason, the default client in net/http
won't work.
One must explicitly set the HTTP client in Session
to make it work.
import (
"appengine"
"appengine/urlfetch"
)
var context appengine.Context
seesion := globalApp.Session("a-access-token")
session.HttpClient = urlfetch.Client(context)
res, err := session.Get("/me", nil)
Select Graph API version
See Platform Versioning to understand the Facebook versioning strategy.
fb.Version = "v3.0"
fb.Api("huan.du", GET, nil)
session := &fb.Session{}
session.Version = "v3.0"
Enable appsecret_proof
Facebook can verify Graph API Calls with appsecret_proof
. It's a feature to make Graph API call more secure. See Securing Graph API Requests to know more about it.
globalApp := fb.New("your-app-id", "your-app-secret")
globalApp.EnableAppsecretProof = true
session := globalApp.Session("a-valid-access-token")
session.Get("/me", nil)
session.EnableAppsecretProof(false)
Debugging API Requests
Facebook has introduced a way to debug Graph API calls. See Debugging API Requests for more details.
This package provides both a package level and per session debug flag. Set Debug
to a DEBUG_*
constant to change debug mode globally, or use Session#SetDebug
to change debug mode for one session.
When debug mode is turned on, use Result#DebugInfo
to get DebugInfo
struct from the result.
fb.Debug = fb.DEBUG_ALL
res, _ := fb.Get("/me", fb.Params{"access_token": "xxx"})
debugInfo := res.DebugInfo()
fmt.Println("http headers:", debugInfo.Header)
fmt.Println("facebook api version:", debugInfo.FacebookApiVersion)
Monitoring API usage info
Call Result#UsageInfo
to get a UsageInfo
struct containing both app and page-level rate limit information from the result. More information about rate limiting can be found here.
res, _ := fb.Get("/me", fb.Params{"access_token": "xxx"})
usageInfo := res.UsageInfo()
fmt.Println("App level rate limit information:", usageInfo.App)
fmt.Println("Page level rate limit information:", usageInfo.Page)
fmt.Println("Ad account rate limiting information:", usageInfo.AdAccount)
fmt.Println("Business use case usage information:", usageInfo.BusinessUseCase)
Work with package golang.org/x/oauth2
The golang.org/x/oauth2
package can handle the Facebook OAuth2 authentication process and access token quite well. This package can work with it by setting Session#HttpClient
to OAuth2's client.
import (
"golang.org/x/oauth2"
oauth2fb "golang.org/x/oauth2/facebook"
fb "github.com/ga-commerce/facebook/v2"
)
conf := &oauth2.Config{
ClientID: "AppId",
ClientSecret: "AppSecret",
RedirectURL: "CallbackURL",
Scopes: []string{"email"},
Endpoint: oauth2fb.Endpoint,
}
token, err := conf.Exchange(oauth2.NoContext, "code")
client := conf.Client(oauth2.NoContext, token)
session := &fb.Session{
Version: "v2.4",
HttpClient: client,
}
res, _ := session.Get("/me", nil)
Control timeout and cancelation with Context
The Session
accept a Context
.
ctx, cancel := context.WithTimeout(session.Context(), 100 * time.Millisecond)
defer cancel()
result, err := session.WithContext(ctx).Get("/me", nil)
See this Go blog post about context for more details about how to use Context
.
Change Log
See CHANGELOG.md.
Out of Scope
- No OAuth integration. This package only provides APIs to parse/verify access token and code generated in OAuth 2.0 authentication process.
- No old RESTful API and FQL support. Such APIs are deprecated for years. Forget about them.
License
This package is licensed under the MIT license. See LICENSE for details.