# Clerk Go SDK
The official [Clerk](https://clerk.com) Go client library for accessing the [Clerk Backend API](https://clerk.com/docs/reference/backend-api).
[![Go Reference](https://pkg.go.dev/badge/github.com/clerk/clerk-sdk-go/v2.svg)](https://pkg.go.dev/github.com/clerk/clerk-sdk-go/v2)
[![chat on Discord](https://img.shields.io/discord/856971667393609759.svg?logo=discord)](https://discord.com/invite/b5rXHjAg7A)
[![documentation](https://img.shields.io/badge/documentation-clerk-green.svg)](https://clerk.com/docs)
[![twitter](https://img.shields.io/twitter/follow/ClerkDev?style=social)](https://twitter.com/intent/follow?screen_name=ClerkDev)
### Migrating from `v1`?
Check out the [upgrade guide](UPGRADING.md#1-x-x-to-2-x-x).
## Requirements
- Go 1.19 or later.
## Installation
If you are using Go Modules and have a `go.mod` file in your project's root, you can import clerk-sdk-go directly.
```go
import (
"github.com/clerk/clerk-sdk-go/v2"
)
```
Alternatively, you can `go get` the package explicitly.
```
go get -u github.com/clerk/clerk-sdk-go/v2
```
## Usage
For details on how to use this module, see the [Go Documentation](https://pkg.go.dev/github.com/clerk/clerk-sdk-go/v2).
The library has a resource-based structure which follows the way the [Clerk Backend API](https://clerk.com/docs/reference/backend-api) resources are organized.
Each API supports specific operations, like Create or List. While operations for each resource vary, a similar pattern is applied throughout the library.
In order to start using API operations the library needs to be configured with your Clerk API secret key. Depending on your use case,
there's two ways to use the library; with or without a client.
For most use cases, the API without a client is a better choice.
On the other hand, if you need to set up multiple Clerk
API keys, using clients for API operations provides more flexibility.
Let's see both approaches in detail.
### Usage without a client
If you only use one API key, you can import the packages required for the APIs you need to interact with and call functions for API operations.
```go
import (
"github.com/clerk/clerk-sdk-go/v2"
"github.com/clerk/clerk-sdk-go/v2/$resource$"
)
// Each operation requires a context.Context as the first argument.
ctx := context.Background()
// Set the API key
clerk.SetKey("sk_live_XXX")
// Create
resource, err := $resource$.Create(ctx, &$resource$.CreateParams{})
// Get
resource, err := $resource$.Get(ctx, id)
// Update
resource, err := $resource$.Update(ctx, id, &$resource$.UpdateParams{})
// Delete
resource, err := $resource$.Delete(ctx, id)
// List
list, err := $resource$.List(ctx, &$resource$.ListParams{})
for _, resource := range list.$Resource$s {
// do something with the resource
}
```
### Usage with a client
If you're dealing with multiple keys, it is recommended to use a client based approach. The API packages for each
resource export a Client, which supports all the API's operations.
This way you can create as many clients as needed, each with their own API key.
```go
import (
"github.com/clerk/clerk-sdk-go/v2"
"github.com/clerk/clerk-sdk-go/v2/$resource$"
)
// Each operation requires a context.Context as the first argument.
ctx := context.Background()
// Initialize a client with an API key
config := &clerk.ClientConfig{}
config.Key = "sk_live_XXX"
client := $resource$.NewClient(config)
// Create
resource, err := client.Create(ctx, &$resource$.CreateParams{})
// Get
resource, err := client.Get(ctx, id)
// Update
resource, err := client.Update(ctx, id, &$resource$.UpdateParams{})
// Delete
resource, err := client.Delete(ctx, id)
// List
list, err := client.List(ctx, &$resource$.ListParams{})
for _, resource := range list.$Resource$s {
// do something with the resource
}
```
Here's an example of how the above operations would look like for specific APIs.
```go
import (
"github.com/clerk/clerk-sdk-go/v2"
"github.com/clerk/clerk-sdk-go/v2/organization"
"github.com/clerk/clerk-sdk-go/v2/organizationmembership"
"github.com/clerk/clerk-sdk-go/v2/user"
)
func main() {
// Each operation requires a context.Context as the first argument.
ctx := context.Background()
// Set the API key
clerk.SetKey("sk_live_XXX")
// Create an organization
org, err := organization.Create(ctx, &organization.CreateParams{
Name: clerk.String("Clerk Inc"),
})
// Update the organization
org, err = organization.Update(ctx, org.ID, &organization.UpdateParams{
Slug: clerk.String("clerk"),
})
// List organization memberships
listParams := organizationmembership.ListParams{}
listParams.Limit = clerk.Int64(10)
memberships, err := organizationmembership.List(ctx, params)
if memberships.TotalCount < 0 {
return
}
membership := memberships[0]
// Get a user
usr, err := user.Get(ctx, membership.UserID)
}
```
### Accessing API responses
Each resource that is returned by an API operation has a `Response` field which
contains information about the response that was sent from the Clerk Backend API.
The `Response` contains fields like the the raw HTTP response's headers,
the status and the raw response body. See the [APIResponse](https://pkg.go.dev/github.com/clerk/clerk-sdk-go/v2#APIResponse)
documentation for available fields and methods.
```go
dmn, err := domain.Create(context.Background(), &domain.CreateParams{})
if !dmn.Response.Success() {
dmn.Response.TraceID
}
```
### Errors
For cases where an API operation returns an error, the library will try to return an `APIErrorResponse`.
The `APIErrorResponse` type provides information such as the HTTP status code of the response, a list of errors
and a trace ID that can be used for debugging.
The `APIErrorResponse` is an `APIResource`. You can [access the API response](#accessing-api-responses) for errors as well.
See the [APIErrorResponse](https://pkg.go.dev/github.com/clerk/clerk-sdk-go/v2#APIErrorResponse) documentation for available fields and methods.
```go
_, err := user.List(context.Background(), &user.ListParams{})
if apiErr, ok := err.(*clerk.APIErrorResponse); ok {
apiErr.TraceID
apiErr.Error()
apiErr.Response.RawJSON
}
```
### HTTP Middleware
The library provides two functions that can be used for adding authentication with Clerk to HTTP handlers.
Both middleware functions support header based authentication with a bearer token. The token is parsed, verified and
its claims are extracted as [SessionClaims](https://pkg.go.dev/github.com/clerk/clerk-sdk-go/v2#SessionClaims).
The claims will then be made available in the `http.Request.Context` for the next handler in the chain. The library
provides a helper for accessing the claims from the context, [SessionClaimsFromContext](https://pkg.go.dev/github.com/clerk/clerk-sdk-go/v2#SessionClaimsFromContext).
The two middleware functions are [WithHeaderAuthorization](https://pkg.go.dev/github.com/clerk/clerk-sdk-go/v2/http#WithHeaderAuthorization)
and [RequireHeaderAuthorization](https://pkg.go.dev/github.com/clerk/clerk-sdk-go/v2/http#RequireHeaderAuthorization).
Their difference is that the `RequireHeaderAuthorization` calls `WithHeaderAuthorization` under the hood, but responds
with HTTP 403 Forbidden if it fails to detect valid session claims.
Let's see an example of how the middleware can be used.
```go
import (
"fmt"
"net/http"
"github.com/clerk/clerk-sdk-go/v2"
clerkhttp "github.com/clerk/clerk-sdk-go/v2/http"
)
func main() {
clerk.SetKey("sk_live_XXX")
mux := http.NewServeMux()
mux.HandleFunc("/", publicRoute)
protectedHandler := http.HandlerFunc(protectedRoute)
mux.Handle(
"/protected",
clerkhttp.WithHeaderAuthorization()(protectedHandler),
)
http.ListenAndServe(":3000", mux)
}
func publicRoute(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"access": "public"}`))
}
func protectedRoute(w http.ResponseWriter, r *http.Request) {
claims, ok := clerk.SessionClaimsFromContext(r.Context())
if !ok {
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte(`{"access": "unauthorized"}`))
return
}
fmt.Fprintf(w, `{"user_id": "%s"}`, claims.Subject)
}
```
Both `WithHeaderAuthorization` and `RequireHeaderAuthorization` can be
customized. They accept various options as functional arguments.
For a comprehensive list of available options check the
[AuthorizationParams](https://pkg.go.dev/github.com/clerk/clerk-sdk-go/v2/http#AuthorizationParams) documentation.
### Testing
There are various ways to mock the library in your test suite.
#### Usage without client
If you're using the library without instantiating clients for APIs, you can
set the package's `Backend` with a custom configuration.
1. Stub out the HTTP client's transport
```go
func TestWithCustomTransport(t *testing.T) {
clerk.SetBackend(clerk.NewBackend(&clerk.BackendConfig{
HTTPClient: &http.Client{
Transport: mockRoundTripper,
},
}))
}
type mockRoundTripper struct {}
// Implement the http.RoundTripper interface.
func (r *mockRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
// Construct and return the http.Response.
}
```
2. Use a httptest.Server
Similar to the custom http.Transport approach, you can use the net/http/httptest package's utilities and
provide the http.Client to the package's Backend.
```go
func TestWithMockServer(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Write the response.
}))
defer ts.Close()
clerk.SetBackend(clerk.NewBackend(&clerk.BackendConfig{
HTTPClient: ts.Client(),
URL: &ts.URL,
}))
}
```
3. Implement your own Backend
You can implement your own [Backend](https://pkg.go.dev/github.com/clerk/clerk-sdk-go/v2#Backend) and set it as the package's default Backend.
```go
func TestWithCustomBackend(t *testing.T) {
clerk.SetBackend(&customBackend{})
}
type customBackend struct {}
// Implement the Backend interface
func (b *customBackend) Call(ctx context.Context, r *clerk.APIRequest, reader clerk.ResponseReader) error {
// Construct a clerk.APIResponse and use the reader's Read method.
reader.Read(&clerk.APIResponse{})
}
```
#### Usage with client
If you're already using the library by instantiating clients for API operations,
or you need to ensure your test suite can safely run in parallel, you can simply
pass a custom http.Client to your clients.
1. Stub out the HTTP client's transport
```go
func TestWithCustomTransport(t *testing.T) {
config := &clerk.ClientConfig{}
config.HTTPClient = &http.Client{
Transport: mockRoundTripper,
}
client := user.NewClient(config)
}
type mockRoundTripper struct {}
// Implement the http.RoundTripper interface.
func (r *mockRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
// Construct and return the http.Response.
}
```
2. Use a httptest.Server
Similar to the custom http.Transport approach, you can use the net/http/httptest package's utilities and
provide the http.Client to the API client.
```go
func TestWithMockServer(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Write the response.
}))
defer ts.Close()
config := &clerk.ClientConfig{}
config.HTTPClient = ts.Client()
config.URL = &ts.URL
client := user.NewClient(config)
}
```
3. Implement your own Backend
You can implement your own [Backend](https://pkg.go.dev/github.com/clerk/clerk-sdk-go/v2#Backend) and set it as the API client's Backend.
```go
func TestWithCustomBackend(t *testing.T) {
client := user.NewClient(&clerk.ClientConfig{})
client.Backend = &customBackend{}
}
type customBackend struct {}
// Implement the Backend interface
func (b *customBackend) Call(ctx context.Context, r *clerk.APIRequest, reader *clerk.ResponseReader) error {
// Construct a clerk.APIResponse and use the reader's Read method.
reader.Read(&clerk.APIResponse{})
}
```
## Development
Contributions are welcome. If you submit a pull request please keep in mind that
1. Code must be `go fmt` compliant.
2. All packages, types and functions should be documented.
3. Ensure that `go test ./...` succeeds. Ideally, your pull request should include tests.
4. If your pull request introduces a new API or API operation, run `go generate ./...` to generate the necessary API functions.
## License
This SDK is licensed under the MIT license found in the [LICENSE](./LICENSE) file.