SkillAgentSearch skills...

Goxgen

Create your project based on GraphQL SDL

Install / Use

/learn @goxgen/Goxgen
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

goxgen

GitHub license GitHub stars GitHub release (latest by date) Go Report Card codecov

Your One-Stop Solution for GraphQL Application Generation

<p style="text-align: center"> <img src="docs/images/logo.png" width="200px" alt=""> </p>

goxgen is a powerful library designed to simplify the creation of GraphQL applications. By defining your domain and API interface through a single syntax, You can quickly generate a fully-functional GraphQL server. Beyond that, goxgen also provides support for ORM(GORM) and a Command-Line Interface for server operations.

Built upon the gqlgen framework, goxgen extends its capabilities to offer a more streamlined developer experience.

🌟 Features

  • 📝 Single Syntax for Domain and API: Define your domain and API interface in GraphQL schema language.
  • 📊 GraphQL: Schema-based application generation
  • 🎛️ ORM Support: Seamlessly integrates with various ORM systems like GORM and ENT.
  • ⚙️ CLI Support: Comes with a CLI tool to spin up your server application in no time.
  • 📚 Domain Driven Design: Extensible project structure
  • 🛡️ Future-Ready: Plans to roll out UI for admin back-office, along with comprehensive authentication and authorization features.

Schema definition

goxgen using a directives for business logic and domain definition.

All schema files in xgen has this format schema.{some_name}.graphql, for example schema.user.graphql

Resource directives

Resource directives is a main directives for domain resource definition.

  • @Resource - Your domain resource
  • @Field - Field of resource

Action Directives

  • @Action - Action that can be done for single resource
  • @ListAction - Action that can be done for bulk resources
  • @ActionField - Field of action or list action

🚀 Quick Start

👣 Step-by-step guide

📄 Creating the necessary files

You should create two files in your project

  1. Standard gen.go file with go:generate directive
    package main
    
    //go:generate go run -mod=mod github.com/goxgen/goxgen
    
    
  2. Xgen config file xgenc.go
    //go:build ignore
    // +build ignore
    
    package main
    
    import (
    	"context"
    	"fmt"
    	"github.com/goxgen/goxgen/plugins/cli"
    	"github.com/goxgen/goxgen/projects/basic"
    	"github.com/goxgen/goxgen/projects/gorm"
    	"github.com/goxgen/goxgen/xgen"
    )
    
    func main() {
    	xg := xgen.NewXgen(
    		xgen.WithPackageName("github.com/goxgen/goxgen/cmd/internal/integration"),
    		xgen.WithProject(
    			"myproject",
    			basic.NewProject(),
    		),
    		xgen.WithProject(
    			"gorm_advanced",
    			gorm.NewProject(
    				gorm.WithBasicProjectOption(basic.WithTestDir("tests")),
    			),
    		),
    		xgen.WithProject(
    			"gorm_example",
    			gorm.NewProject(
    				gorm.WithBasicProjectOption(basic.WithTestDir("tests")),
    			),
    		),
    		xgen.WithPlugin(cli.NewPlugin()),
    	)
    
    	err := xg.Generate(context.Background())
    	if err != nil {
    		fmt.Println(err)
    	}
    }
    
    

Then run go generate command, and goxgen will generate project structure

go generate

📁 Structure of a generated project

After running go generate command, goxgen will generate project structure like this

├── gorm_advanced
│   ├── generated
│   │   ├── server
│   │   ├── generated_gqlgen.go
│   │   ├── generated_gqlgen_models.go
│   │   ├── generated_xgen_directives.graphql
│   │   ├── generated_xgen_gorm.go
│   │   ├── generated_xgen_introspection.go
│   │   ├── generated_xgen_introspection.graphql
│   │   ├── generated_xgen_mappers.go
│   │   └── generated_xgen_sortable.go
│   ├── tests
│   │   ├── default-tests.yaml
│   │   ├── user-lifecycle.yaml
│   │   └── user-pagination.yaml
│   ├── graphql.config.yml
│   ├── resolver.go
│   ├── schema.main.graphql
│   └── schema.resolver.go
├── gorm_example
│   ├── generated
│   │   ├── server
│   │   ├── generated_gqlgen.go
│   │   ├── generated_gqlgen_models.go
│   │   ├── generated_xgen_directives.graphql
│   │   ├── generated_xgen_gorm.go
│   │   ├── generated_xgen_introspection.go
│   │   ├── generated_xgen_introspection.graphql
│   │   ├── generated_xgen_mappers.go
│   │   └── generated_xgen_sortable.go
│   ├── tests
│   │   ├── default-tests.yaml
│   │   └── user-lifecycle.yaml
│   ├── graphql.config.yml
│   ├── resolver.go
│   ├── schema.phone.graphql
│   ├── schema.resolver.go
│   └── schema.user.graphql
├── myproject
│   ├── generated
│   │   ├── server
│   │   ├── generated_gqlgen.go
│   │   ├── generated_gqlgen_models.go
│   │   ├── generated_xgen_directives.graphql
│   │   ├── generated_xgen_introspection.go
│   │   ├── generated_xgen_introspection.graphql
│   │   ├── generated_xgen_mappers.go
│   │   └── generated_xgen_sortable.go
│   ├── tests
│   │   └── default-tests.yaml
│   ├── graphql.config.yml
│   ├── resolver.go
│   ├── schema.main.graphql
│   ├── schema.resolver.go
│   ├── schema.todo.graphql
│   └── schema.users.graphql
├── .env
├── .env.default
├── .gitignore
├── gen.go
├── generated_xgen_cli.go
├── gorm_advanced.db
├── gorm_example.db
├── gormproj.db
└── xgenc.go

Note: generated directories can be ignored in git. But you can add it to git if you want.

📑 Providing schema

Check the schema definition section for more information.

You should provide a schema for each project and run go generate again.

Gorm example

Let's focus on gorm_example, which uses the GORM ORM. The connection to the GORM database can be configured from the gqlgen standard resolver.go file in the gorm_example directory.

resolver.go is designed to support your custom dependency injection (DI) and any services you've provided.

package gorm_example

import (
	"github.com/goxgen/goxgen/cmd/internal/integration/gorm_example/generated"
	"github.com/goxgen/goxgen/plugins/cli/settings"
	"gorm.io/gorm"
	"embed"
	"fmt"
)

//go:embed tests/*
var TestsFS embed.FS

type Resolver struct {
	DB *gorm.DB
}

func NewResolver(sts *settings.EnvironmentSettings) (*Resolver, error) {
	r := &Resolver{}
	db, err := generated.NewGormDB(sts)
	if err != nil {
		return nil, fmt.Errorf("failed to create gorm db: %w", err)
	}
	r.DB = db

	return r, nil
}

Creating a example schema for resources

schema.user.graphql

# Define the User resource(entity) and its fields
# Enable DB mapping for the resource
type User
@Resource(Name: "user", DB: {Table: "user"})
{
    id: ID! @Field(Label: "ID", DB: {Column: "id", PrimaryKey: true})
    name: String! @Field(Label: "Text", DB: {Column: "name", Unique: true})
    phoneNumbers: [Phone!]! @Field(Label: "Phone Numbers", DB: {})
}

# User input type for create and update actions
# Define the actions for the resource
input UserInput
@Action(Resource: "user", Action: CREATE_MUTATION, Route: "new")
@Action(Resource: "user", Action: UPDATE_MUTATION, Route: "update")
{
    id: ID @ActionField(Label: "ID", MapTo: ["User.ID"])
    name: String @ActionField(Label: "Name", MapTo: ["User.Name"])
    phones: [PhoneNumberInput!] @ActionField(Label: "Phone Numbers", MapTo: ["User.PhoneNumbers"])
}

# User input type for browse action
input BrowseUserInput
@ListAction(Resource: "user", Action: BROWSE_QUERY, Route: "list", Pagination: true, Sort: {Default: [{by: "name", direction: ASC}]})
{
    id: ID @ActionField(Label: "ID", MapTo: ["User.ID"])
    name: String @ActionField(Label: "Name", MapTo: ["User.Name"])
}

schema.phone.graphql

type Phone
@Resource(Name: "phone_number",  DB: {Table: "phone_number"})
{
    id: ID! @Field(Label: "ID", DB: {Column: "id", PrimaryKey: true})
    number: String! @Field(Label: "Number", DB: {Column: "number"})
    user: User! @Field(Label: "User", DB: {})
}

input PhoneNumberInput
@Action(Resource: "phone_number", Action: CREATE_MUTATION, Route: "new")
@Action(Resource: "phone_number", Action: UPDATE_MUTATION, Route: "update")
{
    id: ID @ActionField(Label: "ID", MapTo: ["Phone.ID"])
    number: String @ActionField(Label: "Name", MapTo: ["Phone.Number"])
    user: UserInput @ActionField(Label: "User", MapTo: ["Phone.User"])
}

After writing a custom schema You should run again gogen command.

go generate

After regenerating the code, the schema.resolver.go file will be updated based on your schema. You can find the resolver functions for each field in the schema.resolver.go file.

"Create User" mutation resolver

func (r *mutationResolver) UserCreate(ctx context.Context, input *generated.UserInput) (*generated.User, error) {
	u, err := input.ToUserModel(ctx)
	if err != nil {
		return nil, err
	}
	res := r.DB.Preload(clause.Associations).Create(u)
	if res.Error != nil {
		return nil, res.Error
	}
	return u, nil
}

"Browse User" query resolver

func (r *queryResolver) UserBrowse(ctx context.Context, where *generated.BrowseUserInput, pagination *generated.XgenPaginationInput, sort *generated.UserSortInput) ([]*generated.User, error) {
	var users []*generated.User
	u, err := where.ToUserModel(ctx)
	if err != nil {
		return nil, err
	}
	res := r.DB.
		Preload(clause.Associations).
		Scopes(
			generated.Paginate(pagination), // passing `pagination` to the xgen `generated.Paginate` scope
			generated.Sort(sort),           // passing `sort` to the xgen `generated.Sort` scope
		).
		Where(&[]*generated.User{u}).
		Find(&user

Related Skills

View on GitHub
GitHub Stars38
CategoryDevelopment
Updated9mo ago
Forks2

Languages

Go

Security Score

87/100

Audited on Jun 11, 2025

No findings