facebook

package module
v1.3.0 Latest Latest
Warning

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

Go to latest
Published: Dec 6, 2014 License: MIT Imports: 18 Imported by: 0

README

A Facebook Graph API SDK In Golang

Build Status

This is a Go library fully supports Facebook Graph API (both 1.0 and 2.x) with file upload, batch request, FQL and multi-FQL. It can be used in Google App Engine.

See full document for API details.

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 library. I'll try my best to response it.

Usage

Quick Tutorial

Here is a sample to read my Facebook username by uid.

package main

import (
    "fmt"
    fb "github.com/huandu/facebook"
)

func main() {
    res, _ := fb.Get("/538744468", fb.Params{
        "fields": "username",
    })
    fmt.Println("here is my facebook username:", res["username"])
}

Type of res["username"] is interface{}. This library provides several helpful methods to decode fields to any Go type or even a custom Go struct.

// Decode "username" to a go string.
var username string
res.DecodeField("username", &username)
fmt.Println("alternative way to get username:", username)

// It's also possible to decode the whole result into a predefined struct.
type User struct {
    Username string
}

var user User
res.Decode(&user)
fmt.Println("print username in struct:", user.Username)
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 {
    // err can be an facebook API error.
    // if so, the Error struct contains error details.
    if e, ok := err.(*Error); ok {
        fmt.Logf("facebook error. [message:%v] [type:%v] [code:%v] [subcode:%v]",
            e.Message, e.Type, e.Code, e.ErrorSubcode)
        return
    }

    return
}

// read my last feed.
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("/search", fb.Params{
        "access_token": "a-valid-access-token",
        "type":         "page",
        "q":            "nightlife,singapore",
    })

var items []fb.Result

err := res.DecodeField("data", &items)

if err != nil {
    fmt.Logf("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 controls over all API calls. They can also make your code clear and concise.

// create a global App var to hold your app id and secret.
var globalApp = fb.New("your-app-id", "your-app-secret")

// facebook asks for a valid redirect uri when parsing signed request.
// it's a new enforced policy starting in late 2013.
globalApp.RedirectUri = "http://your.site/canvas/url/"

// here comes a client with a facebook signed request string in query string.
// creates a new session with signed request.
session, _ := globalApp.SessionFromSignedRequest(signedRequest)

// if you can get a valid access token in other way.
// creates a session directly with the token.
seesion := globalApp.Session(token)

// validate access token. err is nil if token is valid.
err := session.Validate()

// use session to send api request with your access token.
res, _ := session.Get("/me/feed", nil)
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)

// create a paging structure.
paging, _ := res.Paging(session)

// get current results.
results := paging.Data()

// get next page.
noMore, err := paging.Next()
results = paging.Data()
Read graph api response and decode result into a struct

As facebook Graph API always uses lower case words as keys in API response. This library can convert go's camel-case-style struct field name to facebook's underscore-style API key name.

For instance, given we have following JSON response from facebook.

{
    "foo_bar": "player"
}

We can use following struct to decode it.

type Data struct {
    FooBar string  // "FooBar" => "foo_bar"
}

Like encoding/json package, struct can have tag definitions. Tags can be used to mark a field as required in API response and/or map field to a specific key name.

Following is a full sample wrap up everything about struct decoding.

// define a facebook feed object.
type FacebookFeed struct {
    Id string `facebook:",required"`             // this field must exist in response
    Story string
    FeedFrom *FacebookFeedFrom `facebook:"from"` // use customized field name "from"
    CreatedTime string
}

type FacebookFeedFrom struct {
    Name, Id string
}

// create a feed object direct from graph api result.
var feed FacebookFeed
res, _ := session.Get("/me/feed", nil)
res.DecodeField("data.0", &feed) // read latest 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 {
    // check error...
    return
}

// batchResult1 and batchResult2 are response for params1 and params2.
batchResult1, _ := results[0].Batch()
batchResult2, _ := results[1].Batch()

