Article
· 6 hr ago 31m read

Bringing It All Together: Go, GORM, and InterSystems IRIS in Action

Since we reached two important milestones for Go developers working with InterSystems IRIS:

Now it’s time to see everything working together.

To demonstrate how easily Go developers can adopt InterSystems IRIS, I took an existing production-grade open-source project — the RealWorld Example App — which showcases a full-stack Medium.com-style clone implemented with Go Fiber, GORM, and SQLite.

RealWorld Example App

With just a few configuration tweaks, I swapped out SQLite for gorm-iris, keeping everything else unchanged. The result?
A fully functional Go + Fiber application powered by InterSystems IRIS — no code rewrites, no ORM gymnastics, just a different database backend.

You can find the complete working demo here: github.com/caretdev/golang-fiber-iris-realworld-example-app

Getting Started

Let’s run the demo project.


1. Clone the Project

git clone git@github.com:caretdev/golang-fiber-iris-realworld-example-app.git 
cd golang-fiber-iris-realworld-example-app

2. Download Dependencies and Generate Swagger Docs

Install Go dependencies and generate the API documentation:

go mod download 
go install github.com/swaggo/swag/cmd/swag@latest 
go generate .

This will:

  • Download all required Go modules.
  • Install the swag tool for generating Swagger documentation.
  • Execute the Go generate command, which rebuilds Swagger definitions from annotations in the codebase.

After running this, you should see the generated documentation files under the docs/ directory.

3. Database Setup and Testing

The project includes a db.go file, which defines the logic for initializing a database connection.
To simplify testing and ensure a clean environment, we’re using testcontainers-iris-go — it spins up a fresh InterSystems IRIS container for each test run.

This means every test starts with an empty, isolated IRIS instance — ideal for reliable automated testing.

Here’s the core part of the code that makes it work:

var container *iriscontainer.IRISContainer

func TestDB(useContainer bool) *gorm.DB {
	var err error
	var connectionString = "iris://_SYSTEM:SYS@localhost:1972/USER"
	if useContainer {
		options := []testcontainers.ContainerCustomizer{
			iriscontainer.WithNamespace("TEST"),
			iriscontainer.WithUsername("testuser"),
			iriscontainer.WithPassword("testpassword"),
		}
		ctx := context.Background()
		container, err = iriscontainer.RunContainer(ctx, options...)
		if err != nil {
			log.Println("Failed to start container:", err)
			os.Exit(1)
		}
		connectionString = container.MustConnectionString(ctx)
		fmt.Println("Container started: ", connectionString)
	}

	newLogger := logger.New(
		log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
		logger.Config{
			SlowThreshold: time.Second,  // Slow SQL threshold
			LogLevel:      logger.Error, // Log level
			Colorful:      true,         // Disable color
		},
	)

	db, err := gorm.Open(iris.New(iris.Config{
		DSN: connectionString,
	}), &gorm.Config{
		Logger: newLogger,
	})
	if !useContainer {
		_ = db.Exec("DROP DATABASE TEST").Error
		_ = db.Exec("CREATE DATABASE TEST").Error
		_ = db.Exec("USE DATABASE TEST").Error
	}

	if err != nil {
		fmt.Println("storage err: ", err)
	}
	return db
}

func DropTestDB() error {
	if container != nil {
		container.Terminate(context.Background())
	}
	container = nil
	return nil
}

func AutoMigrate(db *gorm.DB) {
	db.AutoMigrate(
		&model.User{},
		&model.Follow{},
		&model.Article{},
		&model.Comment{},
		&model.Tag{},
	)
}

Using containerized IRIS is set under a cli flag and set in a handler_test.go file

var (
	useContainer bool
)

func TestMain(m *testing.M) {
	flag.BoolVar(&useContainer, "container", true, "Use container image.")
	flag.Parse()
	// setup()
	code := m.Run()
	// tearDown()
	os.Exit(code)
}

func setup() {
	d = db.TestDB(useContainer)
	db.AutoMigrate(d)
	us = store.NewUserStore(d)
	as = store.NewArticleStore(d)
	h = NewHandler(us, as)
	e = router.New()
	loadFixtures()
}

How It Works

  • When useContainer is true, the function launches a new IRIS container via testcontainers-iris-go.
    • It creates a clean environment with custom credentials (testuser / testpassword) and a namespace called TEST.
    • The connection string is retrieved automatically via container.MustConnectionString(ctx).
  • When running locally without containers, the code connects to a pre-existing IRIS instance and ensures the TEST database is recreated before tests run.
  • AutoMigrate() automatically creates all required tables using the models defined in the project (User, Article, Comment, etc.).

