Back to blogYouTube Video

Published October 29, 2025

Syncing projects with Google Drive - Part 2 (Config Files and Drive API)

Can't play the video or having issues? Here's the direct link.

AI Summary

In this second part of the Google Drive Syncing project, the developer focuses on refactoring the codebase for better organization and implementing a configuration system to manage which local folders are synchronized to the cloud.

Key Takeaways

  • Refactored business logic out of the `main` function and into a dedicated `drive` struct with a `connect` method to improve maintainability.
  • Introduced a `Config` struct to handle application settings via a `config.json` file, allowing users to specify a base Google Drive folder, global ignore lists (e.g., `node_modules`, `.git`), and specific local folder paths to sync.
  • Implemented a feature to optionally utilize `.gitignore` files within projects to automatically skip unnecessary files during upload.
  • Developed a utility function `addFolderNames` that automatically extracts the folder name from the absolute path using `filepath.Base` if no custom name is provided in the config.
  • Briefly explained the importance of the Go `context` package for managing concurrent tasks and cancellation in production applications.

Description

In this part, we'll set up a config file to read our requirements, and expand on the Drive API a bit more. Github: https://github.com/hassanaziz0012/drive-syncer-video Full Playlist: https://youtube.com/playlist?list=PLTGiYd8gFivjVHK2xB7TLf7XLW8rmKXRM&si=L97UpFIHgfHbvLMT LINKS Website: https://www.hassandev.me My Book: https://www.hassandev.me/designing-websites X / Twitter: https://x.com/nothassanaziz

Transcript