// Use parsed result.
var id string
res := batchResult1.Result
res.DecodeField("id", &id)

// Use response header.
contentType := batchResult1.Header.Get("Content-Type")
Send FQL query
results, _ := fb.FQL("SELECT username FROM page WHERE page_id = 20531316728")
fmt.Println(results[0]["username"]) // print "facebook"

// most FQL query requires access token. create session to hold access token.
session := &fb.Session{}
session.SetAccessToken("A-VALID-ACCESS-TOKEN")
results, _ := session.FQL("SELECT username FROM page WHERE page_id = 20531316728")
fmt.Println(results[0]["username"]) // print "facebook"
Make multi-FQL
res, _ := fb.MultiFQL(Params{
    "query1": "SELECT username FROM page WHERE page_id = 20531316728",
    "query2": "SELECT uid FROM user WHERE uid = 538744468",
})
var query1, query2 []Result

// get response for query1 and query2.
res.DecodeField("query1", &query1)
res.DecodeField("query2", &query2)

// most FQL query requires access token. create session to hold access token.
session := &fb.Session{}
session.SetAccessToken("A-VALID-ACCESS-TOKEN")
res, _ := session.MultiFQL(Params{
    "query1": "...",
    "query2": "...",
})

// same as the sample without access token...
Use it in Google App Engine

Google App Engine provide appengine/urlfetch package as standard http client package. Default client in net/http doesn't work. One must explicitly set http client in Session to make it work.

import (
    "appengine"
    "appengine/urlfetch"
)

// suppose it's the appengine context initialized somewhere.
var context appengine.Context

// default Session object uses http.DefaultClient which is not allowed to use
// in appengine. we have to create a Session and assign it a special client.
seesion := globalApp.Session("a-access-token")
session.HttpClient = urlfetch.Client(context)

// now, session uses appengine http client now.
res, err := session.Get("/me", nil)
Select Graph API version

See Platform Versioning to understand facebook versioning strategy.

// this library uses default version which is controlled by facebook app setting.
// change following global variable to specific a global default version.
fb.Version = "v2.0"

// now you will get an error as v2.0 api doesn't allow you to do so.
fb.Api("huan.du", GET, nil)

// you can also specify version per session.
session := &fb.Session{}
session.Version = "v2.0" // overwrite global default.
Enable appsecret_proof

Facebook can verify Graph API Calls with appsecret_proof. It's a feature to make your Graph API call more secure. See Securing Graph API Requests to know more about it.

globalApp := fb.New("your-app-id", "your-app-secret")

// enable "appsecret_proof" for all sessions created by this app.
globalApp.EnableAppsecretProof = true

// all your calls in this session are secured.
session := globalApp.Session("a-valid-access-token")
session.Get("/me", nil)

// it's also possible to enable/disable this feature per session.
session.EnableAppsecretProof(false)

Change Log

See CHANGELOG.md.

TODO

  1. Real-time update subscriptions.

Get It

Use go get github.com/huandu/facebook to get and install it.

Out of Scope

  1. No OAuth integration. This library only provides APIs to parse/verify access token and code generated in OAuth 2.0 authentication process.
  2. No old RESTful API support. Such APIs are deprecated for years. Forget about them.

License

This library is licensed under MIT license. See LICENSE for details.

Documentation

Overview

This is a Go library fully supports Facebook Graph API (both 1.0 and 2.0) with file upload, batch request, FQL and multi-FQL. It can be used in Google App Engine.

Library design is highly influenced by facebook official PHP/JS SDK. If you have experience with PHP/JS SDK, you may feel quite familiar with it.

Go to project home page to see samples. Link: https://github.com/huandu/facebook

This library doesn't implement any deprecated old RESTful API. And it won't.

Index

Constants

View Source
const (
	ERROR_CODE_UNKNOWN = -1 // unknown facebook graph api error code.

)

Variables

View Source
var (
	// Default facebook api version.
	// It can be "v1.0" or "v2.0" or empty per facebook current document.
	// See https://developers.facebook.com/docs/apps/versions for details.
	Version string
)

Functions

func Data

func Data(filename string, source io.Reader) *binaryData

Creates a new binary data holder.

func File

func File(filename, path string) *binaryFile

Creates a binary file holder.

func FileAlias

func FileAlias(filename, path string) *binaryFile

Creates a binary file holder and specific a different path for reading.

Types

type App

type App struct {
	// Facebook app id
	AppId string

	// Facebook app secret
	AppSecret string

	// Facebook app redirect URI in the app's configuration.
	RedirectUri string

	// Enable appsecret proof in every API call to facebook.
	// Facebook document: https://developers.facebook.com/docs/graph-api/securing-requests
	EnableAppsecretProof bool
}

Holds facebook application information.

func New

func New(appId, appSecret string) *App

Creates a new App and sets app id and secret.

func (*App) AppAccessToken

func (app *App) AppAccessToken() string

Gets application access token, useful for gathering public information about users and applications.

func (*App) ExchangeToken

func (app *App) ExchangeToken(accessToken string) (token string, expires int, err error)

Exchange a short lived access token to a long lived access token. Return new access token and its expires time.

func (*App) GetCode

func (app *App) GetCode(accessToken string) (code string, err error)

Get code from a long lived access token. Return the code retrieved from facebook.

func (*App) ParseCode

func (app *App) ParseCode(code string) (token string, err error)

Parses facebook code to a valid access token.

In facebook PHP SDK, there is a CSRF state to avoid attack. That state is not checked in this library. Caller is responsible to store and check state if possible.

Returns a valid access token exchanged from a code.

func (*App) ParseSignedRequest

func (app *App) ParseSignedRequest(signedRequest string) (res Result, err error)

Parses signed request.

func (*App) Session

func (app *App) Session(accessToken string) *Session

Creates a session based on current App setting.

func (*App) SessionFromSignedRequest

func (app *App) SessionFromSignedRequest(signedRequest string) (session *Session, err error)

Creates a session from a signed request. If signed request contains a code, it will automatically use this code to exchange a valid access token.

type BatchResult added in v1.3.0

type BatchResult struct {
	StatusCode int         // HTTP status code.
	Header     http.Header // HTTP response headers.
	Body       string      // Raw HTTP response body string.
	Result     Result      // Facebook api result parsed from body.
}

Represents facebook batch API call result. See https://developers.facebook.com/docs/graph-api/making-multiple-requests/#multiple_methods.

type Error

type Error struct {
	Message      string
	Type         string
	Code         int
	ErrorSubcode int // subcode for authentication related errors.
}

Facebook API error.

func (*Error) Error

func (e *Error) Error() string

Returns error string.

type HttpClient

type HttpClient interface {
	Do(req *http.Request) (resp *http.Response, err error)
	Get(url string) (resp *http.Response, err error)
	Post(url string, bodyType string, body io.Reader) (resp *http.Response, err error)
}

An interface to send http request. This interface is designed to be compatible with type `*http.Client`.

type Method

type Method string

API HTTP method. Can be GET, POST or DELETE.

const (
	GET    Method = "GET"
	POST   Method = "POST"
	DELETE Method = "DELETE"
	PUT    Method = "PUT"
)

Facebook graph api methods.

type PagingResult

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

Represents facebook API call result with paging information.

func (*PagingResult) Data

func (pr *PagingResult) Data() []Result

Get current data.

func (*PagingResult) Decode added in v1.2.0

func (pr *PagingResult) Decode(v interface{}) (err error)

Decodes the current full result to a struct. See Result#Decode.

func (*PagingResult) HasNext

func (pr *PagingResult) HasNext() bool

Check whether there is next page.

func (*PagingResult) HasPrevious

func (pr *PagingResult) HasPrevious() bool

Check whether there is previous page.

func (*PagingResult) Next

func (pr *PagingResult) Next() (noMore bool, err error)

Read next page.

func (*PagingResult) Previous

func (pr *PagingResult) Previous() (noMore bool, err error)

Read previous page.

type Params

type Params map[string]interface{}

API params.

For general uses, just use Params as a ordinary map.

For advanced uses, use MakeParams to create Params from any struct.

func MakeParams

func MakeParams(data interface{}) (params Params)

Makes a new Params instance by given data. Data must be a struct or a map with string keys. MakeParams will change all struct field name to lower case name with underscore. e.g. "FooBar" will be changed to "foo_bar".

Returns nil if data cannot be used to make a Params instance.

func (Params) Encode

func (params Params) Encode(writer io.Writer) (mime string, err error)

Encodes params to query string. If map value is not a string, Encode uses json.Marshal() to convert value to string.

Encode will panic if Params contains values that cannot be marshalled to json string.

type Result

type Result map[string]interface{}

Facebook API call result.

func Api

func Api(path string, method Method, params Params) (Result, error)

Makes a facebook graph api call.

Method can be GET, POST, DELETE or PUT.

Params represents query strings in this call. Keys and values in params will be encoded for URL automatically. So there is no need to encode keys or values in params manually. Params can be nil.

If you want to get

https://graph.facebook.com/huandu?fields=name,username

Api should be called as following

Api("/huandu", GET, Params{"fields": "name,username"})

or in a simplified way

Get("/huandu", Params{"fields": "name,username"})

Api is a wrapper of Session.Api(). It's designed for graph api that doesn't require app id, app secret and access token. It can be called in multiple goroutines.

If app id, app secret or access token is required in graph api, caller should create a new facebook session through App instance instead.

func Batch

func Batch(batchParams Params, params ...Params) ([]Result, error)

Makes a batch facebook graph api call. Batch is designed for more advanced usage including uploading binary files.

An uploading files sample

// equivalent to following curl command (borrowed from facebook docs)
//     curl \
//         -F 'access_token=…' \
//         -F 'batch=[{"method":"POST","relative_url":"me/photos","body":"message=My cat photo","attached_files":"file1"},{"method":"POST","relative_url":"me/photos","body":"message=My dog photo","attached_files":"file2"},]' \
//         -F 'file1=@cat.gif' \
//         -F 'file2=@dog.jpg' \
//         https://graph.facebook.com
Batch(Params{
    "access_token": "the-access-token",
    "file1": File("cat.gif"),
    "file2": File("dog.jpg"),
}, Params{
    "method": "POST",
    "relative_url": "me/photos",
    "body": "message=My cat photo",
    "attached_files": "file1",
}, Params{
    "method": "POST",
    "relative_url": "me/photos",
    "body": "message=My dog photo",
    "attached_files": "file2",
})

Facebook document: https://developers.facebook.com/docs/graph-api/making-multiple-requests

func BatchApi

func BatchApi(accessToken string, params ...Params) ([]Result, error)

Makes a batch facebook graph api call.

BatchApi supports most kinds of batch calls defines in facebook batch api document, except uploading binary data. Use Batch to do so.

Note: API response is stored in "body" field of a Result.

results, _ := BatchApi(accessToken, Params{...}, Params{...})

// Use first batch api response.
var res1 *BatchResult
var err error
res1, err = results[0].Batch()

if err != nil {
    // this is not a valid batch api response.
}

// Use BatchResult#Result to get response body content as Result.
res := res1.Result

Facebook document: https://developers.facebook.com/docs/graph-api/making-multiple-requests

func Delete

func Delete(path string, params Params) (Result, error)

Delete is a short hand of Api(path, DELETE, params).

func FQL

func FQL(query string) ([]Result, error)

Makes a FQL query. Returns a slice of Result. If there is no query result, the result is nil.

FQL can only make query without "access_token". For query requiring "access_token", create Session and call its FQL method.

Facebook document: https://developers.facebook.com/docs/technical-guides/fql#query

func Get

func Get(path string, params Params) (Result, error)

Get is a short hand of Api(path, GET, params).

func MakeResult

func MakeResult(jsonBytes []byte) (Result, error)

Makes a Result from facebook Graph API response.

func MultiFQL

func MultiFQL(queries Params) (Result, error)

Makes a multi FQL query. Returns a parsed Result. The key is the multi query key, and the value is the query result.

MultiFQL can only make query without "access_token". For query requiring "access_token", create Session and call its MultiFQL method.

See Session.MultiFQL document for samples.

Facebook document: https://developers.facebook.com/docs/technical-guides/fql#multi

func Post

func Post(path string, params Params) (Result, error)

Post is a short hand of Api(path, POST, params).

func Put

func Put(path string, params Params) (Result, error)

Put is a short hand of Api(path, PUT, params).

func Request

func Request(request *http.Request) (Result, error)

Makes an arbitrary HTTP request. It expects server responses a facebook Graph API response.

request, _ := http.NewRequest("https://graph.facebook.com/538744468", "GET", nil)
res, err := Request(request)
fmt.Println(res["gender"])  // get "male"

func (Result) Batch added in v1.3.0

func (res Result) Batch() (*BatchResult, error)

Creates a BatchResult for this result. Returns error if the Result is not a batch api response.

See BatchApi document for a sample usage.

func (Result) Decode

func (res Result) Decode(v interface{}) (err error)

Decodes full result to a struct. It only decodes fields defined in the struct.

As all facebook response fields are lower case strings, Decode will convert all camel-case field names to lower case string. e.g. field name "FooBar" will be converted to "foo_bar". The side effect is that if a struct has 2 fields with only capital differences, decoder will map these fields to a same result value.

If a field is missing in the result, Decode keeps it unchanged by default.

Decode can read struct field tag value to change default behavior.

Examples:

type Foo struct {
    // "id" must exist in response. note the leading comma.
    Id string `facebook:",required"`

    // use "name" as field name in response.
    TheName string `facebook:"name"`
}

To change default behavior, set a struct tag `facebook:",required"` to fields should not be missing.

Returns error if v is not a struct or any required v field name absents in res.

func (Result) DecodeField

func (res Result) DecodeField(field string, v interface{}) error

Decodes a field of result to any type, including struct. Field name format is defined in Result.Get().

More details about decoding struct see Result.Decode().

func (Result) Err

func (res Result) Err() error

Checks if Result is a Graph API error. Returns nil if Result is not an error.

The returned error can be converted to Error by type assertion.

err := res.Err()
if err != nil {
    if e, ok := err.(*Error); ok {
        // read more details in e.Message, e.Code and e.Type
    }
}

For more information about Graph API Errors, see https://developers.facebook.com/docs/reference/api/errors/

func (Result) Get

func (res Result) Get(field string) interface{}

Gets a field.

Field can be a dot separated string. If field name is "a.b.c", it will try to return value of res["a"]["b"]["c"].

To access array items, use index value in field. For instance, field "a.0.c" means to read res["a"][0]["c"].

It doesn't work with Result which has a key contains dot. Use GetField in this case.

Returns nil if field doesn't exist.

func (Result) GetField

func (res Result) GetField(fields ...string) interface{}

Gets a field.

Arguments are treated as keys to access value in Result. If arguments are "a","b","c", it will try to return value of res["a"]["b"]["c"].

To access array items, use index value as a string. For instance, args of "a", "0", "c" means to read res["a"][0]["c"].

Returns nil if field doesn't exist.

func (Result) Paging

func (res Result) Paging(session *Session) (*PagingResult, error)

Creates a PagingResult for this Result. Returns error if the Result cannot be used for paging.

Facebook uses following JSON structure to response paging information. If "data" doesn't present in Result, Paging will return error.

{
    "data": [...],
    "paging": {
        "previous": "https://graph.facebook.com/...",
        "next": "https://graph.facebook.com/..."
    }
}

type Session

type Session struct {
	HttpClient HttpClient
	Version    string // facebook versioning.
	// contains filtered or unexported fields
}

