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.modfile 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 the
go.modfile. 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.modfile. 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.
go.modfile also defines the module path (which is
greenlight.alexedwards.netin 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.
bindirectory will contain our compiled application binaries, ready for deployment to a production server.
cmd/apidirectory 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.
internaldirectory 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 under
cmd/apiwill import the packages in the
internaldirectory (but never the other way around).
migrationsdirectory will contain the SQL migration files for our database.
remotedirectory will contain the configuration files and setup scripts for our production server.
go.modfile will declare our project dependencies, versions and module path.
Makefilewill 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.
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: