├── client ├── src │ ├── index.css │ ├── vite-env.d.ts │ ├── chakra │ │ └── theme.ts │ ├── App.tsx │ ├── main.tsx │ └── components │ │ ├── Navbar.tsx │ │ ├── TodoForm.tsx │ │ ├── TodoList.tsx │ │ └── TodoItem.tsx ├── dist │ ├── go.png │ ├── react.png │ ├── explode.png │ ├── golang.png │ ├── index.html │ └── vite.svg ├── public │ ├── go.png │ ├── golang.png │ ├── react.png │ ├── explode.png │ └── vite.svg ├── vite.config.ts ├── tsconfig.node.json ├── .gitignore ├── index.html ├── .eslintrc.cjs ├── tsconfig.json ├── package.json └── README.md ├── .env.sample ├── .gitignore ├── air.toml ├── README.md ├── LICENSE ├── go.mod ├── main.go ├── COMPARISONS.md └── go.sum /client/src/index.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /.env.sample: -------------------------------------------------------------------------------- 1 | PORT=5000 2 | MONGODB_URI= 3 | ENV=development -------------------------------------------------------------------------------- /client/dist/go.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/burakorkmez/react-go-tutorial/HEAD/client/dist/go.png -------------------------------------------------------------------------------- /client/dist/react.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/burakorkmez/react-go-tutorial/HEAD/client/dist/react.png -------------------------------------------------------------------------------- /client/public/go.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/burakorkmez/react-go-tutorial/HEAD/client/public/go.png -------------------------------------------------------------------------------- /client/dist/explode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/burakorkmez/react-go-tutorial/HEAD/client/dist/explode.png -------------------------------------------------------------------------------- /client/dist/golang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/burakorkmez/react-go-tutorial/HEAD/client/dist/golang.png -------------------------------------------------------------------------------- /client/public/golang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/burakorkmez/react-go-tutorial/HEAD/client/public/golang.png -------------------------------------------------------------------------------- /client/public/react.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/burakorkmez/react-go-tutorial/HEAD/client/public/react.png -------------------------------------------------------------------------------- /client/public/explode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/burakorkmez/react-go-tutorial/HEAD/client/public/explode.png -------------------------------------------------------------------------------- /client/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /client/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true, 8 | "strict": true 9 | }, 10 | "include": ["vite.config.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | # dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | GO + React + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /client/dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | GO + React + TS 8 | 9 | 10 | 11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /client/src/chakra/theme.ts: -------------------------------------------------------------------------------- 1 | import { extendTheme, type ThemeConfig } from "@chakra-ui/react"; 2 | import { mode } from "@chakra-ui/theme-tools"; 3 | 4 | const config: ThemeConfig = { 5 | initialColorMode: "dark", 6 | useSystemColorMode: true, 7 | }; 8 | 9 | // 3. extend the theme 10 | const theme = extendTheme({ 11 | config, 12 | styles: { 13 | global: (props: any) => ({ 14 | body: { 15 | backgroundColor: mode("gray.500", "")(props), 16 | }, 17 | }), 18 | }, 19 | }); 20 | 21 | export default theme; 22 | -------------------------------------------------------------------------------- /client/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:@typescript-eslint/recommended', 7 | 'plugin:react-hooks/recommended', 8 | ], 9 | ignorePatterns: ['dist', '.eslintrc.cjs'], 10 | parser: '@typescript-eslint/parser', 11 | plugins: ['react-refresh'], 12 | rules: { 13 | 'react-refresh/only-export-components': [ 14 | 'warn', 15 | { allowConstantExport: true }, 16 | ], 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /client/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { Container, Stack } from "@chakra-ui/react"; 2 | import Navbar from "./components/Navbar"; 3 | import TodoForm from "./components/TodoForm"; 4 | import TodoList from "./components/TodoList"; 5 | 6 | export const BASE_URL = import.meta.env.MODE === "development" ? "http://localhost:5000/api" : "/api"; 7 | 8 | function App() { 9 | return ( 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | ); 18 | } 19 | 20 | export default App; 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .env 3 | node_modules 4 | 5 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 6 | *.o 7 | *.a 8 | *.so 9 | 10 | # Folders 11 | _obj 12 | _test 13 | vendor/ 14 | 15 | # logs 16 | *.log 17 | 18 | # Compiled binary files 19 | *.exe 20 | *.out 21 | 22 | # macOS specific files 23 | .DS_Store 24 | 25 | # Go specific files 26 | # Compiled binary for packages and commands 27 | /bin/ 28 | # Output of the go coverage tool, specifically when used with LiteIDE 29 | *.out 30 | 31 | # Dependency directories (remove the comment below if you are using dep) 32 | -------------------------------------------------------------------------------- /air.toml: -------------------------------------------------------------------------------- 1 | root = "." # The root directory of the project 2 | tmp_dir = "tmp" # The temporary directory where air will store its temporary files 3 | 4 | [build] # The build configuration 5 | bin = "main" # The name of the binary file to be generated after building the project 6 | cmd = "go build -o {{.Output}} {{.Input}}" # The command to build the project 7 | exclude = ["tmp/*", "client/*"] # Specifies the directories to be excluded from monitoring for changes 8 | include = ["**/*.go"] # Specifies the file patterns to be included for monitoring. 9 | ignore = ["tmp/*"] # Specifies the files or directories to be ignored when triggering a build. -------------------------------------------------------------------------------- /client/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./App.tsx"; 4 | import "./index.css"; 5 | import { ChakraProvider } from "@chakra-ui/react"; 6 | import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; 7 | import theme from "./chakra/theme.ts"; 8 | 9 | const queryClient = new QueryClient(); 10 | 11 | ReactDOM.createRoot(document.getElementById("root")!).render( 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | ); 20 | -------------------------------------------------------------------------------- /client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true 22 | }, 23 | "include": ["src"], 24 | "references": [{ "path": "./tsconfig.node.json" }] 25 | } 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Let's Go! React with Go Complete Fullstack App - TypeScript, React Query, MongoDB, ChakraUI 2 | 3 | ![Demo App](https://i.ibb.co/JvRTWmW/Group-93.png) 4 | 5 | [Video Tutorial on Youtube](https://youtu.be/zw8z_o_kDqc) 6 | 7 | Some Features: 8 | 9 | - ⚙️ Tech Stack: Go, React, TypeScript, MongoDB, TanStack Query, ChakraUI 10 | - ✅ Create, Read, Update, and Delete (CRUD) functionality for todos 11 | - 🌓 Light and Dark mode for user interface 12 | - 📱 Responsive design for various screen sizes 13 | - 🌐 Deployment 14 | - 🔄 Real-time data fetching, caching, and updates with TanStack Query 15 | - 🎨 Stylish UI components with ChakraUI 16 | - ⏳ And much more! 17 | 18 | ### .env file 19 | 20 | ```shell 21 | MONGO_URI= 22 | PORT=5000 23 | ENV=development 24 | ``` 25 | 26 | ### Compile and run 27 | 28 | ```shell 29 | go run main.go 30 | ``` 31 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "@chakra-ui/react": "^2.8.2", 14 | "@emotion/react": "^11.11.4", 15 | "@emotion/styled": "^11.11.5", 16 | "@tanstack/react-query": "^5.32.1", 17 | "framer-motion": "^11.1.7", 18 | "react": "^18.2.0", 19 | "react-dom": "^18.2.0", 20 | "react-icons": "^5.2.0" 21 | }, 22 | "devDependencies": { 23 | "@types/react": "^18.2.66", 24 | "@types/react-dom": "^18.2.22", 25 | "@typescript-eslint/eslint-plugin": "^7.2.0", 26 | "@typescript-eslint/parser": "^7.2.0", 27 | "@vitejs/plugin-react": "^4.2.1", 28 | "eslint": "^8.57.0", 29 | "eslint-plugin-react-hooks": "^4.6.0", 30 | "eslint-plugin-react-refresh": "^0.4.6", 31 | "typescript": "^5.2.2", 32 | "vite": "^5.2.0" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Burak 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/burakorkmez/react-go-tutorial 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/andybalholm/brotli v1.0.5 // indirect 7 | github.com/gofiber/fiber/v2 v2.52.4 // indirect 8 | github.com/golang/snappy v0.0.1 // indirect 9 | github.com/google/uuid v1.5.0 // indirect 10 | github.com/joho/godotenv v1.5.1 // indirect 11 | github.com/klauspost/compress v1.17.0 // indirect 12 | github.com/mattn/go-colorable v0.1.13 // indirect 13 | github.com/mattn/go-isatty v0.0.20 // indirect 14 | github.com/mattn/go-runewidth v0.0.15 // indirect 15 | github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect 16 | github.com/rivo/uniseg v0.2.0 // indirect 17 | github.com/valyala/bytebufferpool v1.0.0 // indirect 18 | github.com/valyala/fasthttp v1.51.0 // indirect 19 | github.com/valyala/tcplisten v1.0.0 // indirect 20 | github.com/xdg-go/pbkdf2 v1.0.0 // indirect 21 | github.com/xdg-go/scram v1.1.2 // indirect 22 | github.com/xdg-go/stringprep v1.0.4 // indirect 23 | github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect 24 | go.mongodb.org/mongo-driver v1.15.0 // indirect 25 | golang.org/x/crypto v0.17.0 // indirect 26 | golang.org/x/sync v0.1.0 // indirect 27 | golang.org/x/sys v0.15.0 // indirect 28 | golang.org/x/text v0.14.0 // indirect 29 | ) 30 | -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | # React + TypeScript + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | 10 | ## Expanding the ESLint configuration 11 | 12 | If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: 13 | 14 | - Configure the top-level `parserOptions` property like this: 15 | 16 | ```js 17 | export default { 18 | // other rules... 19 | parserOptions: { 20 | ecmaVersion: 'latest', 21 | sourceType: 'module', 22 | project: ['./tsconfig.json', './tsconfig.node.json'], 23 | tsconfigRootDir: __dirname, 24 | }, 25 | } 26 | ``` 27 | 28 | - Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` 29 | - Optionally add `plugin:@typescript-eslint/stylistic-type-checked` 30 | - Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list 31 | -------------------------------------------------------------------------------- /client/src/components/Navbar.tsx: -------------------------------------------------------------------------------- 1 | import { Box, Flex, Button, useColorModeValue, useColorMode, Text, Container } from "@chakra-ui/react"; 2 | import { IoMoon } from "react-icons/io5"; 3 | import { LuSun } from "react-icons/lu"; 4 | 5 | export default function Navbar() { 6 | const { colorMode, toggleColorMode } = useColorMode(); 7 | 8 | return ( 9 | 10 | 11 | 12 | {/* LEFT SIDE */} 13 | 19 | logo 20 | + 21 | logo 22 | = 23 | logo 24 | 25 | 26 | {/* RIGHT SIDE */} 27 | 28 | 29 | Daily Tasks 30 | 31 | {/* Toggle Color Mode */} 32 | 35 | 36 | 37 | 38 | 39 | ); 40 | } 41 | -------------------------------------------------------------------------------- /client/dist/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/components/TodoForm.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | import { Button, Flex, Input, Spinner } from "@chakra-ui/react"; 3 | import { useMutation, useQueryClient } from "@tanstack/react-query"; 4 | import { useState } from "react"; 5 | import { IoMdAdd } from "react-icons/io"; 6 | import { BASE_URL } from "../App"; 7 | 8 | const TodoForm = () => { 9 | const [newTodo, setNewTodo] = useState(""); 10 | 11 | const queryClient = useQueryClient(); 12 | 13 | const { mutate: createTodo, isPending: isCreating } = useMutation({ 14 | mutationKey: ["createTodo"], 15 | mutationFn: async (e: React.FormEvent) => { 16 | e.preventDefault(); 17 | try { 18 | const res = await fetch(BASE_URL + `/todos`, { 19 | method: "POST", 20 | headers: { 21 | "Content-Type": "application/json", 22 | }, 23 | body: JSON.stringify({ body: newTodo }), 24 | }); 25 | const data = await res.json(); 26 | 27 | if (!res.ok) { 28 | throw new Error(data.error || "Something went wrong"); 29 | } 30 | 31 | setNewTodo(""); 32 | return data; 33 | } catch (error: any) { 34 | throw new Error(error); 35 | } 36 | }, 37 | onSuccess: () => { 38 | queryClient.invalidateQueries({ queryKey: ["todos"] }); 39 | }, 40 | onError: (error: any) => { 41 | alert(error.message); 42 | }, 43 | }); 44 | 45 | return ( 46 |
47 | 48 | setNewTodo(e.target.value)} 52 | ref={(input) => input && input.focus()} 53 | /> 54 | 63 | 64 |
65 | ); 66 | }; 67 | export default TodoForm; 68 | 69 | // STARTER CODE: 70 | 71 | // import { Button, Flex, Input, Spinner } from "@chakra-ui/react"; 72 | // import { useState } from "react"; 73 | // import { IoMdAdd } from "react-icons/io"; 74 | 75 | // const TodoForm = () => { 76 | // const [newTodo, setNewTodo] = useState(""); 77 | // const [isPending, setIsPending] = useState(false); 78 | 79 | // const createTodo = async (e: React.FormEvent) => { 80 | // e.preventDefault(); 81 | // alert("Todo added!"); 82 | // }; 83 | // return ( 84 | //
85 | // 86 | // setNewTodo(e.target.value)} 90 | // ref={(input) => input && input.focus()} 91 | // /> 92 | // 101 | // 102 | //
103 | // ); 104 | // }; 105 | // export default TodoForm; 106 | -------------------------------------------------------------------------------- /client/src/components/TodoList.tsx: -------------------------------------------------------------------------------- 1 | import { Flex, Spinner, Stack, Text } from "@chakra-ui/react"; 2 | 3 | import TodoItem from "./TodoItem"; 4 | import { useQuery } from "@tanstack/react-query"; 5 | import { BASE_URL } from "../App"; 6 | 7 | export type Todo = { 8 | _id: number; 9 | body: string; 10 | completed: boolean; 11 | }; 12 | 13 | const TodoList = () => { 14 | const { data: todos, isLoading } = useQuery({ 15 | queryKey: ["todos"], 16 | queryFn: async () => { 17 | try { 18 | const res = await fetch(BASE_URL + "/todos"); 19 | const data = await res.json(); 20 | 21 | if (!res.ok) { 22 | throw new Error(data.error || "Something went wrong"); 23 | } 24 | return data || []; 25 | } catch (error) { 26 | console.log(error); 27 | } 28 | }, 29 | }); 30 | 31 | return ( 32 | <> 33 | 42 | Today's Tasks 43 | 44 | {isLoading && ( 45 | 46 | 47 | 48 | )} 49 | {!isLoading && todos?.length === 0 && ( 50 | 51 | 52 | All tasks completed! 🤞 53 | 54 | Go logo 55 | 56 | )} 57 | 58 | {todos?.map((todo) => ( 59 | 60 | ))} 61 | 62 | 63 | ); 64 | }; 65 | export default TodoList; 66 | 67 | // STARTER CODE: 68 | 69 | // import { Flex, Spinner, Stack, Text } from "@chakra-ui/react"; 70 | // import { useState } from "react"; 71 | // import TodoItem from "./TodoItem"; 72 | 73 | // const TodoList = () => { 74 | // const [isLoading, setIsLoading] = useState(true); 75 | // const todos = [ 76 | // { 77 | // _id: 1, 78 | // body: "Buy groceries", 79 | // completed: true, 80 | // }, 81 | // { 82 | // _id: 2, 83 | // body: "Walk the dog", 84 | // completed: false, 85 | // }, 86 | // { 87 | // _id: 3, 88 | // body: "Do laundry", 89 | // completed: false, 90 | // }, 91 | // { 92 | // _id: 4, 93 | // body: "Cook dinner", 94 | // completed: true, 95 | // }, 96 | // ]; 97 | // return ( 98 | // <> 99 | // 100 | // Today's Tasks 101 | // 102 | // {isLoading && ( 103 | // 104 | // 105 | // 106 | // )} 107 | // {!isLoading && todos?.length === 0 && ( 108 | // 109 | // 110 | // All tasks completed! 🤞 111 | // 112 | // Go logo 113 | // 114 | // )} 115 | // 116 | // {todos?.map((todo) => ( 117 | // 118 | // ))} 119 | // 120 | // 121 | // ); 122 | // }; 123 | // export default TodoList; 124 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "os" 8 | 9 | "github.com/gofiber/fiber/v2" 10 | "github.com/joho/godotenv" 11 | "go.mongodb.org/mongo-driver/bson" 12 | "go.mongodb.org/mongo-driver/bson/primitive" 13 | "go.mongodb.org/mongo-driver/mongo" 14 | "go.mongodb.org/mongo-driver/mongo/options" 15 | ) 16 | 17 | type Todo struct { 18 | ID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"` 19 | Completed bool `json:"completed"` 20 | Body string `json:"body"` 21 | } 22 | 23 | var collection *mongo.Collection 24 | 25 | func main() { 26 | fmt.Println("hello world") 27 | 28 | if os.Getenv("ENV") != "production" { 29 | // Load the .env file if not in production 30 | err := godotenv.Load(".env") 31 | if err != nil { 32 | log.Fatal("Error loading .env file:", err) 33 | } 34 | } 35 | 36 | MONGODB_URI := os.Getenv("MONGODB_URI") 37 | clientOptions := options.Client().ApplyURI(MONGODB_URI) 38 | client, err := mongo.Connect(context.Background(), clientOptions) 39 | 40 | if err != nil { 41 | log.Fatal(err) 42 | } 43 | 44 | defer client.Disconnect(context.Background()) 45 | 46 | err = client.Ping(context.Background(), nil) 47 | if err != nil { 48 | log.Fatal(err) 49 | } 50 | 51 | fmt.Println("Connected to MONGODB ATLAS") 52 | 53 | collection = client.Database("golang_db").Collection("todos") 54 | 55 | app := fiber.New() 56 | 57 | // app.Use(cors.New(cors.Config{ 58 | // AllowOrigins: "http://localhost:5173", 59 | // AllowHeaders: "Origin,Content-Type,Accept", 60 | // })) 61 | 62 | app.Get("/api/todos", getTodos) 63 | app.Post("/api/todos", createTodo) 64 | app.Patch("/api/todos/:id", updateTodo) 65 | app.Delete("/api/todos/:id", deleteTodo) 66 | 67 | port := os.Getenv("PORT") 68 | if port == "" { 69 | port = "5000" 70 | } 71 | 72 | if os.Getenv("ENV") == "production" { 73 | app.Static("/", "./client/dist") 74 | } 75 | 76 | log.Fatal(app.Listen("0.0.0.0:" + port)) 77 | 78 | } 79 | 80 | func getTodos(c *fiber.Ctx) error { 81 | var todos []Todo 82 | 83 | cursor, err := collection.Find(context.Background(), bson.M{}) 84 | 85 | if err != nil { 86 | return err 87 | } 88 | 89 | defer cursor.Close(context.Background()) 90 | 91 | for cursor.Next(context.Background()) { 92 | var todo Todo 93 | if err := cursor.Decode(&todo); err != nil { 94 | return err 95 | } 96 | todos = append(todos, todo) 97 | } 98 | 99 | return c.JSON(todos) 100 | } 101 | 102 | func createTodo(c *fiber.Ctx) error { 103 | todo := new(Todo) 104 | // {id:0,completed:false,body:""} 105 | 106 | if err := c.BodyParser(todo); err != nil { 107 | return err 108 | } 109 | 110 | if todo.Body == "" { 111 | return c.Status(400).JSON(fiber.Map{"error": "Todo body cannot be empty"}) 112 | } 113 | 114 | insertResult, err := collection.InsertOne(context.Background(), todo) 115 | if err != nil { 116 | return err 117 | } 118 | 119 | todo.ID = insertResult.InsertedID.(primitive.ObjectID) 120 | 121 | return c.Status(201).JSON(todo) 122 | } 123 | 124 | func updateTodo(c *fiber.Ctx) error { 125 | id := c.Params("id") 126 | objectID, err := primitive.ObjectIDFromHex(id) 127 | 128 | if err != nil { 129 | return c.Status(400).JSON(fiber.Map{"error": "Invalid todo ID"}) 130 | } 131 | 132 | filter := bson.M{"_id": objectID} 133 | update := bson.M{"$set": bson.M{"completed": true}} 134 | 135 | _, err = collection.UpdateOne(context.Background(), filter, update) 136 | if err != nil { 137 | return err 138 | } 139 | 140 | return c.Status(200).JSON(fiber.Map{"success": true}) 141 | 142 | } 143 | 144 | func deleteTodo(c *fiber.Ctx) error { 145 | id := c.Params("id") 146 | objectID, err := primitive.ObjectIDFromHex(id) 147 | 148 | if err != nil { 149 | return c.Status(400).JSON(fiber.Map{"error": "Invalid todo ID"}) 150 | } 151 | 152 | filter := bson.M{"_id": objectID} 153 | _, err = collection.DeleteOne(context.Background(), filter) 154 | 155 | if err != nil { 156 | return err 157 | } 158 | 159 | return c.Status(200).JSON(fiber.Map{"success": true}) 160 | } 161 | -------------------------------------------------------------------------------- /client/src/components/TodoItem.tsx: -------------------------------------------------------------------------------- 1 | import { Badge, Box, Flex, Spinner, Text } from "@chakra-ui/react"; 2 | import { FaCheckCircle } from "react-icons/fa"; 3 | import { MdDelete } from "react-icons/md"; 4 | import { Todo } from "./TodoList"; 5 | import { useMutation, useQueryClient } from "@tanstack/react-query"; 6 | import { BASE_URL } from "../App"; 7 | 8 | const TodoItem = ({ todo }: { todo: Todo }) => { 9 | const queryClient = useQueryClient(); 10 | 11 | const { mutate: updateTodo, isPending: isUpdating } = useMutation({ 12 | mutationKey: ["updateTodo"], 13 | mutationFn: async () => { 14 | if (todo.completed) return alert("Todo is already completed"); 15 | try { 16 | const res = await fetch(BASE_URL + `/todos/${todo._id}`, { 17 | method: "PATCH", 18 | }); 19 | const data = await res.json(); 20 | if (!res.ok) { 21 | throw new Error(data.error || "Something went wrong"); 22 | } 23 | return data; 24 | } catch (error) { 25 | console.log(error); 26 | } 27 | }, 28 | onSuccess: () => { 29 | queryClient.invalidateQueries({ queryKey: ["todos"] }); 30 | }, 31 | }); 32 | 33 | const { mutate: deleteTodo, isPending: isDeleting } = useMutation({ 34 | mutationKey: ["deleteTodo"], 35 | mutationFn: async () => { 36 | try { 37 | const res = await fetch(BASE_URL + `/todos/${todo._id}`, { 38 | method: "DELETE", 39 | }); 40 | const data = await res.json(); 41 | if (!res.ok) { 42 | throw new Error(data.error || "Something went wrong"); 43 | } 44 | return data; 45 | } catch (error) { 46 | console.log(error); 47 | } 48 | }, 49 | onSuccess: () => { 50 | queryClient.invalidateQueries({ queryKey: ["todos"] }); 51 | }, 52 | }); 53 | 54 | return ( 55 | 56 | 65 | 69 | {todo.body} 70 | 71 | {todo.completed && ( 72 | 73 | Done 74 | 75 | )} 76 | {!todo.completed && ( 77 | 78 | In Progress 79 | 80 | )} 81 | 82 | 83 | updateTodo()}> 84 | {!isUpdating && } 85 | {isUpdating && } 86 | 87 | deleteTodo()}> 88 | {!isDeleting && } 89 | {isDeleting && } 90 | 91 | 92 | 93 | ); 94 | }; 95 | export default TodoItem; 96 | 97 | // STARTER CODE: 98 | 99 | // import { Badge, Box, Flex, Text } from "@chakra-ui/react"; 100 | // import { FaCheckCircle } from "react-icons/fa"; 101 | // import { MdDelete } from "react-icons/md"; 102 | 103 | // const TodoItem = ({ todo }: { todo: any }) => { 104 | // return ( 105 | // 106 | // 115 | // 119 | // {todo.body} 120 | // 121 | // {todo.completed && ( 122 | // 123 | // Done 124 | // 125 | // )} 126 | // {!todo.completed && ( 127 | // 128 | // In Progress 129 | // 130 | // )} 131 | // 132 | // 133 | // 134 | // 135 | // 136 | // 137 | // 138 | // 139 | // 140 | // 141 | // ); 142 | // }; 143 | // export default TodoItem; 144 | -------------------------------------------------------------------------------- /COMPARISONS.md: -------------------------------------------------------------------------------- 1 | # Equivalent of `npm init` in Go 2 | 3 | ```bash 4 | go mod init 5 | ``` 6 | 7 | - `go mod init` is used to initialize a new module. 8 | - It creates a new `go.mod` file in the current directory. 9 | - The `go.mod` file contains information about the module, its dependencies, and the Go version. 10 | 11 | # Equivalent of `npm run start` in Go 12 | 13 | ```bash 14 | go run 15 | ``` 16 | 17 | - `go run` is used to compile and run a Go program. 18 | - It compiles the program and executes it. 19 | 20 | # Equivalent of `npm install ` in Go 21 | 22 | ```bash 23 | go get 24 | ``` 25 | 26 | - `go get` is not a package manager. 27 | - `go get` is used to download and install packages from remote repositories. 28 | - It does not handle versioning. 29 | - This command fetches the package and its dependencies (if any) 30 | 31 | # Equivalent of `package.json` in Go 32 | 33 | ```bash 34 | go.mod file 35 | ``` 36 | 37 | - It contains information about the module, its dependencies, and the Go version. 38 | 39 | # Equivalent of `npm install` in Go 40 | 41 | ```bash 42 | go mod tidy 43 | ``` 44 | 45 | - `go mod tidy` is used to add missing and remove unused modules. 46 | - It updates the go.mod file to use the latest version of the dependencies. 47 | 48 | # Equivalent of `JSON.stringify()` in Go 49 | 50 | ```go 51 | import "encoding/json" 52 | 53 | func main() { 54 | data := map[string]interface{}{ 55 | "name": "John Doe", 56 | "age": 30, 57 | } 58 | 59 | jsonString, err := json.Marshal(data) 60 | if err != nil { 61 | fmt.Println(err) 62 | return 63 | } 64 | 65 | fmt.Println(string(jsonString)) 66 | } 67 | ``` 68 | 69 | - `json.Marshal()` is used to convert a Go data structure to a JSON string. 70 | 71 | # Equivalent of `JSON.parse()` in Go 72 | 73 | ```go 74 | import "encoding/json" 75 | 76 | func main() { 77 | jsonString := `{"name":"John Doe","age":30}` 78 | 79 | var data map[string]interface{} 80 | err := json.Unmarshal([]byte(jsonString), &data) 81 | if err != nil { 82 | fmt.Println(err) 83 | return 84 | } 85 | 86 | fmt.Println(data) 87 | } 88 | ``` 89 | 90 | - `json.Unmarshal()` is used to convert a JSON string to a Go data structure. 91 | 92 | # Equivalent of `nodemon` in Go 93 | 94 | ```bash 95 | go install github.com/cosmtrek/air@latest 96 | ``` 97 | 98 | - `air` is a live reload tool for Go applications. 99 | - It watches for file changes and automatically rebuilds and restarts the application. 100 | - It is similar to `nodemon` in the Node.js ecosystem. 101 | - There are other tools like `fresh` which can also be used for live reloading in Go. 102 | 103 | # Equivalent of `dotenv` in Go 104 | 105 | ```bash 106 | go get github.com/joho/godotenv 107 | ``` 108 | 109 | - `godotenv` is a Go package that loads environment variables from a `.env` file. 110 | - It is similar to `dotenv` in the Node.js ecosystem. 111 | - It allows developers to store sensitive information like API keys, database URIs, etc., in a `.env` file and load them into the application. 112 | 113 | ## Code Example of Using `godotenv` 114 | 115 | ```go 116 | package main 117 | 118 | import ( 119 | "fmt" 120 | "log" 121 | "os" 122 | 123 | "github.com/joho/godotenv" 124 | ) 125 | 126 | func main() { 127 | err := godotenv.Load(".env") 128 | if err != nil { 129 | log.Fatal("Error loading .env file") 130 | } 131 | 132 | MONGODB_URI := os.Getenv("MONGODB_URI") 133 | } 134 | ``` 135 | 136 | - In this example, we load environment variables from a `.env` file using `godotenv.Load(".env")`. 137 | - We can then access the environment variables using `os.Getenv("MONGODB_URI")`. 138 | 139 | # Equivalent of `Express.js` in Go 140 | 141 | ```bash 142 | go get github.com/gofiber/fiber/v2 143 | ``` 144 | 145 | - `Fiber` is a web framework for Go that is inspired by Express.js. 146 | - It is fast, lightweight, and easy to use. 147 | - It provides a similar API to Express.js, making it easy for developers familiar with Express.js to transition to Go. 148 | - Other popular web frameworks in Go include `Gin` and `Echo`. 149 | 150 | # Equivalent of `Express.js Middleware` in Go 151 | 152 | ```go 153 | func main() { 154 | app := fiber.New() 155 | 156 | app.Use(middleware) 157 | 158 | app.Get("/", func(c *fiber.Ctx) error { 159 | return c.SendString("Hello, World!") 160 | }) 161 | 162 | app.Listen(":3000") 163 | } 164 | func middleware(next http.Handler) http.Handler { 165 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 166 | // Middleware logic 167 | 168 | // . 169 | // . 170 | // . 171 | 172 | next.ServeHTTP(w, r) 173 | }) 174 | } 175 | ``` 176 | 177 | - In this example, we define a middleware function that takes the `next` handler as an argument. 178 | - The middleware function wraps the `next` handler and executes some logic before calling the `next` handler. 179 | 180 | # Equivalent of `Express.js Route Handling` in Go 181 | 182 | ```go 183 | func main() { 184 | app := fiber.New() 185 | 186 | app.Get("/", helloHandler) 187 | 188 | app.Listen(":3000") 189 | } 190 | 191 | func helloHandler(c *fiber.Ctx) error { 192 | return c.SendString("Hello, World!") 193 | } 194 | ``` 195 | 196 | - In this example, we define a route handler function `helloHandler` that sends a response back to the client. 197 | - We then register the route handler with the `app.Get()` method, specifying the route path and the handler function. 198 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= 2 | github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= 3 | github.com/gofiber/fiber/v2 v2.52.4 h1:P+T+4iK7VaqUsq2PALYEfBBo6bJZ4q3FP8cZ84EggTM= 4 | github.com/gofiber/fiber/v2 v2.52.4/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= 5 | github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= 6 | github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 7 | github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= 8 | github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 9 | github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= 10 | github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= 11 | github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= 12 | github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= 13 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 14 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 15 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 16 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 17 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 18 | github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= 19 | github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 20 | github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0= 21 | github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= 22 | github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= 23 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 24 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= 25 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 26 | github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA= 27 | github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g= 28 | github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= 29 | github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= 30 | github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= 31 | github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= 32 | github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= 33 | github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= 34 | github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= 35 | github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= 36 | github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= 37 | github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= 38 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 39 | go.mongodb.org/mongo-driver v1.15.0 h1:rJCKC8eEliewXjZGf0ddURtl7tTVy1TK3bfl0gkUSLc= 40 | go.mongodb.org/mongo-driver v1.15.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= 41 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 42 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 43 | golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= 44 | golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= 45 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 46 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 47 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 48 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 49 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 50 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 51 | golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= 52 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 53 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 54 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 55 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 56 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 57 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 58 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 59 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 60 | golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= 61 | golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 62 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 63 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 64 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 65 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 66 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 67 | golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= 68 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= 69 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 70 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 71 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 72 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 73 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 74 | --------------------------------------------------------------------------------