Holds a facebook session with an access token. Session should be created by App.Session or App.SessionFromSignedRequest.

func (*Session) AccessToken

func (session *Session) AccessToken() string

Gets current access token.

func (*Session) Api

func (session *Session) Api(path string, method Method, params Params) (Result, error)

Makes a facebook graph api call.

If session access token is set, "access_token" in params will be set to the token value.

Returns facebook graph api call result. If facebook returns error in response, returns error details in res and set err.

func (*Session) App

func (session *Session) App() *App

Gets associated App.

func (*Session) AppsecretProof

func (session *Session) AppsecretProof() string

Check appsecret proof is enabled or not.

func (*Session) Batch

func (session *Session) Batch(batchParams Params, params ...Params) ([]Result, error)

Makes a batch facebook graph api call. Batch is designed for more advanced usage including uploading binary files.

If session access token is set, "access_token" in batchParams will be set to the token value.

Facebook document: https://developers.facebook.com/docs/graph-api/making-multiple-requests

func (*Session) BatchApi

func (session *Session) BatchApi(params ...Params) ([]Result, error)

Makes a batch call. Each params represent a single facebook graph api call.

BatchApi supports most kinds of batch calls defines in facebook batch api document, except uploading binary data. Use Batch to upload binary data.

If session access token is set, the token will be used in batch api call.

Returns an array of batch call result on success.

Facebook document: https://developers.facebook.com/docs/graph-api/making-multiple-requests

func (*Session) Delete

func (session *Session) Delete(path string, params Params) (Result, error)

Delete is a short hand of Api(path, DELETE, params).

func (*Session) EnableAppsecretProof

func (session *Session) EnableAppsecretProof(enabled bool) error

Enable or disable appsecret proof status. Returns error if there is no App associasted with this Session.

func (*Session) FQL

func (session *Session) FQL(query string) ([]Result, error)

Makes a FQL query. Returns a slice of Result. If there is no query result, the result is nil.

Facebook document: https://developers.facebook.com/docs/technical-guides/fql#query

func (*Session) Get

func (session *Session) Get(path string, params Params) (Result, error)

Get is a short hand of Api(path, GET, params).

func (*Session) Inspect

func (session *Session) Inspect() (result Result, err error)

Inspect Session access token. Returns JSON array containing data about the inspected token. See https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/v2.2#checktoken

func (*Session) MultiFQL

func (session *Session) MultiFQL(queries Params) (Result, error)

Makes a multi FQL query. Returns a parsed Result. The key is the multi query key, and the value is the query result.

Here is a multi-query sample.

res, _ := session.MultiFQL(Params{
    "query1": "SELECT name FROM user WHERE uid = me()",
    "query2": "SELECT uid1, uid2 FROM friend WHERE uid1 = me()",
})

// Get query results from response.
var query1, query2 []Result
res.DecodeField("query1", &query1)
res.DecodeField("query2", &query2)

Facebook document: https://developers.facebook.com/docs/technical-guides/fql#multi

func (*Session) Post

func (session *Session) Post(path string, params Params) (Result, error)

Post is a short hand of Api(path, POST, params).

func (*Session) Put

func (session *Session) Put(path string, params Params) (Result, error)

Put is a short hand of Api(path, PUT, params).

func (*Session) Request

func (session *Session) Request(request *http.Request) (Result, error)

Makes an arbitrary HTTP request. It expects server responses a facebook Graph API response.

request, _ := http.NewRequest("https://graph.facebook.com/538744468", "GET", nil)
res, err := session.Request(request)
fmt.Println(res["gender"])  // get "male"

func (*Session) SetAccessToken

func (session *Session) SetAccessToken(token string)

Sets a new access token.

func (*Session) User

func (session *Session) User() (id string, err error)

Gets current user id from access token.

Returns error if access token is not set or invalid.

It's a standard way to validate a facebook access token.

func (*Session) Validate

func (session *Session) Validate() (err error)

Validates Session access token. Returns nil if access token is valid.

Jump to

Keyboard shortcuts

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