1

I have a go file for a package that is becoming unwieldy, so I want to split it into multiple logical files.

I have the files currently split (and abridged) like so:

// node.go
package schema

import (
    "github.com/graphql-go/graphql"
    "github.com/graphql-go/relay"
)

var nodeDefinitions *relay.NodeDefinitions

func init() {
    nodeDefinitions = relay.NewNodeDefinitions(relay.NodeDefinitionsConfig{
        IDFetcher: func(id string, info graphql.ResolveInfo) interface{} {
            ...
        },
        TypeResolve: func(value interface{}, info graphql.ResolveInfo) *graphql.Object {
            // based on the type of the value, return GraphQLObjectType
            switch value.(type) {
            default:
                return testType //Depends on the test.go
            }
        },
    })
}


//root.go
package schema

import (
    "github.com/graphql-go/graphql"
)

var Schema graphql.Schema

func init() {
    queryType := graphql.NewObject(graphql.ObjectConfig{
        Name: "Query",
        Fields: graphql.Fields{
            "version": &graphql.Field{
                 Type: versionType, //Depends on testtype.go
                 ...
            },
            "node": nodeDefinitions.NodeField, //Depends on node.go
        },
    })

    Schema, _ = graphql.NewSchema(graphql.SchemaConfig{
        Query: queryType,
    })
}

//testtype.go
package schema

import (
    "github.com/graphql-go/graphql"
    "github.com/graphql-go/relay"
)

var testType *graphql.Object

func init() {
    testType = graphql.NewObject(graphql.ObjectConfig{
        ...
        Interfaces: []*graphql.Interface{
            nodeDefinitions.NodeInterface, //Depends on node.go
        },
    })
}

I then use the package in main.go:

result := graphql.Do(graphql.Params{
        Schema:        schema.Schema,
        RequestString: r.URL.Query()["query"][0],
    })

While the package builds correctly, it complains with an error when I run it:

panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x1 addr=0x0 pc=0x48e4b6]

goroutine 1 [running]:
test/testproject/vendor/github.com/graphql-go/graphql.(*Object).Error(0x0, 0xc082003380, 0x3c)
        C:/go/src/test/testproject/vendor/github.com/graphql-go/graphql/definition.go:440 +0x26
test/testproject/vendor/github.com/graphql-go/graphql.defineFieldMap(0xda48b0, 0xc08206c780, 0xc0820550e0, 0xda
        C:/go/src/test/testproject/vendor/github.com/graphql-go/graphql/definition.go:498 +0x532
test/testproject/vendor/github.com/graphql-go/graphql.(*Object).Fields(0xc08206c780, 0xc08206c780)
        C:/go/src/test/testproject/vendor/github.com/graphql-go/graphql/definition.go:416 +0x106
test/testproject/vendor/github.com/graphql-go/graphql.typeMapReducer(0xc082055110, 0xda4560, 0xc08206c780, 0x0,
        C:/go/src/test/testproject/vendor/github.com/graphql-go/graphql/schema.go:208 +0x79a
test/testproject/vendor/github.com/graphql-go/graphql.NewSchema(0xc08206c780, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
        C:/go/src/test/testproject/vendor/github.com/graphql-go/graphql/schema.go:67 +0x809
test/testproject/schema.init.2()
        C:/go/src/test/testproject/schema/root.go:37 +0x244
test/testproject/schema.init()
        C:/go/src/test/testproject/schema/version.go:37 +0x5b
main.init()
        C:/go/src/test/testproject/main.go:37 +0x42

This is because:

node.go depends on testtype.go
testtype.go depends on node.go
root.go depends on node.go and testtype.go

go build builds the files in the page by sorting the filenames alphabetically: node.go then root.go then testtype.go.

If I rename root.go to z.go, it builds and runs correctly. Besides renaming root.go to z.go which is less than ideal, is there any other way I can get this working in a scalable way?

F21
  • 32,163
  • 26
  • 99
  • 170

1 Answers1

2

You could use just one init() function and put everything in that.

Or if you want to stick with multiple init() functions in multiple .go files, then create one "master" init function that will be called init(), and rename other init functions, e.g. initA(), initB(), and call these from the master init in proper order:

func init() {
    initA()
    initB()
}

See related question+answer: What does lexical file name order mean?

Community
  • 1
  • 1
icza
  • 389,944
  • 63
  • 907
  • 827
  • Is using a master `init()` to call `initA()`, `initB()` etc considered idiomatic go? – F21 Jan 28 '16 at 05:42
  • If you have dependencies between your init functions, yes, this is a good way which also documents the dependency, and makes sure your build will not depend on how the build system lists/proceeds with different files of a package. – icza Jan 28 '16 at 05:43