4. Run the Tests

Once the database configuration is in place, you can execute all tests using:

go test ./handler -v

flag -container or -container=0 can be added to swich the way of testing

This command will:

  • Build and run all Go tests in verbose mode.
  • Start a new IRIS container for each test (if enabled).
  • Automatically apply migrations and clean up after completion.

If everything is configured correctly, you’ll see log output similar to:

 
Tests result

5. Running the Application with Docker Compose

After confirming that tests pass successfully, the next step is to run the complete Fiber + GORM + IRIS application using Docker.
In this setup, the Go backend is built into a binary, copied into the IRIS container, and started automatically alongside IRIS itself.

This approach makes deployment extremely simple — you can spin up everything with one command.


Updated docker-compose.yml

The project’s modified docker-compose.yml now defines an iris service that handles both:

  • Running the InterSystems IRIS database.
  • Starting the Go Fiber application as part of the same container.

Here’s the key idea:

  • The Go app is built using a multistage Docker build.
  • The resulting binary is copied into the IRIS image.
  • A small init script is included to automatically start the Go app when IRIS launches.

This gives you a self-contained container — a single image that runs both IRIS and your Go web API in perfect sync.


Build and Start the Application

To build the IRIS image and launch the environment, simply run:

docker compose up -d iris --build

This will:

  1. Build your Go application.
  2. Create a new IRIS-based Docker image containing the app binary and initialization scripts.
  3. Start the container in detached mode (-d), with IRIS and the Go API running together.

💡 Note:
One of the beautiful aspects of Go is that you’re not limited to embedding your application inside the IRIS container.
Thanks to Go’s single-binary compilation, you can easily build a standalone Docker image for your application — one that connects to InterSystems IRIS over the network using the same connection string.

This approach has two big advantages:

  • The resulting image is much smaller, often under 30 MB.
  • It cleanly separates application logic from database infrastructure, making it ideal for microservices or cloud deployments.

In production, you can keep IRIS and your Go service in separate containers (or even separate hosts), connected securely through your network — combining IRIS’s reliability with Go’s portability.

6. End-to-End API Testing with Newman

Once your Go + IRIS application is up and running, it’s time to verify that the REST API behaves exactly as expected.
The project includes an additional layer of integration tests — using the Postman / Newman collection from the original RealWorld project.

This ensures that the backend fully complies with the RealWorld API specification and that all endpoints work correctly when backed by InterSystems IRIS.


Newman as a Service

To make testing seamless, the docker-compose.yml file defines an extra service named newman-checker.
This service runs the Newman CLI inside a container, executes the full RealWorld API test collection, and connects internally to the running Fiber + IRIS application.

Because both services run inside the same Docker network, the tests can access the backend directly without any extra configuration.


Run the API Tests

To execute the end-to-end tests, simply run:

docker compose run newman-checker

This will:

  1. Start the Newman container.
  2. Connect it to the running iris service.
  3. Execute the entire RealWorld API test suite against your Go + IRIS backend.

If everything is set up correctly, you should see a summary like:

 
Newman tests result

What This Confirms

Passing the Newman suite means your Go + Fiber + GORM + IRIS stack is fully compatible with the RealWorld API spec —
a great indicator that your backend logic, ORM integration, and database connectivity all work as expected.

It’s not just a demo anymore — it’s a production-grade, spec-compliant backend, powered by InterSystems IRIS.

7. Explore the API with Swagger UI

Once everything is up and running, you can finally explore the live REST API through a clean, interactive interface.
The project comes with a pre-generated Swagger UI, making it easy to inspect and test endpoints directly in your browser.

After starting the application, open:

👉 http://localhost:8585/swagger/index.html

You’ll see the full RealWorld API specification — all endpoints powered by your Go Fiber backend, connected through GORM to InterSystems IRIS.

From here, you can:

  • Register and log in users
  • Create, edit, and delete articles
  • Post comments
  • Follow or unfollow users

All the requests you send are processed in real time by IRIS, through the new Go driver and ORM integration.

What’s Next

You’ve now built and run a complete Go + Fiber + GORM + IRIS backend —
with automated tests, Swagger documentation, and containerized deployment.

From here, you can:

  • Extend the app with your own features.
  • Deploy IRIS and your Go app as separate services for scalability.
  • Experiment with advanced IRIS capabilities — globals, analytics, and interoperability.

This demo shows that InterSystems IRIS can be a first-class citizen in the modern Go ecosystem —
powerful, fast, and ready for cloud-native applications.


This project is participating in the contest, please vote here.

Discussion (0)1
Log in or sign up to continue