pocketbase
                                
                                 pocketbase copied to clipboard
                                
                                    pocketbase copied to clipboard
                            
                            
                            
                        Golang API SDK
I see that there is Dart and JS sdk but I was wondering if a golang api / client exists in the main repo ?
i use gioui , which is like Flutter but is 100% golang and so would like to explore using gioui with Pocketbase.
If you google for “gioui golang” you will see many examples of gioui apps in GitHub etc
Currently there is no official Go SDK for the PocketBase APIs.
I don't plan to work on a Go SDK because I'm not sure if it will be worth investing the time considering that Go is rarely used for UI/client-side development and for those rare cases a small custom wrapper around the Records and the Users auth REST apis could be enough for most users.
Community contributions are more than welcomed (the Dart SDK could be a good starting point to get a general idea of the available services and the expected DX).
p.s. If someone wants to work on this, please keep in mind that there will be some breaking changes related to the users service in #376.
Thanks for the response.
it’s likely not too hard to implement by sharing golang code between the client and server .
I had a brief look at the golang code but would you be able to point me to the models and the server api .
also do you use websockets for push ?
The db models/dtos could be found in https://github.com/pocketbase/pocketbase/tree/master/models.
All web apis handling (including the realtime) is located in https://github.com/pocketbase/pocketbase/tree/master/apis.
Server-sent events (SSE) are used for the realtime subscriptions in a simple fan-out pattern. The code could be found in https://github.com/pocketbase/pocketbase/blob/master/apis/realtime.go#L288. All active subscribed clients are stored in the memory as part of the app instance app.subscriptionsBroker.
I'm not sure if you can reuse any of the existing core code, but you can use at least the models for a reference.
Usually the SDK shouldn't be concerned with the inner implementation details and it is expected to communicate with the backend primarily through the web APIs (aka. sending and receiving http requests). I would personally use the standard Go http.Client for that and map each API service following the Web API documentation in a similar manner as in the Dart SDK.
thanks @ganigeorgiev
everything you said makes perfect sense.
I will have a look and update you as i go.
I'm also interested in a Golang SDK!
Hey @gaby thats 2 of us at least .thanks fo speaking up.
if you want we can work on it together . I have not started yet but think it’s not a giant API to support . It’s nice that it uses SSE and not websockets.
Are you also wanting to use it with gioui? For WASM compilation we can use native SSE built into the browser. For desktop / mobile there are a few SSE golang libs out there .
looking forward to discussing with you.
Also I forgot to ask @ganigeorgiev if he would be ok for the golang API to be part of this GitHub org ?
the golang code can be written such that it provides a CLI, a CRL ( cli operating over a network , and a TUI and GUI. It’s easy to do all 4 because of the architecture you have designed with model and transport decoupled.
I have a few other ideas to allow it to scale out too using a message broker like NATS between all these clients and the server. It’s almost perfect application of NATS because of the real time event based nature of the architecture. SSE can stilll be used - we just put a SSE proxy on top of NATS. Anyway that’s a bigger discussion we can have if you want ?
For desktop / mobile there are a few SSE golang libs out there.
If you can't find a good SSE client, you can always parse the sse message manually. Basic implementation of such rudimentary client could be found in the Dart SDK - https://github.com/pocketbase/dart-sdk/blob/master/lib/src/sse/sse_client.dart
Also I forgot to ask ganigeorgiev if he would be ok for the golang API to be part of this GitHub org ?
I don't want to add non-official packages in the pocketbase/* org namespace because it comes with the expectations that they will be "officially" supported and I don't think I'll have the time for that. In any case, the package org, outside of the import paths, doesn't really matter that much, since we can always add links and references in the README to community-maintained packages for visibility.
About the scaling and NATS - as long as it doesn't complicate too much the internals and tests, I'll accept contributions to the main repo. But keep in mind that PocketBase was created because the other similar platforms felt too complex and overwhelming to install, setup and maintain and I want to keep it simple and portable as much as possible.
I agree with you about making things complex being an anti pattern . I loathe it also.
it’s probably better to discuss scaling / ha stuff in a issue or discussion. Probably best if you raise it explaining your general approach if any
SSE lib: https://github.com/r3labs/sse
Hi! I'm planning on creating a golang application that makes heavy use of Pocketbase, I've been working on creating an SDK, but I'd be more than happy to work on something that's opensource/semi-official. Is there a repo to which I can contribute?
@fuster92 Unfortunately i have not started it yet. I would be happy to work with you and others on this. @gaby You might also be interested ?
I dont think its a large amount of work as we can reuse some of the code in the pocket base repo.
Nice! I'll try to polish a bit what I have, which is only the admins api and a basic client, and we can work from that, sound good to you @gedw99 ?
I’m also interested in a golang SDK. I intend to write an app composed of microservices which talk to pocketbase as the datastore and auth provider. Might be interested in contributing if there is critical mass.
@fuster92 awesome 👍
Looks like we have a few interested !!!
If you have anything let me know repo. I have a fair bit of time right now to work on it
@clarkezone
I’m also interested in a golang SDK. I intend to write an app composed of microservices which talk to pocketbase as the datastore and auth provider. Might be interested in contributing if there is critical mass.
Sounds like a great demo to try out the sdk.
Hopefully @fuster92 cones back to me with what he has so far and we can all iterate on it
When someone has a repo started, please let me know and I'll add it to awesome-pocketbase 👍
I've created a super minimal client that uses email/password authentication. It's resilient (has built-in retries), super simple, and supports all CRUD operations. I'll also provide one for the 0.8 release. Let me know if you want this to be available as a module.
Usage:
package main
import (
	"encoding/json"
	"log"
	"github.com/coinpaprika/news/pkg/pocketbase"
)
type (
	response struct {
		Page       int `json:"page"`
		PerPage    int `json:"perPage"`
		TotalItems int `json:"totalItems"`
		TotalPages int `json:"totalPages"`
		Items      []struct {
			CollectionID   string   `json:"@collectionId"`
			CollectionName string   `json:"@collectionName"`
			Content        string   `json:"content"`
			Created        string   `json:"created"`
			Description    string   `json:"description"`
			ID             string   `json:"id"`
			TickerIds      []string `json:"ticker_ids"`
			Title          string   `json:"title"`
			Updated        string   `json:"updated"`
			URL            string   `json:"url"`
		} `json:"items"`
	}
)
func main() {
	client := pocketbase.NewClient("http://your.host", "email@host", "password")
	// Documentation for the params can be found here: https://pocketbase.io/docs/api-records/
	respBytes, err := client.List("news", pocketbase.Params{Size: 2, Filters: "title~'Bitcoin'"})
	if err != nil {
		panic(err)
	}
	var respParsed response
	if err := json.Unmarshal(respBytes, &respParsed); err != nil {
		panic(err)
	}
	log.Print(respParsed)
	if err := client.Update("news", respParsed.Items[0].ID, map[string]interface{}{"title": "New title"}); err != nil {
		panic(err)
	}
}
Tested and it works (auth and create).. ripped out my hacked version and replaced with it, thx!
Thanks @r--w this is a great start. I am keen to play with this too
Do you want to host it under the pocketbase org ? Not sure if @gaby wants to or not - might depend on getting a maintainer perhaps.
@gedw99 Slightly offtopic but to clear an eventual misunderstanding - as mentioned in https://github.com/pocketbase/pocketbase/issues/378#issuecomment-1239017594, the pocketbase/* namespace is reserved for officially maintained packages only. Community packages are free to reside whereever the author decide and eventually sometime in the future when PocketBase API is stable enough we may add links to community SDKs in the readme and in the website docs.
@gedw99 Slightly offtopic but to clear an eventual misunderstanding - as mentioned in #378 (comment), the
pocketbase/*is reserved for official maintained packages only. Community packages are free to reside whereever the author decide and eventually sometime in the future when PocketBase API is stable enough we may add links to community SDKs in the readme and in the website docs.
Good to know .
Hey @clarkezone
like your k8 on top of Pocketbase golang demo. The git hooks system with longhorn volumes is a clever approach. Will have a play.
I am working on similar setup with using wasm / nats , to replace k8. The WASM replaces the docker / layer. The NATS mesh replaces the k8 / istio mesh.
so you write your golang code to do a todo app example, The git hooks fire, the golang is compiled to wasm by the engine , and deployed.
I don’t need to mount a volume for SQLite, but instead just use local fs . The SQLite is continuously replicated using marmot / nats and continuously backup up to s3.
Thanks @r--w this is a great start. I am keen to play with this too
Do you want to host it under the pocketbase org ? Not sure if @gaby wants to or not - might depend on getting a maintainer perhaps.
I'm not part of pocketbase. I do find it very helpful to have an api for it. One of the reasons I was looking for one was to add it as a Storage Driver for GoFiber in https://github.com/gofiber/storage
It would be up to @r--w where to host the module. Do you have interest in maintaining it, or rather have a group of people do it in a shared repo outside the pocketbase org?
Ok, thanks for the feedback. I'll build a nice module in the following weeks with the support of v.0.8.0 and will try to find the best way to make it more extensible for different auth methods and unify access to the returning objects.
IMO this module should be developed and listed as a community-supported one, and who knows, after reaching parity with JS/Dart in the future, it might be transferred to the PocketBase organization - no problem with that.
Currently, my goal is to create a valuable lib for my needs, and I am happy to merge other PRs, but I can't guarantee that I'll keep the feature parity with official libraries.
You can assign this ticket to me.
Ok, thanks for the feedback. I'll build a nice module in the following weeks with the support of v.0.8.0 and will try to find the best way to make it more extensible for different auth methods and unify access to the returning objects.
IMO this module should be developed and listed as a community-supported one, and who knows, after reaching parity with JS/Dart in the future, it might be transferred to the PocketBase organization - no problem with that.
Currently, my goal is to create a valuable lib for my needs, and I am happy to merge other PRs, but I can't guarantee that I'll keep the feature parity with official libraries.
You can assign this ticket to me.
thanks @r--w
great news - as soon as you have a repo up, will help...
Same!
Pocketbase don't have golang api, only rest api now
Web layer mixed with db layer. For example create record depended on context (and not public):
func (api *recordApi) create(c echo.Context) error {
	collection, _ := c.Get(ContextCollectionKey).(*models.Collection)
	if collection == nil {
		return NewNotFoundError("", "Missing collection context.")
	}
	admin, _ := c.Get(ContextAdminKey).(*models.Admin)
	if admin == nil && collection.CreateRule == nil {
		// only admins can access if the rule is nil
		return NewForbiddenError("Only admins can perform this action.", nil)
	}
        ....
If separate rest api from api, we may add Create method for record:
func CreateRecord(app core.App, collectionNameOrId string, requestData map[string]any) (*models.Record, error) {
	collection, err := app.Dao().FindCollectionByNameOrId(collectionNameOrId)
	if err != nil {
		return nil, err
	}
	record := models.NewRecord(collection)
	form := forms.NewRecordUpsert(app, record)
	if err := form.LoadData(requestData); err != nil {
		return nil, err
	}
	// create the record
	if err := form.Submit(); err != nil {
		return nil, err
	}
	return record, err
}
And use it from app like this:
	// usr_feed
	requestData := map[string]any{}
	requestData["user_id"] = userId
	requestData["feed_id"] = feed.GetId()
	_, err = CreateRecord(app, "usr_feed", requestData)
Another example - list method:
func RecordsList(app core.App, collectionNameOrId, queryString, expand string) (*search.Result, error) {
	collection, err := app.Dao().FindCollectionByNameOrId(collectionNameOrId)
	if err != nil {
		return nil, err
	}
	requestData := map[string]any{}
	fieldsResolver := resolvers.NewRecordFieldResolver(
		app.Dao(),
		collection,
		requestData,
		// hidden fields are searchable only by admins
		false,
	)
	searchProvider := search.NewProvider(fieldsResolver).
		Query(app.Dao().RecordQuery(collection))
	var rawRecords = []dbx.NullStringMap{}
	var result *search.Result
	result, err = searchProvider.ParseAndExec(queryString, &rawRecords)
	if err != nil {
		return nil, err
	}
	records := models.NewRecordsFromNullStringMaps(collection, rawRecords)
	// expand records relations
	expands := strings.Split(expand, ",")
	if len(expands) > 0 {
		failed := app.Dao().ExpandRecords(
			records,
			expands,
			expandFetch(app.Dao(), false, requestData),
		)
		if len(failed) > 0 && app.IsDebug() {
			log.Println("Failed to expand relations: ", failed)
		}
	}
	result.Items = records
	return result, nil
}
List usage:
func UsrFeeds(app core.App, userId string) (*search.Result, error) {
	return RecordsList(app, "usr_feed", fmt.Sprintf("filter=(user_id='%s')", userId), "feed_id,feed_id.domain_id")
}
...
		e.Router.AddRoute(echo.Route{
			Method: http.MethodGet,
			Path:   "/feed/:id/:name",
			Handler: func(c echo.Context) error {
				usrFeeds(c, app)
				posts(c, app)
				return c.Render(http.StatusOK, "main.html", siteData(c))
			},
			Middlewares: []echo.MiddlewareFunc{
				apis.RequireAdminOrRecordAuth(),
			},
		})
And use from pocketbase. Split web layer and api. Make api public. But it's big code refactoring..
What do you think about this? @ganigeorgiev
@recoilme I'm not understanding the proposal. This issue is for a client SDK, aka. when not using PocketBase as framework.
In any case I'm moving it to a discussion since I'm not planning to work on a Go SDK. As mentioned in the previous comments, community contributions are welcomed.