Giter Club home page Giter Club logo

postgrest-go's Introduction

Postgrest GO

golangci-lint CodeFactor

Golang client for PostgREST. The goal of this library is to make an "ORM-like" restful interface.

Documentation

Full documentation can be found here.

Quick start

Install

go get github.com/supabase-community/postgrest-go

Usage

package main

import (
	"fmt"

	"github.com/supabase-community/postgrest-go"
)

func main() {
	client := postgrest.NewClient("http://localhost:3000/rest/v1", "", nil)
	if client.ClientError != nil {
		panic(client.ClientError)
	}

	result := client.Rpc("add_them", "", map[string]int{"a": 12, "b": 3})
	if client.ClientError != nil {
		panic(client.ClientError)
	}

	fmt.Println(result)
}

Testing

Some tests are implemented to run against mocked Postgrest endpoints. Optionally, tests can be run against an actual Postgrest instance by setting a POSTGREST_URL environment variable to the fully-qualified URL to a Postgrest instance, and, optionally, an API_KEY environment variable (if, for example, testing against a local Supabase instance).

A script is included in the test directory that can be used to seed the test database.

To run all tests:

go test ./...

License

This repo is licensed under the Apache License.

Sponsors

We are building the features of Firebase using enterprise-grade, open source products. We support existing communities wherever possible, and if the products don’t exist we build them and open source them ourselves. Thanks to these sponsors who are making the OSS ecosystem better for everyone.

New Sponsor

postgrest-go's People

Contributors

aaronschweig avatar ahmetcanaydemir avatar boatilus avatar darora avatar fritte795 avatar icepuma avatar intabulas avatar marcustut avatar muratmirgun avatar tranhoangvuit avatar whoiscarlo avatar yusufpapurcu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

postgrest-go's Issues

RPC no Route matched

Bug report

Describe the bug

setup_workspace type generated on client looks like this

   setup_workspace: {
        Args: {
          refresh_token: string
          user_email: string
          name: string
          is_admin: string
          workspace_name: string
        }

In go, when I am calling it


   	supabaseReqData := supabase.SetupWorkspaceRequest{
   		Name:    userData.Name,
   		UserEmail:   userData.Email,
   		RefreshToken: request.RefreshToken,
   		IsAdmin: userData.IsAdmin,
   		WorkspaceName: userData.Hd,
   	}
   	payload, err := json.Marshal(supabaseReqData)
   	if err != nil {
   		fmt.Println("Error encoding JSON:", err)
   		return
   	}

   	
   	result := db.Rpc("setup_workspace", "", string(payload))

it is throwing the following error: "message":"No API key found in request"

Here supabase.SetupWorkspaceRequest

type SetupWorkspaceRequest struct {
    RefreshToken  string `json:"refresh_token"`
    UserEmail     string `json:"user_email"`
    Name          string `json:"name"`
    IsAdmin       bool   `json:"is_admin"`
    WorkspaceName string `json:"workspace_name"`
}

And db is db *postgrest.Client

I am initializing like this

	supbaseDb := postgrest.NewClient(url,serviceRoleKey, nil)
	if supbaseDb.ClientError != nil {
		panic(supbaseDb.ClientError)
	}
	app.Db = supbaseDb

where url is this http://localhost:54321 and serviceRoleKey is what we get when we do supabase start (service_role key)

In NodeJs, this code would've like this

	supbaseDb := postgrest.NewClient(url+"/rest/v1", "", map[string]string{
		"service_role_key": serviceRoleKey,
	})

Allow custom Transport Configuration

Feature request

Is your feature request related to a problem? Please describe.

Typically the default implementation of Transport http.DefaultTransport does not accept self signed certificates. In some environments PKIs are used with Root- or Intermediate-CAs not signed by an official CA. Therefore it might be necessary to customize the transport used by a client.

With the current implementation this is not possible as the custom implemented RoundTrip always calls http.DefaultTransport.RoundTrip().

Describe the solution you'd like

It should be possibile to define the wrapping Transport of the custom transport in client.go. An usage could look like this:

func main() {
	c := postgrest.NewClient("", "", nil)

	c.ClientTransport.Parent = &http.Transport{
		TLSClientConfig: &tls.Config{
			// Custom tls config
		},
	}
}

Few things need to be changed for this:

  1. The Client.clientTransport needs to be exposed
  2. I would recommend to use a pointer instead of the struct value to guarantee a shared/synced transport between Client.session.Transport and Client.clientTransport
    a. Client.clientTransport is stuttering. Could it not be Client.transport or exposed Client.Transport?
  3. Add a "base" Parent to transport
    a. basic http.RoundTripper that will be called in RoundTrip instead of http.DefaultTransport.RoundTrip()
    b. Initialize transport.Parent with http.DefaultTransport to keep old behavior.

A pull request will be created after this issue.

❓ Can't get it to work.

Synopsis

I can't get this package to function against supabase.co

Details

Whether I use the Anon key or the Service key, I can't select anything.

  • With head set to false, I get []
  • With head set to true, I get ""

Reproduction

func TestSelect(t *testing.T) {
	db := postgrest.NewClient(
		"https://<project-ref>.supabase.co/rest/v1/",
		"public",
		map[string]string{},
	)
	db.TokenAuth("...")

	res, count, err := db.
		From("profiles").
		Select("*", "", false). // also tried specifying columns
		ExecuteString()

	spew.Dump(res, count, err)

	if err != nil {
		t.Fatalf("error returned: %v", err)
	}

	if res == "[]" || res == "" {
		t.Fatalf("no result")
	}
}
=== RUN   TestSelect
(string) (len=2) "[]"
(int64) 0
(interface {}) <nil>
    select_test.go:30: no result
--- FAIL: TestSelect (0.29s)

From() will always append the table name to the baseUrl Path

Bug report

Describe the bug

When using From(), the code will currently forcibly (+=) add the table name to the client transport baseUrl path. This makes it impossible to reuse a client instance (postgrest.NewClient(<url>L, <schema>, <headers>)) as the second time From() is used the schema becomes <schema>.<table><table>

To Reproduce

Steps to reproduce the behavior, please provide code snippets or a repository:

var (
	url = "https://<some-supabase-url>/rest/v1/"
	headers  = map[string]string{"apikey": "<my-supabase-apikey>"}
)

client := postgrest.NewClient(url, "public", headers)

// .. build some list
for _, doc := range listofdocs {
    // Second time this is called, there will be an error because the schema is now wrong
	resp, err := client.From("<mytable>").Insert(doc, "", "", "").Execute()
	if err != nil {
		// handle error
	}
}

Expected behavior

Expected behavior is that if calling multiple times (vs batch insert) that From() wont cause the insert to fail. The JS client (for example) does not exhibit this issue

Screenshots

N/A

Additional context

I am working on a solution to this that I will submit a PR for

New Execute Method for json objects

I think we need new execution method for json object. As an example if client wants inserted data's id, user must unmarshall result and took id from object.

How can I do?

You can create new execution method in execute.go. You must create function executeTo. And take byte array and unmarshall it to interface. After that you must add this method to all structs.

You are welcome to contribute πŸ’―
We are excited to see your PR's πŸš€

Change authorization header

After looking a bit at the supabase documentation, I saw that the authentication is not Basic but Bearer (which means in return that one should have to change this in the client.go file of postgrest.)
I propose two solution:

  • whether to amend it on the static string of the TokenAuth (I just did it myself line 59 of client.go). then is will go to
func (c *Client) TokenAuth(token string) *Client {
	c.clientTransport.header.Set("Authorization", "Bearer "+token)
        ...
}
\\ instead of 
func (c *Client) TokenAuth(token string) *Client {
	c.clientTransport.header.Set("Authorization", "Basic "+token)
        ...
}
  • or to amend it dynamically (I do not really now how PostgreSQL works, so if there are different type of authorization I could envisage the following)
func (c *Client) TokenAuth(token string, auth_type string) *Client {
	c.clientTransport.header.Set("Authorization", auth_type+" "+token)
        ...
} 
\\ instead of 
func (c *Client) TokenAuth(token string) *Client {
	c.clientTransport.header.Set("Authorization", "Basic "+token)
        ...
}```

IMPORTANT: Need to pass BOTH apikey and Authorization headers!

Just wasted approx 5 hours when I realized you need to pass both an apikey AND Authorization key when initializing the client:

client := postgrest.NewClient(os.Getenv("SUPABASE_REST_URL"), "public", map[string]string{"apikey": os.Getenv("SUPABASE_SERVICE_KEY"), "Authorization": "Bearer " + os.Getenv("SUPABASE_SERVICE_KEY")})

IMO opinion this should anyway become a single field call 'key' or something, and then within the library itself it is applied to

Hoping this helps anyone who probably otherwise assumed (like I did) that this library was broken!

Please update the README to reflect this information!

Supabase URL needs `/rest/v1` appended

In the README, the docs say to put in localhost:3000, but if calling supabase itself, if you put in the API URL as it is in Supabase, you get a () no Route matched with those values error.

I had to update my URL to https://<database id>.supabase.co/rest/v1 and it worked.

Could we update the docs? Or is there a fix under the hood we'd want to make? I'm up to make either, but wanted to run it by you all first!

Select from table fails

Bug report

Describe the bug

Following the test example and the supabase-js example in the documentation I am not able to fetch data using the From().Select() chain and an error is returned in the response body instead of the err value itself.

The json response looks like:

{"message":"no Route matched with those values"}

To Reproduce

        type User struct {
		id string
	}
        
        var (
	        headers = map[string]string{"Authorization": fmt.Sprintf("Bearer %s", serverconf.Supabase.Key)}
	        schema  = "public"
        )

        var serverconf := utils.NewServerConfig()

        supabaseClient := postgrest.NewClient(serverconf.Supabase.URL, schema, headers)
		
        res, err := r.client.From("users").Select("*", "", false).Eq("email", email).Execute()

	if err := json.Unmarshal(res, &result); err != nil {
		panic(err)
	}

Expected behavior

Expected to have a valid user response and no error. Also expected error to be returned in err variable but instead it's nil.

Provide examples for authentication etc.

Hey!
I love the idea behind this package, but have some troubles with getting started, as I am new to Supabase and PostgREST.

I would love to see some more examples on the usage of this package (with supabase), namely authentication.

  • (How) should I use the client.TokenAuth method?
  • Can I encode the credentials into the HTTP URI somehow?

For anything I tried, I get some not-so-descriptive error (like just EOF from net/url) which is very likely because I did something wrong.

All the examples I can find are just for a local setup without auth.
Maybe adding a few more tests would be ideal for this? I would love to contribute once I used this a bit more.

I would greatly appreciate any help :)

Define and Creating test cases with explanatory comments

We must define test cases first. I think we can look other supabase postgREST libraries for this.

Bellow I will add cases and addition status with tasklist style so this issue will more useful in tasklist.

TEST DEFINITIONS

  • Basic from/select without auth based here
  • Basic insert/upsert/update/delete based here

X-Client-Info header

we're rolling out a header similar to Stripe's App-Data in all of the client libs so issues can be debugged more easily between the client and the backend

the javascript libs are done already: supabase/supabase-js#238

the format is: X-Client-Info: supabase-js/1.11.0

for client libs that wrap others (like how supabase-js wraps gotrue-js) we allow the wrapper lib to overwrite the wrapped lib's header (given that we can infer the gotrue-js version based on which supabase-js was used to make the call)

any help with rolling this out here would be incredible

Allow for setting Authorization and apiKey headers independently

Feature request

Currently the TokenAuth method of the posgrest.Client struct sets both the "Authorization" header (with the token as the "Bearer") and the apikey header.

This means, for example, that it is not possible after the client is created to customize those headers independently.

Is your feature request related to a problem? Please describe.

I am trying to leverage RLS (Row Level Security) while forwarding authenticated requests to supabase from the client through a Golang server. I set an access_token gained from the JS library as a cookie. I then use this cookie to update the Authorization header so that the correct RLS policies are applied to the user request.

Based on the Auth policies documentation, the expected headers for RLS should be:

curl 'https://sjvwsaokcugktsdaxxze.supabase.co/rest/v1/my_scores?select=*' \
-H "apikey: <ANON_KEY>" \
-H "Authorization: Bearer <ACCESS_TOKEN>"

As seen in the above documentation, the "apiKey" header ought to be set to the ANON_KEY while the "Authorization" header ought to be set to the users access_token.

Describe the solution you'd like

It ought to be possible to independently set these header values for the case where we need a different Authorization header compared to the apiKey.

I would suggest having two separate functions to set the headers instead of a single function as it is now. This change could be additive to maintain backwards compatibility, e.g by adding:

// AuthBearer sets the authorization header for subsequent requests.
func (c *Client) AuthBearer(token string) *Client {
	c.clientTransport.header.Set("Authorization", "Bearer "+token)
	return c
}

// ApiKey sets the apiKey header for subsequent requests.
func (c *Client) ApiKey(token string) *Client {
	c.clientTransport.header.Set("apikey", token)
	return c
}

Describe alternatives you've considered

When initially creating the client it is possible to set the headers independently, e.g.:

client := postgrest.NewClient(url+REST_URL, "public", map[string]string{
    "Authorization": "Bearer " + auth,
    "apikey":        auth,
})

However, for my use case I initialize the client with the ANON_KEY before I have decided whether or not the current API call ought to be authenticated. I would prefer to override the "Authorization" on a call-by-call basis before making the actual Execute* in cases where I know a user-specific access_token is required.

It might also be possible to expose a generic function to set headers, although this might be considered too permissive and may lead to users accidentally setting incorrect headers.

`Execute()` is not sending request to the correct URL

Bug report

Describe the bug

So when I try to insert data with the following code snippet, I expect it to send a request to https://<supabase_url>/rest/v1/products but it seems like the Execute() function is not sending the request to the correct URL.
image

The result of trying to run this is that I get the response as below, after a google search it seems like this is what Kong would respond if it can't find a route

image

So I try to directly change the client.clientTransport.baseURL.Path to https://<supabase_url>/rest/v1/products, the request is sent to the correct place where I can see a postgrest response but the query is still not successfully created yet. I'm suspecting is that the way the querybuilder marshal json is making some errors here.

image

To Reproduce

Steps to reproduce the behavior, please provide code snippets or a repository:

  1. Get the library
  2. Use a supabase url, anon key and proper auth token to create the client
  3. Try to insert data into a table
  4. See error

Expected behavior

The request should be sent to the correct url and querybuilder should properly marshal value into json

Screenshots

Shown above

System information

  • OS: macOS Big Sur 11.4
  • Version of Go: 1.16

Additional context

I will try fixing it and if I do, I will create a PR.

Update doesn't work

Hello, i use this library in order to perform operations to my Supabase database.
I have connected to db with the client:

var (
	headers = map[string]string{"Authorization": fmt.Sprintf("Bearer %s", tokenApi), "apikey": tokenApi}
)

client := postgrest.NewClient("https://<projectID>.supabase.co/rest/v1/", "public", headers)

And I update the table like this:

info, count, err := client.From("testTable").Update(map[string]string{"state": "offline"}, "", "").Match(map[string]string{"member": "comid"}).ExecuteString()
fmt.Println(info) ->  [{"id":1,"member":"comid","avatar":"asdasd","state":"offline"}] 
fmt.Println(count)
fmt.Println(err)

I'm not getting any error and it returns the response in string, but the column "state" in db doesn't update to "offline".

Deprecation of io/ioutil

Chore

Describe the chore

Hello πŸ‘‹πŸΎ β€” I just started using supabase and was looking for a go client for postgrest. Nice work on this!

I have a small refactor chore as it looks like the postgrest-go client uses go version 1.16 according to the go.mod. That being said, io/ioutil has been deprecated. Although the package remains and continues to work as we know, the go team encourages the use of new definitions found in io and os packages.

I have a small PR with the encouraged usage and will link to this issue for reference πŸ˜„

Additional context

More details can be read here: https://golang.org/doc/go1.16#ioutil

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.