{"id":13514269,"url":"https://github.com/Appointy/Jaal","last_synced_at":"2025-03-31T03:30:24.654Z","repository":{"id":40267678,"uuid":"194983583","full_name":"Appointy/Jaal","owner":"Appointy","description":"Develop spec compliant GraphQL servers","archived":false,"fork":false,"pushed_at":"2023-03-04T02:03:31.000Z","size":579,"stargazers_count":77,"open_issues_count":10,"forks_count":9,"subscribers_count":7,"default_branch":"master","last_synced_at":"2024-10-30T08:34:42.400Z","etag":null,"topics":["go","golang","graphql","graphql-api","graphql-schema","graphql-schema-stitching","graphql-server","relay-server"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Appointy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"code-of-conduct.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-07-03T05:10:56.000Z","updated_at":"2024-06-06T15:02:17.000Z","dependencies_parsed_at":"2024-06-18T23:25:34.921Z","dependency_job_id":"9b9d5100-f42f-4c79-b790-5fafab22352c","html_url":"https://github.com/Appointy/Jaal","commit_stats":{"total_commits":147,"total_committers":5,"mean_commits":29.4,"dds":"0.19727891156462585","last_synced_commit":"c4879e2dcb1d93667dc3b77304c37dc5a00b94b3"},"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Appointy%2FJaal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Appointy%2FJaal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Appointy%2FJaal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Appointy%2FJaal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Appointy","download_url":"https://codeload.github.com/Appointy/Jaal/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":222615399,"owners_count":17012073,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["go","golang","graphql","graphql-api","graphql-schema","graphql-schema-stitching","graphql-server","relay-server"],"created_at":"2024-08-01T05:00:51.135Z","updated_at":"2024-11-01T17:31:18.591Z","avatar_url":"https://github.com/Appointy.png","language":"Go","readme":"# Jaal - Develop spec compliant GraphQL servers\n\nJaal is a go framework for building spec compliant GraphQL servers. Jaal has support for all the graphql scalar types and builds the schema from registered objects using reflection. Jaal is inspired from Thunder by Samsara.\n\n## Features\n\n* In-built support for graphQL scalars\n* In-built support for maps\n* Custom Scalar registration\n* Input, Payload and enum registrations\n* Custom field registration on objects\n* Interface Support\n* Union Support\n* In build include and skip directives\n* Protocol buffers API generation\n\n## Getting Started\n\nThe following example depicts how to build a simple graphQL server using jaal.\n\n``` Go\npackage main\n\nimport (\n    \"context\"\n    \"log\"\n    \"net/http\"\n\n    \"github.com/google/uuid\"\n    \"go.appointy.com/jaal\"\n    \"go.appointy.com/jaal/introspection\"\n    \"go.appointy.com/jaal/schemabuilder\"\n)\n\ntype Server struct {\n    Characters []*Character\n}\n\ntype Character struct {\n    Id   string\n    Name string\n    Type Type\n}\n\ntype Type int32\n\nconst (\n    WIZARD Type = iota\n    MUGGLE\n    GOBLIN\n    HOUSE_ELF\n)\n\ntype CreateCharacterRequest struct {\n    Name string\n    Type Type\n}\n\nfunc RegisterPayload(schema *schemabuilder.Schema) {\n    payload := schema.Object(\"Character\", Character{})\n    payload.FieldFunc(\"id\", func(ctx context.Context, in *Character) *schemabuilder.ID {\n        return \u0026schemabuilder.ID{Value: in.Id}\n    })\n    payload.FieldFunc(\"name\", func(ctx context.Context, in *Character) string {\n        return in.Name\n    })\n    payload.FieldFunc(\"type\", func(ctx context.Context, in *Character) Type {\n        return in.Type\n    })\n}\n\nfunc RegisterInput(schema *schemabuilder.Schema) {\n    input := schema.InputObject(\"CreateCharacterRequest\", CreateCharacterRequest{})\n    input.FieldFunc(\"name\", func(target *Character, source string) {\n        target.Name = source\n    })\n    input.FieldFunc(\"type\", func(target *Character, source Type) {\n        target.Type = source\n    })\n}\n\nfunc RegisterEnum(schema *schemabuilder.Schema) {\n    schema.Enum(Type(0), map[string]interface{}{\n        \"WIZARD\":    Type(0),\n        \"MUGGLE\":    Type(0),\n        \"GOBLIN\":    Type(0),\n        \"HOUSE_ELF\": Type(0),\n    })\n}\n\nfunc (s *Server) RegisterOperations(schema *schemabuilder.Schema) {\n    schema.Query().FieldFunc(\"character\", func(ctx context.Context, args struct {\n        Id *schemabuilder.ID\n    }) *Character {\n        for _, ch := range s.Characters {\n            if ch.Id == args.Id.Value {\n                return ch\n            }\n        }\n\n        return nil\n    })\n\n    schema.Query().FieldFunc(\"characters\", func(ctx context.Context, args struct{}) []*Character {\n        return s.Characters\n    })\n\n    schema.Mutation().FieldFunc(\"createCharacter\", func(ctx context.Context, args struct {\n        Input *CreateCharacterRequest\n    }) *Character {\n        ch := \u0026Character{\n            Id:   uuid.Must(uuid.NewUUID()).String(),\n            Name: args.Input.Name,\n            Type: args.Input.Type,\n        }\n        s.Characters = append(s.Characters, ch)\n\n        return ch\n    })\n}\n\nfunc main() {\n    sb := schemabuilder.NewSchema()\n    RegisterPayload(sb)\n    RegisterInput(sb)\n    RegisterEnum(sb)\n\n    s := \u0026Server{\n        Characters: []*Character{{\n            Id:   \"015f13a5-cf9b-49d7-b457-6113bcf8fd56\",\n            Name: \"Harry Potter\",\n            Type: WIZARD,\n        }},\n    }\n\n    s.RegisterOperations(sb)\n    schema, err := sb.Build()\n    if err != nil {\n        log.Fatalln(err)\n    }\n\n    introspection.AddIntrospectionToSchema(schema)\n\n    http.Handle(\"/graphql\", jaal.HTTPHandler(schema))\n    log.Println(\"Running\")\n    if err := http.ListenAndServe(\":9000\", nil); err != nil {\n        panic(err)\n    }\n}\n```\n\nIn the above example, we registered all the operations, inputs \u0026 payloads on the schema. We also registered the fields we wanted to expose on the schema. FIeld registration allows us to control the way in which a field is exposed. Here we exposed the field Id of Character as the graphQL scalar ID.\n\n## Custom Scalar Registration\n\n```Go\ntyp := reflect.TypeOf(time.Time{})\nschemabuilder.RegisterScalar(typ, \"DateTime\", func(value interface{}, dest reflect.Value) error {\n    v, ok := value.(string)\n    if !ok {\n        return errors.New(\"invalid type expected string\")\n    }\n\n    t, err := time.Parse(time.RFC3339, v)\n    if err != nil {\n        return err\n    }\n\n    dest.Set(reflect.ValueOf(t))\n\n    return nil\n})\n```\n\n## Interface Registration\n\n```Go\ntype server struct {\n    dragons []Dragon\n    snakes  []Snake\n}\n\ntype Dragon struct {\n    Id           string\n    Name         string\n    NumberOfLegs int32\n}\n\ntype Snake struct {\n    Id             string\n    Name           string\n    LengthInMetres float32\n}\n\ntype MagicalCreature struct {\n    schemabuilder.Interface\n    *Dragon\n    *Snake\n}\n\nfunc (s *server) RegisterInterface(schema *schemabuilder.Schema) {\n    schema.Query().FieldFunc(\"magicalCreature\", func(ctx context.Context, args struct {\n        Id *schemabuilder.ID\n    }) *MagicalCreature {\n        for _, d := range s.dragons {\n            if d.Id == args.Id.Value {\n                return \u0026MagicalCreature{\n                    Dragon: \u0026d,\n                }\n            }\n        }\n\n        for _, sn := range s.snakes {\n            if sn.Id == args.Id.Value {\n                return \u0026MagicalCreature{\n                    Snake: \u0026sn,\n                }\n            }\n        }\n\n        return nil\n    })\n}\n\nfunc RegisterPayloads(schema *schemabuilder.Schema) {\n\tpayload := schema.Object(\"Dragon\", Dragon{})\n\tpayload.FieldFunc(\"id\", func(ctx context.Context, in *Dragon) schemabuilder.ID {\n\t\treturn schemabuilder.ID{Value: in.Id}\n\t})\n\tpayload.FieldFunc(\"name\", func(ctx context.Context, in *Dragon) string {\n\t\treturn in.Name\n\t})\n\tpayload.FieldFunc(\"numberOfLegs\", func(ctx context.Context, in *Dragon) int32 {\n\t\treturn in.NumberOfLegs\n\t})\n\n\tpayload = schema.Object(\"Snake\", Snake{})\n\tpayload.FieldFunc(\"id\", func(ctx context.Context, in *Snake) schemabuilder.ID {\n\t\treturn schemabuilder.ID{Value: in.Id}\n\t})\n\tpayload.FieldFunc(\"name\", func(ctx context.Context, in *Snake) string {\n\t\treturn in.Name\n\t})\n\tpayload.FieldFunc(\"lengthInMetres\", func(ctx context.Context, in *Snake) float32 {\n\t\treturn in.LengthInMetres\n\t})\n}\n\nfunc main() {\n\ts := server{\n\t\tdragons: []Dragon{\n\t\t\t{\n\t\t\t\tId:           \"01d823a8-fdcd-4d03-957c-7ca898e2e5fd\",\n\t\t\t\tName:         \"Norbert\",\n\t\t\t\tNumberOfLegs: 2,\n\t\t\t},\n\t\t},\n\t\tsnakes: []Snake{\n\t\t\t{\n\t\t\t\tId:             \"2631a997-7a73-45b2-a2fc-ae665a383da1\",\n\t\t\t\tName:           \"Nagini\",\n\t\t\t\tLengthInMetres: 1.23,\n\t\t\t},\n\t\t},\n\t}\n\n\tsb := schemabuilder.NewSchema()\n\tRegisterPayloads(sb)\n\ts.RegisterInterface(sb)\n\n\tschema := sb.MustBuild()\n\tintrospection.AddIntrospectionToSchema(schema)\n\n\thttp.Handle(\"/graphql\", jaal.HTTPHandler(schema))\n\tfmt.Println(\"Running\")\n\tif err := http.ListenAndServe(\":9000\", nil); err != nil {\n\t\tpanic(err)\n\t}\n}\n```\n\nThe object schemabuilder.Interface acts as a special marker. It indicates that the type is to be registered as an interface. Jaal automatically registers the common fields(Id, Name) of the objects(Dragon \u0026 Snake) as the fields of interface (MagicalCreature). While defining a struct for interface, one must remember that all the fields of that struct are anonymous.\n\n## Union Registration\n\nThe above example can be converted to a union by replacing schemabuilder.Interface with schemabuilder.Union and RegisterInterface() by RegisterUnion().\n\n```Go\ntype MagicalCreature struct {\n    schemabuilder.Union\n    *Dragon\n    *Snake\n}\n\nfunc (s *server) RegisterUnion(schema *schemabuilder.Schema) {\n    schema.Query().FieldFunc(\"magicalCreature\", func(ctx context.Context, args struct {\n        Id *schemabuilder.ID\n    }) *MagicalCreature {\n        for _, d := range s.dragons {\n            if d.Id == args.Id.Value {\n                return \u0026MagicalCreature{\n                    Dragon: \u0026d,\n                }\n            }\n        }\n\n        for _, sn := range s.snakes {\n            if sn.Id == args.Id.Value {\n                return \u0026MagicalCreature{\n                    Snake: \u0026sn,\n                }\n            }\n        }\n\n        return nil\n    })\n}\n```\n\n## protoc-gen-jaal - Develop relay compliant GraphQL servers\n\n[protoc-gen-jaal](https://github.com/appointy/protoc-gen-jaal) is a protoc plugin which is used to generate jaal APIs. The server built from these APIs is graphQL spec compliant as well as relay compliant. It also handles oneOf by registering it as a Union on the schema.\n\n## Contributing\n\nPlease read [CONTRIBUTING.md](CONTRIBUTING.md) for reporting bugs and issues, the process for submitting pull requests to us, and roadmap. This project has adopted [Contributor Covenant Code of Conduct](code-of-conduct.md).\n\n## Contributors\n\n* Souvik Mandal (mandalsouvik76@gmail.com) - Implemented protoc-gen-jaal for creating jaal APIs.\n* Bitan Paul (bitanpaul1@gmail.com) - Implemented relay compliant graphql subscriptions.\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details\n\n## Acknowledgments\n\n* **Samsara** - *Initial work* - [Thunder](https://github.com/samsarahq/thunder)\n","funding_links":[],"categories":["Go"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAppointy%2FJaal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FAppointy%2FJaal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAppointy%2FJaal/lists"}