Project Setup and Skeleton Structure
Let’s kick things off by creating a greenlight
directory to act as the top-level ‘home’ for this project. I’m going to create my project directory at $HOME/Projects/greenlight
, but feel free to choose a different location if you wish.
Then change into this directory and use the go mod init
command to enable modules for the project.
When running this command you’ll need to specify a module path, which is essentially a unique identifier for your project. In this book I’ll use greenlight.alexedwards.net
as my module path, but if you’re following along you should ideally swap this for something that is unique to you instead.
At this point you’ll see that a go.mod
file has been created in the root of your project directory. If you open it up, it should look similar to this:
We talked about modules in detail as part of the first Let’s Go book, but as a quick refresher let’s recap the main points here.
- When there is a valid
go.mod
file in the root of your project directory, your project is a module. - When you’re working inside your project directory and download a dependency with
go get
, then the exact version of the dependency will be recorded in thego.mod
file. Because the exact version is known, this makes it much easier to ensure reproducible builds across different machines and environments. - When you run or build the code in your project, Go will use the exact dependencies listed in the
go.mod
file. If the necessary dependencies aren’t already on your local machine, then Go will automatically download them for you — along with any recursive dependencies too. - The
go.mod
file also defines the module path (which isgreenlight.alexedwards.net
in my case). This is essentially the identifier that will be used as the root import path for the packages in your project. - It’s good practice to make the module path unique to you and your project. A common convention in the Go community is to base it on a URL that you own.
Generating the skeleton directory structure
Alright, now that our project directory has been created and we have a go.mod
file, you can go ahead and run the following commands to generate a high-level skeleton structure for the project:
At this point your project directory should look exactly like this:
Let’s take a moment to talk through these files and folders and explain the purpose that they’ll serve in our finished project.
- The
bin
directory will contain our compiled application binaries, ready for deployment to a production server. - The
cmd/api
directory will contain the application-specific code for our Greenlight API application. This will include the code for running the server, reading and writing HTTP requests, and managing authentication. - The
internal
directory will contain various ancillary packages used by our API. It will contain the code for interacting with our database, doing data validation, sending emails and so on. Basically, any code which isn’t application-specific and can potentially be reused will live in here. Our Go code undercmd/api
will import the packages in theinternal
directory (but never the other way around). - The
migrations
directory will contain the SQL migration files for our database. - The
remote
directory will contain the configuration files and setup scripts for our production server. - The
go.mod
file will declare our project dependencies, versions and module path. - The
Makefile
will contain recipes for automating common administrative tasks — like auditing our Go code, building binaries, and executing database migrations.
It’s important to point out that the directory name internal
carries a special meaning and behavior in Go: any packages which live under this directory can only be imported by code inside the parent of the internal
directory. In our case, this means that any packages which live in internal
can only be imported by code inside our greenlight
project directory.
Or, looking at it the other way, this means that any packages under internal
cannot be imported by code outside of our project.
This is useful because it prevents other codebases from importing and relying on the (potentially unversioned and unsupported) packages in our internal
directory — even if the project code is publicly available somewhere like GitHub.
Hello world!
Before we continue, let’s quickly check that everything is setup correctly. Open the cmd/api/main.go
file in your text editor and add the following code:
Save this file, then use the go run
command in your terminal to compile and execute the code in the cmd/api
package. All being well, you will see the following output: