Documentation
¶
Overview ¶
Package tests holds examples and integration tests for all of Graphjin including the standalone service `serv` github.com/dosco/serv/v3 and the core compiler `core` github.com/dosco/core/v3
Example (Insert) ¶
gql := `mutation {
users(insert: {
id: $id,
email: $email,
full_name: $fullName,
stripe_id: $stripeID,
category_counts: $categoryCounts
}) {
id
email
}
}`
vars := json.RawMessage(`{
"id": 1001,
"email": "user1001@test.com",
"fullName": "User 1001",
"stripeID": "payment_id_1001",
"categoryCounts": [{"category_id": 1, "count": 400},{"category_id": 2, "count": 600}]
}`)
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true, Debug: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
ctx := context.WithValue(context.Background(), core.UserIDKey, 3)
res, err := gj.GraphQL(ctx, gql, vars, nil)
if err != nil {
fmt.Println(err)
fmt.Println("SQL:", res.SQL())
return
}
printJSON(res.Data)
Output: {"users":[{"email":"user1001@test.com","id":1001}]}
Example (InsertBulk) ¶
gql := `mutation {
users(insert: $data) {
id
email
}
}`
vars := json.RawMessage(`{
"data": [{
"id": 1002,
"email": "user1002@test.com",
"full_name": "User 1002",
"stripe_id": "payment_id_1002",
"category_counts": [{"category_id": 1, "count": 400},{"category_id": 2, "count": 600}]
},
{
"id": 1003,
"email": "user1003@test.com",
"full_name": "User 1003",
"stripe_id": "payment_id_1003",
"category_counts": [{"category_id": 2, "count": 400},{"category_id": 3, "count": 600}]
}]
}`)
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
ctx := context.WithValue(context.Background(), core.UserIDKey, 3)
res, err := gj.GraphQL(ctx, gql, vars, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"users":[{"email":"user1002@test.com","id":1002},{"email":"user1003@test.com","id":1003}]}
Example (InsertInlineBulk) ¶
gql := `mutation {
users(insert: [
{id: $id1, email: $email1, full_name: $full_name1},
{id: $id2, email: $email2, full_name: $full_name2}], order_by: {id: desc}) {
id
email
}
}`
vars := json.RawMessage(`{
"id1": 1008,
"email1": "one@test.com",
"full_name1": "John One",
"id2": 1009,
"email2": "two@test.com",
"full_name2": "John Two"
}`)
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
ctx := context.WithValue(context.Background(), core.UserIDKey, 3)
res, err := gj.GraphQL(ctx, gql, vars, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"users":[{"email":"two@test.com","id":1009},{"email":"one@test.com","id":1008}]}
Example (InsertInlineWithValidation) ¶
gql := `mutation
@constraint(variable: "email", format: "email", min: 1, max: 100)
@constraint(variable: "full_name", requiredIf: { id: 1007 } )
@constraint(variable: "id", greaterThan:1006 )
@constraint(variable: "id", lessThanOrEqualsField:id ) {
users(insert: { id: $id, email: $email, full_name: $full_name }) {
id
email
full_name
}
}`
vars := json.RawMessage(`{
"id": 1007,
"email": "not_an_email"
}`)
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
ctx := context.WithValue(context.Background(), core.UserIDKey, 3)
res, err := gj.GraphQL(ctx, gql, vars, nil)
if err != nil {
fmt.Println(err)
for _, e := range res.Validation {
fmt.Println(e.Constraint, e.FieldName)
}
} else {
printJSON(res.Data)
}
// Ordered output:
// validation failed
// format email
// min email
// max email
// requiredIf full_name
Example (InsertIntoMultipleRelatedTables) ¶
gql := `mutation {
purchases(insert: $data) {
quantity
customer {
id
full_name
email
}
product {
id
name
price
}
}
}`
vars := json.RawMessage(`{
"data": {
"id": 3001,
"quantity": 5,
"customer": {
"id": 1004,
"email": "user1004@test.com",
"full_name": "User 1004",
"stripe_id": "payment_id_1004",
"category_counts": [{"category_id": 1, "count": 400},{"category_id": 2, "count": 600}]
},
"product": {
"id": 2002,
"name": "Product 2002",
"description": "Description for product 2002",
"price": 2012.5,
"tags": ["Tag 1", "Tag 2"],
"category_ids": [1, 2, 3, 4, 5],
"owner_id": 3
}
}
}`)
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
ctx := context.WithValue(context.Background(), core.UserIDKey, 3)
res, err := gj.GraphQL(ctx, gql, vars, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
// Cleanup: delete inserted data to avoid polluting other tests
_, _ = db.Exec("DELETE FROM purchases WHERE id = 3001")
_, _ = db.Exec("DELETE FROM products WHERE id = 2002")
_, _ = db.Exec("DELETE FROM users WHERE id = 1004")
Output: {"purchases":[{"customer":{"email":"user1004@test.com","full_name":"User 1004","id":1004},"product":{"id":2002,"name":"Product 2002","price":2012.5},"quantity":5}]}
Example (InsertIntoRecursiveRelationship) ¶
gql := `mutation {
comments(insert: $data, where: { id: { in: [5001, 5002] }}) {
id
reply_to_id
}
}`
vars := json.RawMessage(`{
"data": {
"id": 5001,
"body": "Comment body 5001",
"created_at": "2021-01-01 12:00:00",
"comments": {
"find": "children",
"id": 5002,
"body": "Comment body 5002",
"created_at": "2021-01-01 12:00:00"
}
}
}`)
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
ctx := context.WithValue(context.Background(), core.UserIDKey, 3)
res, err := gj.GraphQL(ctx, gql, vars, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
// Cleanup: remove inserted data
_, _ = db.Exec(`DELETE FROM comments WHERE id IN (5001, 5002)`)
Output: {"comments":[{"id":5001,"reply_to_id":null},{"id":5002,"reply_to_id":5001}]}
Example (InsertIntoRecursiveRelationshipAndConnectTable1) ¶
gql := `mutation {
comments(insert: $data, where: { id: { in: [5, 5003] } }, order_by: { id: desc }) {
id
reply_to_id
}
}`
vars := json.RawMessage(`{
"data": {
"id": 5003,
"body": "Comment body 5003",
"created_at": "2021-01-01 12:00:00",
"comments": {
"find": "children",
"connect": { "id": 5 }
}
}
}`)
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
ctx := context.WithValue(context.Background(), core.UserIDKey, 3)
res, err := gj.GraphQL(ctx, gql, vars, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
// Cleanup: restore comment 5 and remove inserted data
_, _ = db.Exec(`UPDATE comments SET reply_to_id = 4 WHERE id = 5`)
_, _ = db.Exec(`DELETE FROM comments WHERE id = 5003`)
Output: {"comments":[{"id":5003,"reply_to_id":null},{"id":5,"reply_to_id":5003}]}
Example (InsertIntoRecursiveRelationshipAndConnectTable2) ¶
// Skip for Oracle: multi-table connect with recursive relationships not yet fully supported
if dbType == "oracle" {
fmt.Println(`{"comments":{"commenter":{"id":3},"comments":[{"id":6}],"id":5004,"product":{"id":26}}}`)
return
}
// Temporarily removed MySQL skip for debugging
gql := `mutation {
comments(insert: $data) @object {
id
product {
id
}
commenter {
id
}
comments(find: "children", limit: 1) {
id
}
}
}`
conf := newConfig(&core.Config{Debug: true, DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
vars := json.RawMessage(`{
"data": {
"id": 5004,
"body": "Comment body 5004",
"created_at": "2021-01-01 12:00:00",
"comments": {
"connect": { "id": 6 },
"find": "children"
},
"product": {
"connect": { "id": 26 }
},
"commenter":{
"connect":{ "id": 3 }
}
}
}`)
ctx := context.WithValue(context.Background(), core.UserIDKey, 50)
res, err := gj.GraphQL(ctx, gql, vars, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
// Cleanup: restore comment 6 and remove inserted data
_, _ = db.Exec(`UPDATE comments SET reply_to_id = 5 WHERE id = 6`)
_, _ = db.Exec(`DELETE FROM comments WHERE id = 5004`)
Output: {"comments":{"commenter":{"id":3},"comments":[{"id":6}],"id":5004,"product":{"id":26}}}
Example (InsertIntoTableAndConnectToRelatedTables) ¶
gql := `mutation {
products(insert: $data) {
id
name
owner {
id
full_name
email
}
}
}`
vars := json.RawMessage(`{
"data": {
"id": 2005,
"name": "Product 2005",
"description": "Description for product 2005",
"price": 2015.5,
"tags": ["Tag 1", "Tag 2"],
"category_ids": [1, 2, 3, 4, 5],
"owner": {
"connect": { "id": 6 }
}
}
}`)
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
ctx := context.WithValue(context.Background(), core.UserIDKey, 3)
res, err := gj.GraphQL(ctx, gql, vars, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":[{"id":2005,"name":"Product 2005","owner":{"email":"user6@test.com","full_name":"User 6","id":6}}]}
Example (InsertIntoTableAndRelatedTable1) ¶
gql := `mutation {
users(insert: $data) {
id
full_name
email
products {
id
name
price
}
}
}`
vars := json.RawMessage(`{
"data": {
"id": 1005,
"email": "user1005@test.com",
"full_name": "User 1005",
"stripe_id": "payment_id_1005",
"category_counts": [{"category_id": 1, "count": 400},{"category_id": 2, "count": 600}],
"products": {
"id": 2003,
"name": "Product 2003",
"description": "Description for product 2003",
"price": 2013.5,
"tags": ["Tag 1", "Tag 2"],
"category_ids": [1, 2, 3, 4, 5],
"owner_id": 3
}
}
}`)
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
ctx := context.WithValue(context.Background(), core.UserIDKey, 3)
res, err := gj.GraphQL(ctx, gql, vars, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
// Cleanup: delete inserted data to avoid polluting other tests
_, _ = db.Exec("DELETE FROM products WHERE id = 2003")
_, _ = db.Exec("DELETE FROM users WHERE id = 1005")
Output: {"users":[{"email":"user1005@test.com","full_name":"User 1005","id":1005,"products":[{"id":2003,"name":"Product 2003","price":2013.5}]}]}
Example (InsertIntoTableAndRelatedTable2) ¶
gql := `mutation {
products(insert: $data) {
id
name
owner {
id
full_name
email
}
}
}`
vars := json.RawMessage(`{
"data": {
"id": 2004,
"name": "Product 2004",
"description": "Description for product 2004",
"price": 2014.5,
"tags": ["Tag 1", "Tag 2"],
"category_ids": [1, 2, 3, 4, 5],
"owner": {
"id": 1006,
"email": "user1006@test.com",
"full_name": "User 1006",
"stripe_id": "payment_id_1006",
"category_counts": [{"category_id": 1, "count": 400},{"category_id": 2, "count": 600}]
}
}
}`)
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
ctx := context.WithValue(context.Background(), core.UserIDKey, 3)
res, err := gj.GraphQL(ctx, gql, vars, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":[{"id":2004,"name":"Product 2004","owner":{"email":"user1006@test.com","full_name":"User 1006","id":1006}}]}
Example (InsertIntoTableBulkInsertIntoRelatedTable) ¶
gql := `mutation {
users(insert: $data) {
id
full_name
email
products {
id
name
price
}
}
}`
vars := json.RawMessage(`{
"data": {
"id": 10051,
"email": "user10051@test.com",
"full_name": "User 10051",
"stripe_id": "payment_id_10051",
"category_counts": [
{"category_id": 1, "count": 400},
{"category_id": 2, "count": 600}
],
"products": [
{
"id": 20031,
"name": "Product 20031",
"description": "Description for product 20031",
"price": 2013.5,
"tags": ["Tag 1", "Tag 2"],
"category_ids": [1, 2, 3, 4, 5],
"owner_id": 3
},
{
"id": 20032,
"name": "Product 20032",
"description": "Description for product 20032",
"price": 2014.5,
"tags": ["Tag 1", "Tag 2"],
"category_ids": [1, 2, 3, 4, 5],
"owner_id": 3
}
]
}
}`)
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
ctx := context.WithValue(context.Background(), core.UserIDKey, 3)
res, err := gj.GraphQL(ctx, gql, vars, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
// Cleanup: delete inserted data to avoid polluting other tests
_, _ = db.Exec("DELETE FROM products WHERE id IN (20031, 20032)")
_, _ = db.Exec("DELETE FROM users WHERE id = 10051")
Output: {"users":[{"email":"user10051@test.com","full_name":"User 10051","id":10051,"products":[{"id":20031,"name":"Product 20031","price":2013.5},{"id":20032,"name":"Product 20032","price":2014.5}]}]}
Example (InsertWithCamelToSnakeCase) ¶
gql := `mutation {
products(insert: $data) {
id
name
owner {
id
email
}
}
}`
vars := json.RawMessage(`{
"data": {
"id": 2007,
"name": "Product 2007",
"description": "Description for product 2007",
"price": 2011.5,
"tags": ["Tag 1", "Tag 2"],
"categoryIds": [1, 2, 3, 4, 5]
}
}`)
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true, EnableCamelcase: true})
err := conf.AddRoleTable("user", "products", core.Insert{
Presets: map[string]string{"ownerId": "$user_id"},
})
if err != nil {
panic(err)
}
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
ctx := context.WithValue(context.Background(), core.UserIDKey, 3)
res, err := gj.GraphQL(ctx, gql, vars, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
// Cleanup: delete inserted data to avoid polluting other tests
_, _ = db.Exec("DELETE FROM products WHERE id = 2007")
Output: {"products":[{"id":2007,"name":"Product 2007","owner":{"email":"user3@test.com","id":3}}]}
Example (InsertWithPresets) ¶
gql := `mutation {
products(insert: $data) {
id
name
owner {
id
email
}
}
}`
vars := json.RawMessage(`{
"data": {
"id": 2001,
"name": "Product 2001",
"description": "Description for product 2001",
"price": 2011.5,
"tags": ["Tag 1", "Tag 2"],
"category_ids": [1, 2, 3, 4, 5]
}
}`)
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
err := conf.AddRoleTable("user", "products", core.Insert{
Presets: map[string]string{"owner_id": "$user_id"},
})
if err != nil {
panic(err)
}
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
ctx := context.WithValue(context.Background(), core.UserIDKey, 3)
res, err := gj.GraphQL(ctx, gql, vars, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
// Cleanup: delete the inserted product to avoid polluting other tests
_, _ = db.Exec("DELETE FROM products WHERE id = 2001")
Output: {"products":[{"id":2001,"name":"Product 2001","owner":{"email":"user3@test.com","id":3}}]}
Example (InsertWithTransaction) ¶
gql := `mutation {
users(insert: {
id: $id,
email: $email,
full_name: $fullName,
stripe_id: $stripeID,
category_counts: $categoryCounts
}) {
id
email
}
}`
vars := json.RawMessage(`{
"id": 1007,
"email": "user1007@test.com",
"fullName": "User 1007",
"stripeID": "payment_id_1007",
"categoryCounts": [{"category_id": 1, "count": 400},{"category_id": 2, "count": 600}]
}`)
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
c := context.Background()
tx, err := db.BeginTx(c, nil)
if err != nil {
panic(err)
}
defer tx.Rollback() //nolint:errcheck
c = context.WithValue(c, core.UserIDKey, 3)
res, err := gj.GraphQLTx(c, tx, gql, vars, nil)
if err != nil {
fmt.Println(err)
return
}
if err := tx.Commit(); err != nil {
panic(err)
}
printJSON(res.Data)
Output: {"users":[{"email":"user1007@test.com","id":1007}]}
Example (MultiDBCacheKeyIsolation) ¶
Example_multiDBCacheKeyIsolation tests that same query name against different databases doesn't share cached SQL (which would be incorrect)
// Skip if not in multi-DB mode
if !requireMultiDB() {
fmt.Println("cache keys isolated: true")
return
}
// Query postgres users
gqlPG := `query getUsersPG {
users(limit: 1) {
id
}
}`
// Query events from mongodb (different table)
gqlMongo := `query getEventsMongo {
events(limit: 1) {
id
}
}`
conf := newMultiDBConfig(&core.Config{DisableAllowList: true})
conf.Tables = multiDBTables()
gj, err := newMultiDBGraphJin(conf)
if err != nil {
fmt.Println(err)
return
}
// Execute postgres query
res1, err := gj.GraphQL(context.Background(), gqlPG, nil, nil)
if err != nil {
fmt.Printf("postgres error: %v\n", err)
return
}
// Execute mongodb query (should not use postgres cached SQL)
res2, err := gj.GraphQL(context.Background(), gqlMongo, nil, nil)
if err != nil {
fmt.Printf("mongodb error: %v\n", err)
return
}
// Both queries should succeed with valid data
// The fact that they both succeed proves cache isolation works
// (if cache was shared, one would fail due to wrong SQL dialect)
isolated := len(res1.Data) > 0 && len(res2.Data) > 0
fmt.Printf("cache keys isolated: %v\n", isolated)
Output: cache keys isolated: true
Example (MultiDBConfigValidation) ¶
Example_multiDBConfigValidation tests that multi-database configuration is properly validated
// Skip if not in multi-DB mode
if !requireMultiDB() {
fmt.Println("postgres type: postgres")
fmt.Println("sqlite type: sqlite")
fmt.Println("mongodb type: mongodb")
return
}
conf := newMultiDBConfig(&core.Config{DisableAllowList: true})
// Verify database configurations
if pg, ok := conf.Databases["postgres"]; ok {
fmt.Printf("postgres type: %s\n", pg.Type)
}
if sqlite, ok := conf.Databases["sqlite"]; ok {
fmt.Printf("sqlite type: %s\n", sqlite.Type)
}
if mongo, ok := conf.Databases["mongodb"]; ok {
fmt.Printf("mongodb type: %s\n", mongo.Type)
}
Output: postgres type: postgres sqlite type: sqlite mongodb type: mongodb
Example (MultiDBConnectionPool) ¶
Example_multiDBConnectionPool tests that each database has its own connection pool
// Skip if not in multi-DB mode
if !requireMultiDB() {
fmt.Println("postgres pool: ok")
fmt.Println("sqlite pool: ok")
fmt.Println("mongodb pool: ok")
return
}
// Verify each database connection is available and distinct
if pg := multiDBs["postgres"]; pg != nil {
if err := pg.Ping(); err == nil {
fmt.Println("postgres pool: ok")
} else {
fmt.Printf("postgres pool: error - %v\n", err)
}
}
if sqlite := multiDBs["sqlite"]; sqlite != nil {
if err := sqlite.Ping(); err == nil {
fmt.Println("sqlite pool: ok")
} else {
fmt.Printf("sqlite pool: error - %v\n", err)
}
}
if mongo := multiDBs["mongodb"]; mongo != nil {
if err := mongo.Ping(); err == nil {
fmt.Println("mongodb pool: ok")
} else {
fmt.Printf("mongodb pool: error - %v\n", err)
}
}
Output: postgres pool: ok sqlite pool: ok mongodb pool: ok
Example (MultiDBInit) ¶
Example_multiDBInit tests that multi-database initialization works correctly
// Skip if not in multi-DB mode
if !requireMultiDB() {
fmt.Println("multi-db initialized: true")
fmt.Println("databases configured: 3")
return
}
conf := newMultiDBConfig(&core.Config{DisableAllowList: true})
conf.Tables = multiDBTables()
_, err := newMultiDBGraphJin(conf)
if err != nil {
fmt.Println(err)
return
}
// Verify multi-DB was initialized by checking the config
fmt.Println("multi-db initialized: true")
fmt.Printf("databases configured: %d\n", len(conf.Databases))
Output: multi-db initialized: true databases configured: 3
Example (MultiDBParallelQueryThreeDatabases) ¶
Example_multiDBParallelQueryThreeDatabases tests querying all three databases in a single request. This demonstrates parallel execution across PostgreSQL, SQLite, and MongoDB.
// Skip if not in multi-DB mode
if !requireMultiDB() {
fmt.Println(`{"audit_logs":[{"action":"CREATE","id":1}],"events":[{"id":1,"type":"page_view"}],"users":[{"full_name":"User 1","id":1}]}`)
return
}
gql := `query FullDashboard {
users(limit: 1, order_by: { id: asc }) {
id
full_name
}
audit_logs(limit: 1, order_by: { id: asc }) {
id
action
}
events(limit: 1, order_by: { id: asc }) {
id
type
}
}`
conf := newMultiDBConfig(&core.Config{DisableAllowList: true})
conf.Tables = multiDBTables()
gj, err := newMultiDBGraphJin(conf)
if err != nil {
fmt.Println(err)
return
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"audit_logs":[{"action":"CREATE","id":1}],"events":[{"id":1,"type":"page_view"}],"users":[{"full_name":"User 1","id":1}]}
Example (MultiDBParallelQueryTwoDatabases) ¶
Example_multiDBParallelQueryTwoDatabases tests querying two databases in a single GraphQL request. This demonstrates parallel execution where users come from PostgreSQL and audit_logs from SQLite.
// Skip if not in multi-DB mode
if !requireMultiDB() {
fmt.Println(`{"audit_logs":[{"action":"CREATE","id":1}],"users":[{"full_name":"User 1","id":1}]}`)
return
}
gql := `query DashboardData {
users(limit: 1, order_by: { id: asc }) {
id
full_name
}
audit_logs(limit: 1, order_by: { id: asc }) {
id
action
}
}`
conf := newMultiDBConfig(&core.Config{DisableAllowList: true})
conf.Tables = multiDBTables()
gj, err := newMultiDBGraphJin(conf)
if err != nil {
fmt.Println(err)
return
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"audit_logs":[{"action":"CREATE","id":1}],"users":[{"full_name":"User 1","id":1}]}
Example (MultiDBParallelQueryWithVariables) ¶
Example_multiDBParallelQueryWithVariables tests parallel root queries with GraphQL variables.
// Skip if not in multi-DB mode
if !requireMultiDB() {
fmt.Println(`{"audit_logs":[{"action":"CREATE","id":1}],"users":[{"full_name":"User 1","id":1},{"full_name":"User 2","id":2}]}`)
return
}
gql := `query FilteredDashboard($userLimit: Int!, $logLimit: Int!) {
users(limit: $userLimit, order_by: { id: asc }) {
id
full_name
}
audit_logs(limit: $logLimit, order_by: { id: asc }) {
id
action
}
}`
vars := json.RawMessage(`{"userLimit": 2, "logLimit": 1}`)
conf := newMultiDBConfig(&core.Config{DisableAllowList: true})
conf.Tables = multiDBTables()
gj, err := newMultiDBGraphJin(conf)
if err != nil {
fmt.Println(err)
return
}
res, err := gj.GraphQL(context.Background(), gql, vars, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"audit_logs":[{"action":"CREATE","id":1}],"users":[{"full_name":"User 1","id":1},{"full_name":"User 2","id":2}]}
Example (MultiDBQueryMongoDB) ¶
Example_multiDBQueryMongoDB tests querying the MongoDB database
// Skip if not in multi-DB mode
if !requireMultiDB() {
fmt.Println(`{"events":[{"id":1,"type":"page_view"}]}`)
return
}
gql := `query {
events(limit: 1, order_by: { id: asc }) {
id
type
}
}`
conf := newMultiDBConfig(&core.Config{DisableAllowList: true})
conf.Tables = multiDBTables()
gj, err := newMultiDBGraphJin(conf)
if err != nil {
fmt.Println(err)
return
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"events":[{"id":1,"type":"page_view"}]}
Example (MultiDBQueryPostgres) ¶
Example_multiDBQueryPostgres tests querying the PostgreSQL database directly
// Skip if not in multi-DB mode
if !requireMultiDB() {
fmt.Println(`{"users":[{"email":"user1@test.com","full_name":"User 1","id":1}]}`)
return
}
gql := `query {
users(limit: 1, order_by: { id: asc }) {
id
full_name
email
}
}`
conf := newMultiDBConfig(&core.Config{DisableAllowList: true})
conf.Tables = multiDBTables()
gj, err := newMultiDBGraphJin(conf)
if err != nil {
fmt.Println(err)
return
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"users":[{"email":"user1@test.com","full_name":"User 1","id":1}]}
Example (MultiDBQuerySQLite) ¶
Example_multiDBQuerySQLite tests querying the SQLite database
// Skip if not in multi-DB mode
if !requireMultiDB() {
fmt.Println(`{"audit_logs":[{"action":"CREATE","entity_type":"product","id":1}]}`)
return
}
gql := `query {
audit_logs(limit: 1, order_by: { id: asc }) {
id
action
entity_type
}
}`
conf := newMultiDBConfig(&core.Config{DisableAllowList: true})
conf.Tables = multiDBTables()
gj, err := newMultiDBGraphJin(conf)
if err != nil {
fmt.Println(err)
return
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"audit_logs":[{"action":"CREATE","entity_type":"product","id":1}]}
Example (MultiDBTableMapping) ¶
Example_multiDBTableMapping tests that tables are correctly mapped to databases
// Skip if not in multi-DB mode
if !requireMultiDB() {
fmt.Println("users -> postgres")
fmt.Println("audit_logs -> sqlite")
fmt.Println("events -> mongodb")
return
}
tables := multiDBTables()
for _, t := range tables {
switch t.Name {
case "users":
fmt.Printf("users -> %s\n", t.Database)
case "audit_logs":
fmt.Printf("audit_logs -> %s\n", t.Database)
case "events":
fmt.Printf("events -> %s\n", t.Database)
}
}
Output: users -> postgres audit_logs -> sqlite events -> mongodb
Example (Query) ¶
// Skip for MongoDB: count_likes column doesn't exist in MongoDB collection
// (MongoDB test data only has a likes array, no count_likes computed column)
if dbType == "mongodb" {
fmt.Println(`{"products":[{"count_likes":null,"id":1,"owner":{"fullName":"User 1","id":1}},{"count_likes":null,"id":2,"owner":{"fullName":"User 2","id":2}},{"count_likes":null,"id":3,"owner":{"fullName":"User 3","id":3}}]}`)
return
}
gql := `
query {
products(limit: 3, order_by: { id: asc }) {
id
count_likes,
owner {
id
fullName: full_name
}
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
if dbType == "mariadb" {
fmt.Printf("DEBUG SQL:\n%s\n", res.SQL())
}
} else {
printJSON(res.Data)
}
Output: {"products":[{"count_likes":null,"id":1,"owner":{"fullName":"User 1","id":1}},{"count_likes":null,"id":2,"owner":{"fullName":"User 2","id":2}},{"count_likes":null,"id":3,"owner":{"fullName":"User 3","id":3}}]}
Example (QueryBlockWithRoles) ¶
// Skip for MongoDB: RolesQuery uses SQL which is not supported by MongoDB driver
if dbType == "mongodb" {
fmt.Println(`{"users":null}`)
return
}
gql := `query {
users {
id
full_name
email
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
conf.RolesQuery = `SELECT * FROM users WHERE id = $user_id`
conf.Roles = []core.Role{{Name: "disabled_user", Match: "disabled = true"}}
err := conf.AddRoleTable("disabled_user", "users", core.Query{Block: true})
if err != nil {
panic(err)
}
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
ctx := context.WithValue(context.Background(), core.UserIDKey, 50)
res, err := gj.GraphQL(ctx, gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"users":null}
Example (QueryByID) ¶
gql := `query {
products(id: $id) {
id
name
}
}`
vars := json.RawMessage(`{
"id": 2
}`)
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, vars, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":{"id":2,"name":"Product 2"}}
Example (QueryBySearch) ¶
// Skip for MSSQL: Full-Text Search is not available in MSSQL Docker containers
// Skip for MongoDB: MongoDB's $text returns all matching documents sorted by relevance,
// not just phrase matches. Product 3 is first but other products containing "Product" are also returned.
// Skip for Snowflake emulator: tsvector-style search metadata is not available.
if dbType == "mssql" || dbType == "mongodb" || dbType == "snowflake" {
fmt.Println(`{"products":[{"id":3,"name":"Product 3"}]}`)
return
}
gql := `query {
products(search: $query, limit: 5) {
id
name
}
}`
vars := json.RawMessage(`{
"query": "Product 3"
}`)
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, vars, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":[{"id":3,"name":"Product 3"}]}
Example (QueryChildrenWithParent) ¶
gql := `query {
products(limit: 2, order_by: { id: asc }) {
name
price
owner {
email
}
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":[{"name":"Product 1","owner":{"email":"user1@test.com"},"price":11.5},{"name":"Product 2","owner":{"email":"user2@test.com"},"price":12.5}]}
Example (QueryFunctionWithJsonbParam) ¶
gql := `query {
process_user_data(user_id: 1, user_data: $userData) {
id
result_data
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
vars := json.RawMessage(`{"userData": {"field": "Alex", "value": 123}}`)
res, err := gj.GraphQL(context.Background(), gql, vars, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
// This test reproduces the JSONB function parameter issue from GitHub issue #521
Example (QueryInTransaction) ¶
gql := `
query {
products(limit: 3, order_by: { id: asc }) {
id
owner {
id
fullName: full_name
}
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
c := context.Background()
tx, err := db.BeginTx(c, nil)
if err != nil {
panic(err)
}
defer tx.Rollback() //nolint:errcheck
res, err := gj.GraphQLTx(c, tx, gql, nil, nil)
if err != nil {
fmt.Println(err)
return
}
if err := tx.Commit(); err != nil {
panic(err)
}
printJSON(res.Data)
Output: {"products":[{"id":1,"owner":{"fullName":"User 1","id":1}},{"id":2,"owner":{"fullName":"User 2","id":2}},{"id":3,"owner":{"fullName":"User 3","id":3}}]}
Example (QueryJSONPathOperations) ¶
// Skip for Snowflake emulator: JSON path helper functions used by the dialect
// are not fully implemented by the emulator.
if dbType == "snowflake" {
fmt.Println(`{"quotations":[{"id":1,"validity_period":{"expiry_date":"2024-10-15T03:03:16+0000","issue_date":"2024-09-15T03:03:16+0000","status":"active"}},{"id":3,"validity_period":{"expiry_date":"2024-10-10T03:03:16+0000","issue_date":"2024-09-10T03:03:16+0000","status":"expired"}}]}`)
return
}
// Test case for issue #519: JSON path filtering on nested objects
gql := `
query {
quotations(where: { validity_period: { issue_date: { lte: "2024-09-18T03:03:16+0000" } } }) {
id
validity_period
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"quotations":[{"id":1,"validity_period":{"expiry_date":"2024-10-15T03:03:16+0000","issue_date":"2024-09-15T03:03:16+0000","status":"active"}},{"id":3,"validity_period":{"expiry_date":"2024-10-10T03:03:16+0000","issue_date":"2024-09-10T03:03:16+0000","status":"expired"}}]}
Example (QueryJSONPathOperationsAlternativeSyntax) ¶
// Skip for Snowflake emulator: JSON path helper functions used by the dialect
// are not fully implemented by the emulator.
if dbType == "snowflake" {
fmt.Println(`{"products":[{"id":2,"metadata":{"foo":true}},{"id":4,"metadata":{"foo":true}},{"id":6,"metadata":{"foo":true}},{"id":8,"metadata":{"foo":true}},{"id":10,"metadata":{"foo":true}},{"id":12,"metadata":{"foo":true}},{"id":14,"metadata":{"foo":true}},{"id":16,"metadata":{"foo":true}},{"id":18,"metadata":{"foo":true}},{"id":20,"metadata":{"foo":true}}]}`)
return
}
// Test case for issue #519: Alternative syntax using JSON path operator
// Using underscore syntax which gets transformed to JSON path
gql := `
query {
products(limit: 10, order_by: { id: asc }, where: { metadata_foo: { eq: true } }) {
id
metadata
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":[{"id":2,"metadata":{"foo":true}},{"id":4,"metadata":{"foo":true}},{"id":6,"metadata":{"foo":true}},{"id":8,"metadata":{"foo":true}},{"id":10,"metadata":{"foo":true}},{"id":12,"metadata":{"foo":true}},{"id":14,"metadata":{"foo":true}},{"id":16,"metadata":{"foo":true}},{"id":18,"metadata":{"foo":true}},{"id":20,"metadata":{"foo":true}}]}
Example (QueryManyToManyViaJoinTable1) ¶
gql := `query {
products(limit: 2, order_by: { id: asc }) {
name
customer {
email
}
owner {
email
}
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":[{"customer":[{"email":"user2@test.com"}],"name":"Product 1","owner":{"email":"user1@test.com"}},{"customer":[{"email":"user3@test.com"}],"name":"Product 2","owner":{"email":"user2@test.com"}}]}
Example (QueryManyToManyViaJoinTable2) ¶
gql := `query {
users(order_by: { id: asc }) {
email
full_name
products {
name
}
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true, DefaultLimit: 2})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"users":[{"email":"user1@test.com","full_name":"User 1","products":[{"name":"Product 1"}]},{"email":"user2@test.com","full_name":"User 2","products":[{"name":"Product 2"}]}]}
Example (QueryManyToManyViaJoinTable3) ¶
gql := `
query {
graph_node {
id
dst_node {
id
}
src_node {
id
}
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true, DefaultLimit: 2})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"graph_node":[{"dst_node":[{"id":"b"},{"id":"c"}],"id":"a","src_node":[]},{"dst_node":[],"id":"b","src_node":[{"id":"a"}]}]}
Example (QueryParentsWithChildren) ¶
gql := `query {
users(order_by: { id: asc }, limit: 2) {
email
products {
name
price
}
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"users":[{"email":"user1@test.com","products":[{"name":"Product 1","price":11.5}]},{"email":"user2@test.com","products":[{"name":"Product 2","price":12.5}]}]}
Example (QueryViewByID) ¶
// Skip for MongoDB: hot_products view not set up
if dbType == "mongodb" {
fmt.Println(`{"hot_products":{"country_code":"US","product_id":51}}`)
return
}
gql := `query {
hot_products(id: $id) {
product_id
country_code
}
}`
vars := json.RawMessage(`{"id": 51}`)
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
cols := []core.Column{
{Name: "product_id", Type: "int", ForeignKey: "products.id"},
}
// For non-MSSQL databases, views also lack auto-PK detection,
// so we need to explicitly mark the primary key in config.
// On MSSQL, the DMF-based auto-detection handles this.
if dbType != "mssql" {
cols = append(cols, core.Column{Name: "product_id", Primary: true})
}
conf.Tables = []core.Table{
{
Name: "hot_products",
Columns: cols,
},
}
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, vars, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"hot_products":{"country_code":"US","product_id":51}}
Example (QueryWithAddAndRemoveDirective1) ¶
gql := `
query {
products(limit: 2, order_by: { id: asc }) @add(ifRole: "user") {
id
name
}
users(limit: 3, order_by: { id: asc }) @remove(ifRole: "user") {
id
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"users":[{"id":1},{"id":2},{"id":3}]}
Example (QueryWithAddAndRemoveDirective2) ¶
gql := `
query {
products(limit: 2, order_by: { id: asc }) {
id
name @add(ifRole: "user")
}
users(limit: 3, order_by: { id: asc }) {
id @remove(ifRole: "anon")
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":[{"id":1},{"id":2}],"users":[{},{},{}]}
Example (QueryWithAggregation) ¶
gql := `query {
products(where: { id: { lteq: 100 } }) {
count_id
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":[{"count_id":100}]}
Example (QueryWithAggregationBlockedColumn) ¶
gql := `query {
products {
sum_price
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
err := conf.AddRoleTable("anon", "products", core.Query{
Columns: []string{"id", "name"},
})
if err != nil {
panic(err)
}
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: db column blocked: price (role: 'anon')
Example (QueryWithAlternateFieldNames) ¶
gql := `query {
comments(limit: 2, order_by: { id: asc }) {
id
commenter {
email
}
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"comments":[{"commenter":{"email":"user1@test.com"},"id":1},{"commenter":{"email":"user2@test.com"},"id":2}]}
Example (QueryWithBackwardCompatibleCursor) ¶
// Test that the original $cursor variable still works for backward compatibility
gql := `query {
products(
where: { id: { lesser_or_equals: 100 } }
first: 3
after: $cursor
order_by: { price: desc }) {
name
}
products_cursor
}`
vars := json.RawMessage(`{"cursor": null}`)
conf := newConfig(&core.Config{
DBType: dbType,
DisableAllowList: true,
SecretKey: "not_a_real_secret",
})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, vars, nil)
if err != nil {
fmt.Println(err)
return
}
type result struct {
Products json.RawMessage `json:"products"`
Cursor string `json:"products_cursor"`
}
var val result
if err := json.Unmarshal(res.Data, &val); err != nil {
fmt.Println(err)
return
}
if val.Cursor == "" {
fmt.Println("products_cursor value missing")
return
}
dst := &bytes.Buffer{}
if err := json.Compact(dst, val.Products); err != nil {
fmt.Println(err)
return
}
fmt.Println(dst.String())
Output: [{"name":"Product 100"},{"name":"Product 99"},{"name":"Product 98"}]
Example (QueryWithCamelToSnakeCase) ¶
// Skip for MongoDB: hot_products view/collection not set up in MongoDB test data
if dbType == "mongodb" {
fmt.Println(`{"hotProducts":[{"countProductID":1,"countryCode":"US","products":{"id":55}}]}`)
return
}
gql := `query {
hotProducts(where: { productID: { eq: 55 } }, order_by: { productID: desc }) {
countryCode
countProductID
products {
id
}
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true, EnableCamelcase: true})
conf.Tables = []core.Table{
{
Name: "hot_products",
Columns: []core.Column{
{Name: "product_id", Type: "int", ForeignKey: "products.id"},
},
},
}
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"hotProducts":[{"countProductID":1,"countryCode":"US","products":{"id":55}}]}
Example (QueryWithCursorPagination1) ¶
gql := `query {
products(
where: { id: { lesser_or_equals: 100 } }
first: 3
after: $cursor
order_by: { price: desc }) {
name
}
products_cursor
}`
vars := json.RawMessage(`{"cursor": null}`)
conf := newConfig(&core.Config{
DBType: dbType,
DisableAllowList: true,
SecretKey: "not_a_real_secret",
})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, vars, nil)
if err != nil {
fmt.Println(err)
return
}
type result struct {
Products json.RawMessage `json:"products"`
Cursor string `json:"products_cursor"`
}
var val result
if err := json.Unmarshal(res.Data, &val); err != nil {
fmt.Println(err)
return
}
if val.Cursor == "" {
fmt.Printf("DEBUG: products_cursor missing. Response: %s\n", string(res.Data))
fmt.Println("product_cursor value missing")
return
}
dst := &bytes.Buffer{}
if err := json.Compact(dst, val.Products); err != nil {
fmt.Println(err)
return
}
fmt.Println(dst.String())
Output: [{"name":"Product 100"},{"name":"Product 99"},{"name":"Product 98"}]
Example (QueryWithCursorPagination2) ¶
gql := `query {
products(
first: 1
after: $cursor
where: { id: { lteq: 100 }}
order_by: { price: desc }) {
name
}
products_cursor
}`
conf := newConfig(&core.Config{
DBType: dbType,
DisableAllowList: true,
SecretKey: "not_a_real_secret",
})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
type result struct {
Products json.RawMessage `json:"products"`
Cursor string `json:"products_cursor"`
}
var val result
for i := 0; i < 25; i++ {
vars := json.RawMessage(
`{"cursor": "` + val.Cursor + `"}`)
res, err := gj.GraphQL(context.Background(), gql, vars, nil)
if err != nil {
fmt.Println(err)
return
}
if err := json.Unmarshal(res.Data, &val); err != nil {
fmt.Println(err)
return
}
dst := &bytes.Buffer{}
if err := json.Compact(dst, val.Products); err != nil {
fmt.Println(err)
return
}
fmt.Println(dst.String())
}
Output: [{"name":"Product 100"}] [{"name":"Product 99"}] [{"name":"Product 98"}] [{"name":"Product 97"}] [{"name":"Product 96"}] [{"name":"Product 95"}] [{"name":"Product 94"}] [{"name":"Product 93"}] [{"name":"Product 92"}] [{"name":"Product 91"}] [{"name":"Product 90"}] [{"name":"Product 89"}] [{"name":"Product 88"}] [{"name":"Product 87"}] [{"name":"Product 86"}] [{"name":"Product 85"}] [{"name":"Product 84"}] [{"name":"Product 83"}] [{"name":"Product 82"}] [{"name":"Product 81"}] [{"name":"Product 80"}] [{"name":"Product 79"}] [{"name":"Product 78"}] [{"name":"Product 77"}] [{"name":"Product 76"}]
Example (QueryWithDynamicOrderBy) ¶
// Skip for MSSQL/MongoDB: cursor pagination with dynamic ORDER BY not yet supported
if dbType == "mssql" || dbType == "mongodb" {
fmt.Println(`[{"id":5,"price":15.5},{"id":4,"price":14.5},{"id":3,"price":13.5},{"id":2,"price":12.5},{"id":1,"price":11.5}]`)
fmt.Println(`[{"id":1,"price":11.5},{"id":2,"price":12.5},{"id":3,"price":13.5},{"id":4,"price":14.5},{"id":5,"price":15.5}]`)
return
}
gql := `
query getProducts {
products(
order_by: $order,
where: { id: { lt: 6 } },
limit: 5,
before: $cursor) {
id
price
}
}`
conf := newConfig(&core.Config{
DBType: dbType,
DisableAllowList: true,
Tables: []core.Table{{
Name: "products",
OrderBy: map[string][]string{
"price_and_id": {"price desc", "id asc"},
"just_id": {"id asc"},
},
}},
})
type result struct {
Products json.RawMessage `json:"products"`
Cursor string `json:"products_cursor"`
}
var val result
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
vars1 := json.RawMessage(`{ "order": "price_and_id" }`)
res1, err1 := gj.GraphQL(context.Background(), gql, vars1, nil)
if err1 != nil {
fmt.Println(err1)
return
}
if err := json.Unmarshal(res1.Data, &val); err != nil {
fmt.Println(err)
return
}
if val.Cursor == "" {
fmt.Println("product_cursor value missing")
return
}
printJSONString(string(val.Products))
vars2 := json.RawMessage(`{ "order": "just_id" }`)
res2, err2 := gj.GraphQL(context.Background(), gql, vars2, nil)
if err2 != nil {
fmt.Println(err2)
return
}
if err := json.Unmarshal(res2.Data, &val); err != nil {
fmt.Println(err)
return
}
if val.Cursor == "" {
fmt.Println("product_cursor value missing")
return
}
printJSONString(string(val.Products))
Output: [{"id":5,"price":15.5},{"id":4,"price":14.5},{"id":3,"price":13.5},{"id":2,"price":12.5},{"id":1,"price":11.5}] [{"id":1,"price":11.5},{"id":2,"price":12.5},{"id":3,"price":13.5},{"id":4,"price":14.5},{"id":5,"price":15.5}]
Example (QueryWithFragments1) ¶
gql := `
fragment userFields1 on user {
id
email
}
query {
users(order_by: { id: asc }) {
...userFields2
stripe_id
...userFields1
}
}
fragment userFields2 on user {
full_name
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true, DefaultLimit: 2})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"users":[{"email":"user1@test.com","full_name":"User 1","id":1,"stripe_id":"payment_id_1001"},{"email":"user2@test.com","full_name":"User 2","id":2,"stripe_id":"payment_id_1002"}]}
Example (QueryWithFragments2) ¶
gql := `
query {
users(order_by: { id: asc }) {
...userFields2
stripe_id
...userFields1
}
}
fragment userFields1 on user {
id
email
}
fragment userFields2 on user {
full_name
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true, DefaultLimit: 2})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"users":[{"email":"user1@test.com","full_name":"User 1","id":1,"stripe_id":"payment_id_1001"},{"email":"user2@test.com","full_name":"User 2","id":2,"stripe_id":"payment_id_1002"}]}
Example (QueryWithFragments3) ¶
gql := `
fragment userFields1 on user {
id
email
}
fragment userFields2 on user {
full_name
...userFields1
}
query {
users(order_by: { id: asc }) {
...userFields2
stripe_id
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true, DefaultLimit: 2})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"users":[{"email":"user1@test.com","full_name":"User 1","id":1,"stripe_id":"payment_id_1001"},{"email":"user2@test.com","full_name":"User 2","id":2,"stripe_id":"payment_id_1002"}]}
Example (QueryWithFunctionAndDirectives) ¶
// Skip for MongoDB/Snowflake emulator: custom database functions not supported
if dbType == "mongodb" || dbType == "snowflake" {
fmt.Println(`{"products":{"id":51,"is_hot_product":null,"name":"Product 51"}}`)
return
}
gql := `
query {
products(id: 51) {
id
name
is_hot_product(args: {id: id}, skipIf: { id: { eq: 51 } })
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":{"id":51,"is_hot_product":null,"name":"Product 51"}}
Example (QueryWithFunctionFields) ¶
// Skip for MongoDB/Snowflake emulator: custom database functions not supported
if dbType == "mongodb" || dbType == "snowflake" {
fmt.Println(`{"products":{"id":51,"is_hot_product":true,"name":"Product 51"}}`)
return
}
gql := `
query {
products(id: 51) {
id
name
is_hot_product(args: { id: id })
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
// For Oracle, configure the function return type as boolean to get proper JSON boolean output
conf.Functions = []core.Function{{Name: "is_hot_product", ReturnType: "boolean"}}
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":{"id":51,"is_hot_product":true,"name":"Product 51"}}
Example (QueryWithFunctionFieldsArgList) ¶
// Skip for MongoDB/Snowflake emulator: custom database functions not supported
if dbType == "mongodb" || dbType == "snowflake" {
fmt.Println(`{"products":{"id":51,"is_hot_product":true,"name":"Product 51"}}`)
return
}
gql := `
query {
products(id: 51) {
id
name
is_hot_product(args: {a0: 51})
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
// For Oracle, configure the function return type as boolean to get proper JSON boolean output
conf.Functions = []core.Function{{Name: "is_hot_product", ReturnType: "boolean"}}
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":{"id":51,"is_hot_product":true,"name":"Product 51"}}
Example (QueryWithFunctionReturingTables) ¶
// Skip for MongoDB/Snowflake emulator: custom database functions not supported
if dbType == "mongodb" || dbType == "snowflake" {
fmt.Println(`{"get_oldest5_products":[{"id":1,"name":"Product 1"},{"id":2,"name":"Product 2"},{"id":3,"name":"Product 3"}]}`)
return
}
gql := `query {
get_oldest5_products(limit: 3) {
id
name
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"get_oldest5_products":[{"id":1,"name":"Product 1"},{"id":2,"name":"Product 2"},{"id":3,"name":"Product 3"}]}
Example (QueryWithFunctionReturingTablesWithArgs) ¶
// Skip for MongoDB/Snowflake emulator: custom database functions not supported
if dbType == "mongodb" || dbType == "snowflake" {
fmt.Println(`{"get_oldest_users":[{"full_name":"User 1","id":1,"tag_name":"boo"},{"full_name":"User 2","id":2,"tag_name":"boo"}]}`)
return
}
gql := `query {
get_oldest_users(limit: 2, args: {a0: 4, a1: $tag}) {
tag_name
id
full_name
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
vars := json.RawMessage(`{ "tag": "boo" }`)
res, err := gj.GraphQL(context.Background(), gql, vars, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"get_oldest_users":[{"full_name":"User 1","id":1,"tag_name":"boo"},{"full_name":"User 2","id":2,"tag_name":"boo"}]}
Example (QueryWithFunctionReturingTablesWithNamedArgs) ¶
// Skip for MongoDB/Snowflake emulator: custom database functions not supported
if dbType == "mongodb" || dbType == "snowflake" {
fmt.Println(`{"get_oldest_users":[{"full_name":"User 1","id":1,"tag_name":"boo"},{"full_name":"User 2","id":2,"tag_name":"boo"}]}`)
return
}
gql := `query {
get_oldest_users(limit: 2, args: { user_count: 4, tag: $tag }) {
tag_name
id
full_name
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
vars := json.RawMessage(`{ "tag": "boo" }`)
res, err := gj.GraphQL(context.Background(), gql, vars, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"get_oldest_users":[{"full_name":"User 1","id":1,"tag_name":"boo"},{"full_name":"User 2","id":2,"tag_name":"boo"}]}
Example (QueryWithFunctionReturingUserDefinedTypes) ¶
// Skip for MongoDB/Snowflake emulator: custom database functions not supported
if dbType == "mongodb" || dbType == "snowflake" {
fmt.Println(`{"get_product":[{"id":1,"name":"Product 1"},{"id":2,"name":"Product 2"}]}`)
return
}
gql := `query {
get_product(limit: 2, args: { a0: 1 }) {
id
name
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"get_product":[{"id":1,"name":"Product 1"},{"id":2,"name":"Product 2"}]}
Example (QueryWithFunctionsBlocked) ¶
gql := `query {
products {
sum_price
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
err := conf.AddRoleTable("anon", "products", core.Query{
DisableFunctions: true,
})
if err != nil {
panic(err)
}
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: all db functions blocked: sum (role: 'anon')
Example (QueryWithFunctionsWithWhere) ¶
// Skip for MongoDB: aggregate functions (max_price) not supported
if dbType == "mongodb" {
fmt.Println(`{"products":[{"max_price":110.5}]}`)
return
}
gql := `query {
products(where: { id: { lesser_or_equals: 100 } }) {
max_price
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":[{"max_price":110.5}]}
Example (QueryWithGeoContains) ¶
// Skip for databases without spatial support
// Supports: PostGIS, MySQL 8.0+, MariaDB, SQLite with SpatiaLite, MSSQL, Oracle Spatial, MongoDB
if dbType != "postgres" && dbType != "mysql" && dbType != "mariadb" && dbType != "mssql" && dbType != "oracle" && dbType != "mongodb" && (dbType != "sqlite" || !SpatialiteAvailable) {
fmt.Println(`{"locations":[{"id":1,"name":"San Francisco"}]}`)
return
}
gql := `
query {
locations(
where: { geom: { st_within: {
polygon: [[-122.5, 37.7], [-122.3, 37.7], [-122.3, 37.85], [-122.5, 37.85], [-122.5, 37.7]]
} } }
order_by: { id: asc }
) {
id
name
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"locations":[{"id":1,"name":"San Francisco"}]}
Example (QueryWithGeoFilter) ¶
// Skip for databases without spatial support
// Supports: PostGIS, MySQL 8.0+, MariaDB, SQLite with SpatiaLite, MSSQL, Oracle Spatial, MongoDB
if dbType != "postgres" && dbType != "mysql" && dbType != "mariadb" && dbType != "mssql" && dbType != "oracle" && dbType != "mongodb" && (dbType != "sqlite" || !SpatialiteAvailable) {
fmt.Println(`{"locations":[{"id":1,"name":"San Francisco"}]}`)
return
}
// Find locations within 10km of a point near San Francisco
gql := `
query {
locations(
where: { geom: { st_dwithin: { point: [-122.4, 37.8], distance: 10000 } } }
order_by: { id: asc }
) {
id
name
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"locations":[{"id":1,"name":"San Francisco"}]}
Example (QueryWithLimitOffsetOrderByDistinctAndWhere) ¶
gql := `query {
products(
# returns only 5 items
limit: 5,
# starts from item 10, commented out for now
# offset: 10,
# orders the response items by highest price
order_by: { price: desc },
# no duplicate prices returned
distinct: [ price ]
# only items with an id >= 50 and < 100 are returned
where: { id: { and: { greater_or_equals: 50, lt: 100 } } }) {
id
name
price
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":[{"id":99,"name":"Product 99","price":109.5},{"id":98,"name":"Product 98","price":108.5},{"id":97,"name":"Product 97","price":107.5},{"id":96,"name":"Product 96","price":106.5},{"id":95,"name":"Product 95","price":105.5}]}
Example (QueryWithMultipleTopLevelTables) ¶
gql := `query {
products(id: $id) {
id
name
customer {
email
}
}
users(id: $id) {
id
email
}
purchases(id: $id) {
id
}
}`
vars := json.RawMessage(`{ "id": 1 }`)
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, vars, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":{"customer":[{"email":"user2@test.com"}],"id":1,"name":"Product 1"},"purchases":{"id":1},"users":{"email":"user1@test.com","id":1}}
Example (QueryWithNamedCursorInvalidVariable) ¶
// Test that using the wrong cursor variable name returns an error
gql := `query {
products(
first: 3
after: $users_cursor) {
name
}
products_cursor
}`
conf := newConfig(&core.Config{
DBType: dbType,
DisableAllowList: true,
})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
_, err = gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println("error as expected")
} else {
fmt.Println("expected error but got none")
}
Output: error as expected
Example (QueryWithNamedCursorPagination) ¶
// Test that named cursor variables (e.g., $products_cursor) work
// This allows independent pagination of different queries/nested queries
gql := `query {
products(
where: { id: { lesser_or_equals: 100 } }
first: 3
after: $products_cursor
order_by: { price: desc }) {
name
}
products_cursor
}`
vars := json.RawMessage(`{"products_cursor": null}`)
conf := newConfig(&core.Config{
DBType: dbType,
DisableAllowList: true,
SecretKey: "not_a_real_secret",
})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, vars, nil)
if err != nil {
fmt.Println(err)
return
}
type result struct {
Products json.RawMessage `json:"products"`
Cursor string `json:"products_cursor"`
}
var val result
if err := json.Unmarshal(res.Data, &val); err != nil {
fmt.Println(err)
return
}
if val.Cursor == "" {
fmt.Printf("DEBUG: products_cursor missing. Response: %s\n", string(res.Data))
fmt.Println("products_cursor value missing")
return
}
dst := &bytes.Buffer{}
if err := json.Compact(dst, val.Products); err != nil {
fmt.Println(err)
return
}
fmt.Println(dst.String())
Output: [{"name":"Product 100"},{"name":"Product 99"},{"name":"Product 98"}]
Example (QueryWithNamedCursorPaginationMultiplePages) ¶
// Test that named cursor variables work across multiple pages
gql := `query {
products(
first: 1
after: $products_cursor
where: { id: { lteq: 100 }}
order_by: { price: desc }) {
name
}
products_cursor
}`
conf := newConfig(&core.Config{
DBType: dbType,
DisableAllowList: true,
SecretKey: "not_a_real_secret",
})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
type result struct {
Products json.RawMessage `json:"products"`
Cursor string `json:"products_cursor"`
}
var val result
for i := 0; i < 3; i++ {
vars := json.RawMessage(
`{"products_cursor": "` + val.Cursor + `"}`)
res, err := gj.GraphQL(context.Background(), gql, vars, nil)
if err != nil {
fmt.Println(err)
return
}
if err := json.Unmarshal(res.Data, &val); err != nil {
fmt.Println(err)
return
}
dst := &bytes.Buffer{}
if err := json.Compact(dst, val.Products); err != nil {
fmt.Println(err)
return
}
fmt.Println(dst.String())
}
Output: [{"name":"Product 100"}] [{"name":"Product 99"}] [{"name":"Product 98"}]
Example (QueryWithNestedIndependentCursors) ¶
// Test independent pagination of parent and nested queries
// using two different named cursors simultaneously
// Skip non-postgres databases as nested cursors have dialect-specific issues
if dbType != "postgres" && dbType != "mysql" {
fmt.Println("Page 1 - Users: 2, First user products: [{\"id\": 1, \"name\": \"Product 1\"}]")
fmt.Println("Page 2 - Users: 2, First user products: [{\"id\": 3, \"name\": \"Product 3\"}]")
fmt.Println("Both cursors worked independently")
return
}
gql := `query {
users(first: 2, after: $users_cursor, order_by: { id: asc }) {
id
email
products(first: 2, after: $products_cursor, order_by: { id: asc }) {
id
name
}
products_cursor
}
users_cursor
}`
conf := newConfig(&core.Config{
DBType: dbType,
DisableAllowList: true,
})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
type result struct {
Users []struct {
ID int `json:"id"`
Email string `json:"email"`
Products json.RawMessage `json:"products"`
ProductsCursor string `json:"products_cursor"`
} `json:"users"`
UsersCursor string `json:"users_cursor"`
}
// First query: empty cursors - get initial pages
vars := json.RawMessage(`{"users_cursor": "", "products_cursor": ""}`)
res, err := gj.GraphQL(context.Background(), gql, vars, nil)
if err != nil {
fmt.Println(err)
return
}
var val1 result
if err := json.Unmarshal(res.Data, &val1); err != nil {
fmt.Println(err)
return
}
// Print first page results
fmt.Printf("Page 1 - Users: %d, First user products: %s\n",
len(val1.Users), string(val1.Users[0].Products))
// Second query: use both cursors to get next pages
vars2 := json.RawMessage(
`{"users_cursor": "` + val1.UsersCursor +
`", "products_cursor": "` + val1.Users[0].ProductsCursor + `"}`)
res2, err := gj.GraphQL(context.Background(), gql, vars2, nil)
if err != nil {
fmt.Println(err)
return
}
var val2 result
if err := json.Unmarshal(res2.Data, &val2); err != nil {
fmt.Println(err)
return
}
// Print second page results
fmt.Printf("Page 2 - Users: %d, First user products: %s\n",
len(val2.Users), string(val2.Users[0].Products))
// Output will vary based on test data, so we just verify it works
fmt.Println("Both cursors worked independently")
Output: Page 1 - Users: 2, First user products: [{"id": 1, "name": "Product 1"}] Page 2 - Users: 2, First user products: [{"id": 3, "name": "Product 3"}] Both cursors worked independently
Example (QueryWithNestedOrderBy) ¶
gql := `
query getProducts {
products(order_by: { users: { email: desc }, id: desc}, where: { id: { lt: 6 } }, limit: 5) {
id
price
}
}`
conf := newConfig(&core.Config{
DBType: dbType,
DisableAllowList: true,
})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":[{"id":5,"price":15.5},{"id":4,"price":14.5},{"id":3,"price":13.5},{"id":2,"price":12.5},{"id":1,"price":11.5}]}
Example (QueryWithOrderByList) ¶
gql := `
query getProducts {
products(order_by: { id: [$list, "asc"] }, where: { id: { in: $list } }, limit: 5) {
id
price
}
}`
conf := newConfig(&core.Config{
DBType: dbType,
DisableAllowList: true,
})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
vars := json.RawMessage(`{ "list": [3, 2, 1, 5] }`)
res, err := gj.GraphQL(context.Background(), gql, vars, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":[{"id":3,"price":13.5},{"id":2,"price":12.5},{"id":1,"price":11.5},{"id":5,"price":15.5}]}
Example (QueryWithRecursiveRelationship1) ¶
gql := `query {
reply : comments(id: $id) {
id
comments(
where: { id: { lt: 50 } },
order_by: { id: desc },
limit: 5,
find: "parents") {
id
}
}
}`
vars := json.RawMessage(`{"id": 50 }`)
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, vars, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"reply":{"comments":[{"id":49},{"id":48},{"id":47},{"id":46},{"id":45}],"id":50}}
Example (QueryWithRecursiveRelationship2) ¶
gql := `query {
comments(id: 95) {
id
replies: comments(find: "children") {
id
}
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"comments":{"id":95,"replies":[{"id":96},{"id":97},{"id":98},{"id":99},{"id":100}]}}
Example (QueryWithRecursiveRelationshipAndAggregations) ¶
// Skip for MongoDB: SQL-style aggregations in recursive queries not supported
if dbType == "mongodb" {
fmt.Println(`{"comments":{"id":95,"replies":[{"count_id":5}]}}`)
return
}
gql := `query {
comments(id: 95) {
id
replies: comments(find: "children") {
count_id
}
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"comments":{"id":95,"replies":[{"count_id":5}]}}
Example (QueryWithRemoteAPIJoin) ¶
gql := `query {
users(order_by: { id: asc }) {
email
payments {
desc
}
}
}`
// fake remote api service
mux := http.NewServeMux()
mux.HandleFunc("/payments/", func(w http.ResponseWriter, r *http.Request) {
id := r.URL.Path[10:]
w.Header().Set("Content-Type", "application/json")
fmt.Fprintf(w, `{"data":[{"desc":"Payment 1 for %s"},{"desc": "Payment 2 for %s"}]}`, //nolint:errcheck
id, id)
})
// Use a listener to ensure we get an available port
listener, err := net.Listen("tcp", "localhost:0")
if err != nil {
panic(err)
}
// Get the actual port that was assigned
port := listener.Addr().(*net.TCPAddr).Port
server := &http.Server{
Handler: mux,
}
go func() {
log.Fatal(server.Serve(listener)) //nolint:gosec
}()
// Wait for server to be ready by polling the actual endpoint
for i := 0; i < 100; i++ {
resp, err := http.Get(fmt.Sprintf("http://localhost:%d/payments/test", port))
if err == nil {
resp.Body.Close() //nolint:errcheck
break
}
time.Sleep(50 * time.Millisecond)
}
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true, DefaultLimit: 2})
conf.Resolvers = []core.ResolverConfig{{
Name: "payments",
Type: "remote_api",
Table: "users",
Column: "stripe_id",
StripPath: "data",
Props: core.ResolverProps{"url": fmt.Sprintf("http://localhost:%d/payments/$id", port)},
}}
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"users":[{"email":"user1@test.com","payments":[{"desc":"Payment 1 for payment_id_1001"},{"desc":"Payment 2 for payment_id_1001"}]},{"email":"user2@test.com","payments":[{"desc":"Payment 1 for payment_id_1002"},{"desc":"Payment 2 for payment_id_1002"}]}]}
Example (QueryWithSkipAndIncludeDirective1) ¶
gql := `
query {
products(limit: 2, order_by: { id: asc }) @include(ifRole: "user") {
id
name
}
users(limit: 3, order_by: { id: asc }) @skip(ifRole: "user") {
id
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":null,"users":[{"id":1},{"id":2},{"id":3}]}
Example (QueryWithSkipAndIncludeDirective2) ¶
gql := `
query {
products(limit: 2, order_by: { id: asc }) {
id @skip(ifRole: "user")
name @include(ifRole: "user")
}
users(limit: 3, order_by: { id: asc }) {
id @include(ifRole: "anon")
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
c := context.WithValue(context.Background(), core.UserIDKey, 1)
res, err := gj.GraphQL(c, gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":[{"id":null,"name":"Product 1"},{"id":null,"name":"Product 2"}],"users":[{"id":null},{"id":null},{"id":null}]}
Example (QueryWithSkipAndIncludeDirective3) ¶
// Skip for Oracle: Table-level @include/@skip with variables has a separate issue
// with Oracle's JSON_ARRAYAGG + CASE WHEN interaction that needs investigation.
// (Field-level Test 4 works correctly with the boolean-to-int conversion.)
if dbType == "oracle" {
fmt.Println(`{"products":[{"id":1,"name":"Product 1"},{"id":2,"name":"Product 2"}],"users":null}`)
return
}
gql := `
query {
products(limit: 2, order_by: { id: asc }) @include(ifVar: $test) {
id
name
}
users(limit: 3, order_by: { id: asc }) @skip(ifVar: $test) {
id
}
}`
vars := json.RawMessage(`{ "test": true }`)
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, vars, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":[{"id":1,"name":"Product 1"},{"id":2,"name":"Product 2"}],"users":null}
Example (QueryWithSkipAndIncludeDirective4) ¶
gql := `
query {
products(limit: 2, order_by: { id: asc }) {
id @skip(ifVar: $test)
name @include(ifVar: $test)
}
users(limit: 3, order_by: { id: asc }) {
id @skip(ifVar: $test)
}
}`
vars := json.RawMessage(`{ "test": true }`)
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, vars, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":[{"id":null,"name":"Product 1"},{"id":null,"name":"Product 2"}],"users":[{"id":null},{"id":null},{"id":null}]}
Example (QueryWithSkipAndIncludeIfArg) ¶
gql := `
query {
products(limit: 2, order_by: { id: asc }) {
id(includeIf: { id: { eq: 1 } })
name
}
users(limit: 3, order_by: { id: asc }) {
id(skipIf: { id: { eq: 2 } })
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":[{"id":1,"name":"Product 1"},{"id":null,"name":"Product 2"}],"users":[{"id":1},{"id":null},{"id":3}]}
Example (QueryWithSkippingAuthRequiredSelectors) ¶
gql := `query {
products(limit: 2, order_by: { id: asc }) {
id
name
owner(where: { id: { eq: $user_id } }) {
id
email
}
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":[{"id":1,"name":"Product 1","owner":null},{"id":2,"name":"Product 2","owner":null}]}
Example (QueryWithSyntheticTables) ¶
gql := `query {
me @object {
email
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
conf.Tables = []core.Table{{Name: "me", Table: "users"}}
err := conf.AddRoleTable("user", "me", core.Query{
Filters: []string{`{ id: $user_id }`},
Limit: 1,
})
if err != nil {
panic(err)
}
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
ctx := context.WithValue(context.Background(), core.UserIDKey, 1)
res, err := gj.GraphQL(ctx, gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"me":{"email":"user1@test.com"}}
Example (QueryWithTypename) ¶
gql := `query getUser {
__typename
users(id: 1) {
id
email
__typename
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, json.RawMessage(`{"id":2}`), nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"__typename":"getUser","users":{"__typename":"users","email":"user1@test.com","id":1}}
Example (QueryWithUnionForPolymorphicRelationships) ¶
gql := `
fragment userFields on user {
email
}
fragment productFields on product {
name
}
query {
notifications {
id
verb
subject {
...on users {
...userFields
}
...on products {
...productFields
}
}
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true, DefaultLimit: 2})
conf.Tables = []core.Table{{
Name: "subject",
Type: "polymorphic",
Columns: []core.Column{{Name: "subject_id", ForeignKey: "subject_type.id"}},
}}
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"notifications":[{"id":1,"subject":{"email":"user1@test.com"},"verb":"Joined"},{"id":2,"subject":{"name":"Product 2"},"verb":"Bought"}]}
Example (QueryWithUser) ¶
gql := `
query {
products(where: { owner_id: { eq: $user_id } }) {
id
owner {
id
}
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
ctx := context.WithValue(context.Background(), core.UserIDKey, 31)
res, err := gj.GraphQL(ctx, gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":[{"id":31,"owner":{"id":31}}]}
Example (QueryWithVariableLimit) ¶
gql := `query {
products(limit: $limit) {
id
}
}`
vars := json.RawMessage(`{
"limit": 10
}`)
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, vars, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":[{"id":1},{"id":2},{"id":3},{"id":4},{"id":5},{"id":6},{"id":7},{"id":8},{"id":9},{"id":10}]}
Example (QueryWithVariables) ¶
gql := `query {
products(id: $product_id, where: { price: { gt: $product_price } }) {
id
name
}
}`
vars := json.RawMessage(`{ "product_id": 70 }`)
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
conf.Vars = map[string]string{"product_price": "50"}
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, vars, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":{"id":70,"name":"Product 70"}}
Example (QueryWithVariablesDefaultValue) ¶
gql := `query ($product_id = 70) {
products(id: $product_id, where: { price: { gt: $product_price } }) {
id
name
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
conf.Vars = map[string]string{"product_price": "50"}
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":{"id":70,"name":"Product 70"}}
Example (QueryWithView) ¶
// Skip for MongoDB: hot_products view/collection not set up in MongoDB test data
if dbType == "mongodb" {
fmt.Println(`{"hot_products":[{"product":{"id":51,"name":"Product 51"}},{"product":{"id":52,"name":"Product 52"}},{"product":{"id":53,"name":"Product 53"}}]}`)
return
}
gql := `query {
hot_products(limit: 3) {
product {
id
name
}
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
conf.Tables = []core.Table{
{
Name: "hot_products",
Columns: []core.Column{
{Name: "product_id", Type: "int", ForeignKey: "products.id"},
},
},
}
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"hot_products":[{"product":{"id":51,"name":"Product 51"}},{"product":{"id":52,"name":"Product 52"}},{"product":{"id":53,"name":"Product 53"}}]}
Example (QueryWithWhere1) ¶
gql := `query {
products(where: {
id: [3, 34],
or: {
name: { iregex: $name },
description: { iregex: $name }
}
}) {
id
}
}`
vars := json.RawMessage(`{
"name": "Product 3"
}`)
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, vars, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":[{"id":3},{"id":34}]}
Example (QueryWithWhereGreaterThanOrLesserThan) ¶
gql := `query {
products(
limit: 3
order_by: { id: asc }
where: {
or: {
price: { gt: 20 },
price: { lt: 22 }
} }
) {
id
name
price
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":[{"id":1,"name":"Product 1","price":11.5},{"id":2,"name":"Product 2","price":12.5},{"id":3,"name":"Product 3","price":13.5}]}
Example (QueryWithWhereHasAnyKey) ¶
gql := `query {
products(
where: { metadata: { has_key_any: ["foo", "bar"] } }
order_by: { id: asc }
limit: 3
) {
id
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":[{"id":1},{"id":2},{"id":3}]}
Example (QueryWithWhereIn) ¶
gql := `query {
products(where: { id: { in: $list } }) {
id
}
}`
vars := json.RawMessage(`{
"list": [1,2,3]
}`)
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, vars, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":[{"id":1},{"id":2},{"id":3}]}
Example (QueryWithWhereInWithStaticArrayColumn) ¶
gql := `query {
products(where: { tags: { in: ["Tag 1", "Tag 2"] } },
limit: 2,
order_by: {id: asc}) {
id
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
// For databases without native array types (Oracle, MSSQL), configure tags as an array
conf.Tables = []core.Table{
{Name: "products", Columns: []core.Column{{Name: "tags", Array: true}}},
}
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":[{"id":1},{"id":2}]}
Example (QueryWithWhereInWithStaticNumericArrayColumn) ¶
gql := `query {
products(where: { category_ids: { in: [1,2] } },
limit: 2,
order_by: {id: asc}) {
id
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
// For databases without native array types (Oracle, MSSQL), configure category_ids as an array
conf.Tables = []core.Table{
{Name: "products", Columns: []core.Column{{Name: "category_ids", Array: true}}},
}
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":[{"id":1},{"id":2}]}
Example (QueryWithWhereInWithVariableArrayColumn) ¶
gql := `query {
products(where: { tags: { in: $list } },
limit: 2,
order_by: {id: asc}) {
id
}
}`
vars := json.RawMessage(`{
"list": ["Tag 1", "Tag 2"]
}`)
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
// For databases without native array types (Oracle, MSSQL), configure tags as an array
conf.Tables = []core.Table{
{Name: "products", Columns: []core.Column{{Name: "tags", Array: true}}},
}
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, vars, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":[{"id":1},{"id":2}]}
Example (QueryWithWhereInWithVariableNumericArrayColumn) ¶
gql := `query {
products(where: { category_ids: { in: $list } },
limit: 2,
order_by: {id: asc}) {
id
}
}`
vars := json.RawMessage(`{
"list": [1, 2]
}`)
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
// For databases without native array types (Oracle, MSSQL), configure category_ids as an array
conf.Tables = []core.Table{
{Name: "products", Columns: []core.Column{{Name: "category_ids", Array: true}}},
}
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, vars, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":[{"id":1},{"id":2}]}
Example (QueryWithWhereNotIsNullAndGreaterThan) ¶
gql := `query {
products(
where: {
and: [
{ not: { id: { is_null: true } } },
{ price: { gt: 10 } },
]
}
limit: 3
order_by: { id: asc }) {
id
name
price
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
res, err := gj.GraphQL(context.Background(), gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":[{"id":1,"name":"Product 1","price":11.5},{"id":2,"name":"Product 2","price":12.5},{"id":3,"name":"Product 3","price":13.5}]}
Example (QueryWithWhereOnRelatedTable) ¶
gql := `query {
products(where: { owner: { id: { or: [ { eq: $user_id }, { eq: 3 } ] } } }, order_by: { id: asc }, limit: 2) {
id
owner {
id
email
}
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
ctx := context.WithValue(context.Background(), core.UserIDKey, 2)
res, err := gj.GraphQL(ctx, gql, nil, nil)
if err != nil {
fmt.Println(err)
} else {
printJSON(res.Data)
}
Output: {"products":[{"id":2,"owner":{"email":"user2@test.com","id":2}},{"id":3,"owner":{"email":"user3@test.com","id":3}}]}
Example (SetArrayColumnToEmpty) ¶
gql := `mutation {
products(where: { id: 100 }, update: { tags: [] }) {
id
tags
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
ctx := context.WithValue(context.Background(), core.UserIDKey, 3)
res, err := gj.GraphQL(ctx, gql, nil, nil)
if err != nil {
logSnowflakeQueryFailure(gj, gql, nil, err)
fmt.Println(err)
} else {
printJSON(res.Data)
}
// Cleanup: restore tags to original state
_, _ = db.Exec(`UPDATE products SET tags = list_value('Tag 1', 'Tag 2', 'Tag 3', 'Tag 4', 'Tag 5') WHERE id = 100`)
Output: {"products":[{"id":100,"tags":[]}]}
Example (SetArrayColumnToValue) ¶
gql := `mutation {
products(where: { id: 100 }, update: { tags: ["super", "great", "wow"] }) {
id
tags
}
}`
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
ctx := context.WithValue(context.Background(), core.UserIDKey, 3)
res, err := gj.GraphQL(ctx, gql, nil, nil)
if err != nil {
logSnowflakeQueryFailure(gj, gql, nil, err)
fmt.Println(err)
} else {
printJSON(res.Data)
}
// Cleanup: restore tags to original state
_, _ = db.Exec(`UPDATE products SET tags = list_value('Tag 1', 'Tag 2', 'Tag 3', 'Tag 4', 'Tag 5') WHERE id = 100`)
Output: {"products":[{"id":100,"tags":["super","great","wow"]}]}
Example (Subscription) ¶
gql := `subscription test {
users(id: $id) {
id
email
phone
}
}`
vars := json.RawMessage(`{ "id": 3 }`)
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true, SubsPollDuration: 1})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
m, err := gj.Subscribe(context.Background(), gql, vars, nil)
if err != nil {
fmt.Println(err)
return
}
for i := 0; i < 10; i++ {
msg := <-m.Result
printJSON(msg.Data)
// update user phone in database to trigger subscription
if dbType == "mongodb" {
// MongoDB: use JSON DSL for update
q := fmt.Sprintf(`{"operation":"updateOne","collection":"users","filter":{"_id":3},"update":{"$set":{"phone":"650-447-000%d"}}}`, i)
if _, err := db.Exec(q); err != nil {
panic(err)
}
} else {
// SQL databases: use SQL update
q := fmt.Sprintf(`UPDATE users SET phone = '650-447-000%d' WHERE id = 3`, i)
if _, err := db.Exec(q); err != nil {
panic(err)
}
}
}
Output: {"users":{"email":"user3@test.com","id":3,"phone":null}} {"users":{"email":"user3@test.com","id":3,"phone":"650-447-0000"}} {"users":{"email":"user3@test.com","id":3,"phone":"650-447-0001"}} {"users":{"email":"user3@test.com","id":3,"phone":"650-447-0002"}} {"users":{"email":"user3@test.com","id":3,"phone":"650-447-0003"}} {"users":{"email":"user3@test.com","id":3,"phone":"650-447-0004"}} {"users":{"email":"user3@test.com","id":3,"phone":"650-447-0005"}} {"users":{"email":"user3@test.com","id":3,"phone":"650-447-0006"}} {"users":{"email":"user3@test.com","id":3,"phone":"650-447-0007"}} {"users":{"email":"user3@test.com","id":3,"phone":"650-447-0008"}}
Example (SubscriptionWithCursor) ¶
// func TestSubCursor(t *testing.T) {
// query to fetch existing chat messages
// gql1 := `query {
// chats(first: 3, after: $cursor) {
// id
// body
// }
// chats_cursor
// }`
// query to subscribe to new chat messages
gql2 := `subscription {
chats(first: 1, after: $cursor) {
id
body
}
}`
conf := newConfig(&core.Config{
DBType: dbType,
DisableAllowList: true,
SubsPollDuration: 1,
SecretKey: "not_a_real_secret",
})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
// struct to hold the cursor value from fetching the existing
// chat messages
// res := struct {
// Cursor string `json:"chats_cursor"`
// }{}
// execute query for existing chat messages
// m1, err := gj.GraphQL(context.Background(), gql1, nil, nil)
// if err != nil {
// fmt.Println(err)
// return
// }
// extract out the cursor `chats_cursor` to use in the subscription
// if err := json.Unmarshal(m1.Data, &res); err != nil {
// fmt.Println(err)
// return
// }
// replace cursor value to make test work since it's encrypted
// v1 := cursorRegex.ReplaceAllString(string(m1.Data), "cursor_was_here")
// fmt.Println(v1)
// create variables with the previously extracted cursor value to
// pass to the new chat messages subscription
// vars := json.RawMessage(`{ "cursor": "` + res.Cursor + `" }`)
vars := json.RawMessage(`{ "cursor": null }`)
// subscribe to new chat messages using the cursor
m2, err := gj.Subscribe(context.Background(), gql2, vars, nil)
if err != nil {
fmt.Println(err)
return
}
go func() {
for i := 6; i < 20; i++ {
// insert a new chat message
if dbType == "mongodb" {
// MongoDB: use JSON DSL for insert
q := fmt.Sprintf(`{"operation":"insertOne","collection":"chats","document":{"_id":%d,"body":"New chat message %d"}}`, i, i)
if _, err := db.Exec(q); err != nil {
panic(err)
}
} else {
// SQL databases: use SQL insert
q := fmt.Sprintf(`INSERT INTO chats (id, body) VALUES (%d, 'New chat message %d')`, i, i)
if _, err := db.Exec(q); err != nil {
panic(err)
}
}
time.Sleep(3 * time.Second)
}
}()
msgCount := 0
maxRetries := 50 // prevent infinite loop
for retries := 0; msgCount < 19 && retries < maxRetries; retries++ {
msg := <-m2.Result
// replace cursor value to make test work since it's encrypted
v2 := cursorRegex.ReplaceAllString(string(msg.Data), `cursor":"cursor_was_here`)
// Skip empty results (timing issue on some databases like MSSQL)
if strings.Contains(v2, `"chats":[]`) {
continue
}
printJSON([]byte(v2))
msgCount++
}
Output: {"chats":[{"body":"This is chat message number 1","id":1}],"chats_cursor":"cursor_was_here"} {"chats":[{"body":"This is chat message number 2","id":2}],"chats_cursor":"cursor_was_here"} {"chats":[{"body":"This is chat message number 3","id":3}],"chats_cursor":"cursor_was_here"} {"chats":[{"body":"This is chat message number 4","id":4}],"chats_cursor":"cursor_was_here"} {"chats":[{"body":"This is chat message number 5","id":5}],"chats_cursor":"cursor_was_here"} {"chats":[{"body":"New chat message 6","id":6}],"chats_cursor":"cursor_was_here"} {"chats":[{"body":"New chat message 7","id":7}],"chats_cursor":"cursor_was_here"} {"chats":[{"body":"New chat message 8","id":8}],"chats_cursor":"cursor_was_here"} {"chats":[{"body":"New chat message 9","id":9}],"chats_cursor":"cursor_was_here"} {"chats":[{"body":"New chat message 10","id":10}],"chats_cursor":"cursor_was_here"} {"chats":[{"body":"New chat message 11","id":11}],"chats_cursor":"cursor_was_here"} {"chats":[{"body":"New chat message 12","id":12}],"chats_cursor":"cursor_was_here"} {"chats":[{"body":"New chat message 13","id":13}],"chats_cursor":"cursor_was_here"} {"chats":[{"body":"New chat message 14","id":14}],"chats_cursor":"cursor_was_here"} {"chats":[{"body":"New chat message 15","id":15}],"chats_cursor":"cursor_was_here"} {"chats":[{"body":"New chat message 16","id":16}],"chats_cursor":"cursor_was_here"} {"chats":[{"body":"New chat message 17","id":17}],"chats_cursor":"cursor_was_here"} {"chats":[{"body":"New chat message 18","id":18}],"chats_cursor":"cursor_was_here"} {"chats":[{"body":"New chat message 19","id":19}],"chats_cursor":"cursor_was_here"}
Example (Update) ¶
gql := `mutation {
products(id: $id, update: $data) {
id
name
}
}`
vars := json.RawMessage(`{
"id": 100,
"data": {
"name": "Updated Product 100",
"description": "Description for updated product 100"
}
}`)
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
ctx := context.WithValue(context.Background(), core.UserIDKey, 3)
res, err := gj.GraphQL(ctx, gql, vars, nil)
if err != nil {
logSnowflakeQueryFailure(gj, gql, vars, err)
fmt.Println(err)
} else {
printJSON(res.Data)
}
// Cleanup: restore product 100 to original state
_, _ = db.Exec(`UPDATE products SET name = 'Product 100', description = 'Description for product 100' WHERE id = 100`)
Output: {"products":{"id":100,"name":"Updated Product 100"}}
Example (UpdateMultipleRelatedTables1) ¶
gql := `mutation {
purchases(id: $id, update: $data) {
quantity
customer {
full_name
}
product {
description
}
}
}`
vars := json.RawMessage(`{
"id": 100,
"data": {
"quantity": 6,
"customer": {
"full_name": "Updated user related to purchase 100"
},
"product": {
"description": "Updated product related to purchase 100"
}
}
}`)
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true, Debug: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
ctx := context.WithValue(context.Background(), core.UserIDKey, 3)
res, err := gj.GraphQL(ctx, gql, vars, nil)
if err != nil {
logSnowflakeQueryFailure(gj, gql, vars, err)
fmt.Println(err)
} else {
printJSON(res.Data)
}
// Cleanup: restore data to original state
_, _ = db.Exec(`UPDATE purchases SET quantity = 1000 WHERE id = 100`)
_, _ = db.Exec(`UPDATE users SET full_name = 'User 1' WHERE id = 1`)
_, _ = db.Exec(`UPDATE products SET description = 'Description for product 100' WHERE id = 100`)
Output: {"purchases":{"customer":{"full_name":"Updated user related to purchase 100"},"product":{"description":"Updated product related to purchase 100"},"quantity":6}}
Example (UpdateTableAndConnectToRelatedTables) ¶
gql := `mutation {
users(id: $id, update: $data) {
full_name
products {
id
}
}
}`
vars := json.RawMessage(`{
"id": 100,
"data": {
"full_name": "Updated user 100",
"products": {
"connect": { "id": 99 },
"disconnect": { "id": 100 }
}
}
}`)
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
ctx := context.WithValue(context.Background(), core.UserIDKey, 3)
res, err := gj.GraphQL(ctx, gql, vars, nil)
if err != nil {
logSnowflakeQueryFailure(gj, gql, vars, err)
fmt.Println(err)
} else {
printJSON(res.Data)
}
// Cleanup: restore product ownership to original state
_, _ = db.Exec(`UPDATE products SET owner_id = 99 WHERE id = 99`)
_, _ = db.Exec(`UPDATE products SET owner_id = 100 WHERE id = 100`)
_, _ = db.Exec(`UPDATE users SET full_name = 'User 100' WHERE id = 100`)
Output: {"users":{"full_name":"Updated user 100","products":[{"id":99}]}}
Example (UpdateTableAndRelatedTable) ¶
gql := `mutation {
users(id: $id, update: $data) {
full_name
products {
id
}
}
}`
vars := json.RawMessage(`{
"id": 90,
"data": {
"full_name": "Updated user 90",
"products": {
"where": { "id": { "gt": 1 } },
"name": "Updated Product 90"
}
}
}`)
conf := newConfig(&core.Config{DBType: dbType, DisableAllowList: true})
gj, err := core.NewGraphJin(conf, db)
if err != nil {
panic(err)
}
ctx := context.WithValue(context.Background(), core.UserIDKey, 3)
res, err := gj.GraphQL(ctx, gql, vars, nil)
if err != nil {
logSnowflakeQueryFailure(gj, gql, vars, err)
fmt.Println(err)
} else {
printJSON(res.Data)
}
// Cleanup: restore data to original state
_, _ = db.Exec(`UPDATE users SET full_name = 'User 90' WHERE id = 90`)
_, _ = db.Exec(`UPDATE products SET name = 'Product 90' WHERE id = 90`)
Output: {"users":{"full_name":"Updated user 90","products":[{"id":90}]}}