Auto-generated transcript
Alright guys, welcome back to this Go DriveSinker project playlist. This is part 2. In part 1, we basically built all of the OAuth connection and authorization stuff where we connected to the Google Drive API, grabbed the access token, saved it, so on and so forth, and just asked it to list the first 10 files in my Google Drive. And it worked perfectly fine, as you can see. it just connected to the API and saved the credential file the token.json over here and it gave me the first 10 files in my google drive awesome now in this video we're going to first of all do a bit of cleanup over here because I don't like my main function having so much so much of the business logic I tried to separate all of those things out into different files and functions and just organize everything better instead of having all of the business logic of my application in the main function. I just don't think that's a good practice. So we're going to do a bit of cleanup first and then hopefully if the video doesn't get too long we're going to also create a config struct and start to get configuration from the from a file let's just say from a configuration file where we will put stuff like hey which folders do I want to synchronize which folders or files do I want to ignore and so on and so forth right we'll put all those things into a config.json file and then our application will read that and then upload the files and folders that we want to upload based on those rules that we specify in the configuration all right so in order to do this right we need to first be able to handle that config file before we actually implement all of the file uploads and folders and stuff right so let's get started first of all what i want to do is go all the way down here and create a new struct and we'll call this drive and this will have two properties first of all it will have the config and it will have the API the API will just be a drive dot service pointer and config will be a new struct called config there we go I'll configure this in just a bit but for the drive for the drive struct let's create new function call it new drive this will take a config pointer and it will return a drive pointer as such there we go return drive and pass in the config over there and there we go now let's move this config to its own file over here call it config.go package main all of this is in package main now before we actually write the config file and everything let me finish the drive struct first so let's add a pointer receiver over here this will be called connect and it will take the force refresh boolean and also a context over here which will be a context dot context object now to actually implement this we can go up here to the main function and basically just copy all of the stuff that we have over here let's take all of this put it in here and say that the d.api equals service right d.api is just this drive.service field in this drive struct over here and we're just going to assign that to this service right that we get from drive.new service this will help clean up our main function as well and yeah that's all we need to do for now now we can just go up here to the main function again and we can just say drive drv equals new drive pass in a config i'm just going to say config equals a new config object and just pass that in over here because we haven't implemented the config stuff right now right so create the new drive run the connect function force refresh let's say true for now context let's pass in let's create a new context up here and it will just be context.background for now and boom there we go by the way if you guys don't know what context is used for in go you should probably go learn it because it is very useful especially in concurrent programs You can basically use context to cancel different tasks and just manage different tasks when they're being run in different go routines and stuff. So instead of context.background, you can also have context.withCancel, for instance. And what this will allow you to do is you can basically cancel a task if you want to, right? You could say something like, and this is a completely side lesson, by the way, but you could say something like context dot with cancel and this would return a context and also a cancel function. You could then what you could do is you could pass in this context into some other function I guess pass in the context and this other function would run inside a go routine let's say which means it's running concurrently and you have no control over it anymore. But in this main function you could run the cancel function and then this some other function would have to just stop its execution right there you can also add cleanup routines and stuff like that and yeah this is a very fascinating package to learn and to use i'm not going to go too deep into those things i'm just going to use context.background for now but you should know this is very useful to have anyway yeah learn the context package and go anyway this srv variable we're going to change this to drive.api and that's all we need to do to make this work. Let's quickly make sure that our stuff is running over here and it is perfect. As you can see it gave me the files that we need the files the first 10 files in my google drive over here. Now it didn't ask me to refresh my token by going to that browser url and everything right. Why because we've already saved the token over here in token dot JSON and until it expires We can just keep using it over here Once it does expire then we'll get an error and we'll need to refresh the token after that But for now this works. So with that done, let's continue implementing the config struct over here This is going to take a bunch of Fields over here. First of all, it's going to take a base folder which will be a string this will be where we store all of the projects that we upload and synchronize to google drive because look we use google drive to store all kinds of things right we don't want all of those things mixing up with our programming projects so we need one specific folder to store all of our programming projects in my case i'm just using this programming folder over here right which will hold all of my programming projects and that's what this base folder is going to do. So that's the first thing we need in our config. Then a list of folders which will be a list of pointers to the folder object and let's implement that as well real quick. Let's say folder which will be a struct. This will have a name which will be the token the folder name and then a path which is the absolute path to the folder on your local machine that we can then open up and look at the files inside that folder and up and then upload all of them to google drive okay then it's going to have a ignore which will be a string list this is basically all the files that we want to ignore and then it's going to also have the use git ignore over here which will be a boolean and then let's also add a struct tag over here let's say json use underscore git ignore just to tell the json package what the field name will be in the config file and this use git ignore will basically allow us to read the git ignore file in every single project and it will just grab all of the ignored files and folders from that git ignore file and just skip all of them in the upload so this will be very convenient to use because we don't have to specify all of the files and folders that we don't want to upload to google drive we can just add a git ignore file just like we do with all of our other projects right and in this case i don't want to upload the client.json. I don't want to upload token.json. And that's all we need to do, right? Now we can just use this git ignore file in this program as well, in this application we're building. And we don't need to specify individually which files and folders we want to ignore in every single project, right? So this is a very convenient little feature that we're going to build in this application. Now let's create a function, call it read config. This will take a file path, which will be a string and it will return a pointer to the config object. Let's say file and error equals OS dot open. Pass in the file path. Handle the error as well Let say log dot fatal f Cannot read config file at and then print the file path and then let also print the error as in both of those things over here and then obviously a new line at the end And of course, defer f.close. And then let's create a new config object by saying c equals a pointer to config. And then let's use the built-in JSON object, create a new decoder, pass in the file and say decode, pass in c over here, the config. And then we can return c over here. And by the way, this is the beauty of using JSON as the configuration language over here. you will recall if you remember our like if you watch the redis clone that i built in go in just like the past couple of videos you will know that in in redis you didn't actually use json for configuration files right you had to use their own specific language and so parsing the config was a lot more complicated in redis than it is in this custom project that we're building right if you use JSON for your config files you can literally just add this one line over here and the built-in JSON package will take care of everything for you. So with that done let's also create a config.json file over here and let's add some config. So first of all the base folder in my case this will be programming in your case this can be whatever you want projects or code or whatever right ignore which will be a list of strings for my purposes first of all i'm just going to use node modules when this is a python virtual environment directory name and then dot git so we don't back up all the source control stuff right use underscore get ignore this is going to be true because this is going to be a super useful feature of this application and then finally folders this will be a list of objects each object will have a path for now and for now i'm just going to back up the current directory which is home hassan programming drive syncer video let's paste that in over here and that is our config file all done and ready by the way this ignore this is a global ignore this is the stuff that you want to ignore in every single project right so we don't have to repeat that in every single project in every single git ignore file and stuff right this will this stuff will just be skipped in every single project that you synchronize in this application and then for the individual um the individual projects where we want to ignore some specific files we can use the git ignore file so with that done let's go back over here to the config config.go file and try to read this so let's go to main let's instead of using an empty config struct over here let's say read config and pass in the file path which in our case is just config.json right and we can remove the ampersand here because this is already going to return a reference let's let's actually print this as well there we go now let's run this and and see what happens. Okay, so it's nil right now. I wonder why. Oh, oh, oh, I forgot to pass in a reference over here. Just make sure you add this ampersand over here in the decode function call just to turn this into a reference, and then it should work. So as you can see, this is the config struct, the config object. It got the base folder programming. It got the ignore stuff, and so on and so forth, right? Okay, awesome. By the way, this memory address you see over here, this is just the folder list since this is the list of pointers there's a list of pointers and pointers are just memory addresses in go that's what it printed over here but this is a list of folders right this is the first folder that we added over here so just keep that in mind but yeah this works we can read our config awesome but there's one final tiny issue that we need to solve you will notice that um we're not actually able to let like let me just show you let's print the list of folders over here uh we don't need to print the config over here anymore let's just print the list of folders and you'll see what i mean okay so that didn't work because this is still a memory address but let me try instead of that let me just try looping over the whole thing What I basically want to show you is that you can get the folder path over here but you won get the folder name which is something that we really need so we going to have to get that ourselves but let me just show you what this looks like first pass in the f which will be a pointer and print the path and the name and probably separate both of them with let's say and is equal to sign or something and see what that gives us yeah see so the name field is empty and we get an empty string over here is equal to and this is the path so it can read the path just fine but there is no name we need a folder name over here and we can grab that by just looking at the path over here and just taking this final folder name over here right like look at the entire path and just take this final folder name and that will be the folder name right so let's do that remove all of this and i'm going to create a new function down here let's say add folder names this will take the config again let's loop over all of the folders again range c dot folders let's say if the folder name is equal to an empty string you'll see why this check is important in just a second we'll say f dot name equals you can use the file path dot base function and this will return the last element of the path which is exactly what we need in this case let's pass in the folder.path over here and that's all we need to do now let's call this function over here add folder names pass in the config over here and then try printing all of the folders inside this config. Let's do the same thing we did before. Print the name, separate them with an is equal to sign and print the path. Let's run this again. And this time you can see, and by the way, let me probably, I should probably just comment all of this out because this is not doing any good for me right now. There we go. Okay. so i just commented out all the google drive upload stuff and there we go okay so now we're getting the file name the folder name and then the folder pad as well okay so this works now why do i check whether it's an empty string well because it's only going to be an empty string if the user didn't specify the folder name in the config so if for instance we don't want to use this name we can also do something like name and we can say custom name over here and this should read that name and yeah there we go see so we can use a custom name for this folder as well if we want to in the config but if we let's say we forget to do that then the default is just going to be this folder name over here right and that's going to work perfectly fine as well so i think i'm going to stop the video over there again this is getting way too long 24 minutes the last video was like 46 minutes of recording right i still haven't edited that but uh it'll probably end up being like a 30 40 minute video which is insane so yeah let me try just you know just just let me try and keep the videos to a very short length like a 15 20 minute video per per video and uh yeah for now we're done with this we we built this config struct we also built this drive struct over here to organize stuff better and just remove some of the business logic from the main function. We're going to do a bit more cleanup in the next video and we're also going to implement the files and folders upload stuff in the next video or at least we're going to start implementing that because there's a lot of stuff to do in that section as well and yeah there's a ton of stuff to do. So for now we build this config struct and it is working perfectly fine as you can see. In next video we're going to try and build the upload functionality for files and folders so stay tuned for that new videos coming very very soon and yeah that's all thank you for watching like comment subscribe share thank you for following along this new series and i hope you learned something and i hope you have a good time here we're going to we're going to continue building all sorts of cool interesting complicated projects as much as we can i'm going to be sharing all of the work i do on this youtube channel so if you haven't subscribed and make sure you do that and uh yeah thank you for following along i will see you in the next video bye

Share this article

All great things started with a conversation

If you've got a cool project or opportunity and you want me to be a part of it, set up a free meeting with me here, and let's talk. 😊