{"id":20481461,"url":"https://github.com/ethanbaker/sql-wrapper","last_synced_at":"2026-05-07T20:15:51.196Z","repository":{"id":218106945,"uuid":"745561757","full_name":"ethanbaker/sql-wrapper","owner":"ethanbaker","description":"An SQL Wrapper for Structs in Golang","archived":false,"fork":false,"pushed_at":"2024-03-18T19:26:02.000Z","size":152,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-16T04:12:41.169Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ethanbaker.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-01-19T15:53:13.000Z","updated_at":"2024-01-19T18:10:53.000Z","dependencies_parsed_at":"2024-03-18T19:01:18.840Z","dependency_job_id":"7cba9386-be2b-434c-8145-641b20d52351","html_url":"https://github.com/ethanbaker/sql-wrapper","commit_stats":null,"previous_names":["ethanbaker/sql-wrapper"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ethanbaker%2Fsql-wrapper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ethanbaker%2Fsql-wrapper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ethanbaker%2Fsql-wrapper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ethanbaker%2Fsql-wrapper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ethanbaker","download_url":"https://codeload.github.com/ethanbaker/sql-wrapper/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242058081,"owners_count":20065062,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-15T16:08:32.860Z","updated_at":"2026-05-07T20:15:46.149Z","avatar_url":"https://github.com/ethanbaker.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!--\n  Created by: Ethan Baker (contact@ethanbaker.dev)\n  \n  Adapted from:\n    https://github.com/othneildrew/Best-README-Template/\n\nHere are different preset \"variables\" that you can search and replace in this template.\n`path_to_logo`\n`path_to_demo`\n--\u003e\n\n\u003cdiv id=\"top\"\u003e\u003c/div\u003e\n\n\u003c!-- PROJECT SHIELDS/BUTTONS --\u003e\n\u003c!-- \n  Netlify buttons:\n[![Netlify Status]()]()\n  Golang specific buttons:\n--\u003e\n![0.1.0](https://img.shields.io/badge/status-0.1.0-red)\n[![Contributors][contributors-shield]][contributors-url]\n[![Forks][forks-shield]][forks-url]\n[![Stargazers][stars-shield]][stars-url]\n[![Issues][issues-shield]][issues-url]\n[![License][license-shield]][license-url]\n[![LinkedIn][linkedin-shield]][linkedin-url]\n\n\n[![GoDoc](https://godoc.org/github.com/ethanbaker/sql-wrapper?status.svg)](https://godoc.org/github.com/ethanbaker/sql-wrapper)\n[![Go Report Card](https://goreportcard.com/badge/github.com/ethanbaker/sql-wrapper)](https://goreportcard.com/report/github.com/ethanbaker/sql-wrapper)\n[![Go Coverage](./docs/go-coverage.svg)](./docs/go-coverage.svg)\n\n\n\u003c!-- PROJECT LOGO --\u003e\n\u003cbr\u003e\u003cbr\u003e\u003cbr\u003e\n\u003cdiv align=\"center\"\u003e\n  \u003ca href=\"https://github.com/ethanbaker/sql-wrapper\"\u003e\n    \u003cimg src=\"./docs/logo.png\" alt=\"Logo\" width=\"80\" height=\"80\"\u003e\n  \u003c/a\u003e\n\n  \u003ch3 align=\"center\"\u003eSQL Wrapper\u003c/h3\u003e\n\n  \u003cp align=\"center\"\u003e\n    An SQL wrapper for structs in Golang\n  \u003c/p\u003e\n\u003c/div\u003e\n\n\n\u003c!-- TABLE OF CONTENTS --\u003e\n\u003cdetails\u003e\n  \u003csummary\u003eTable of Contents\u003c/summary\u003e\n  \u003col\u003e\n    \u003cli\u003e\n      \u003ca href=\"#about-the-project\"\u003eAbout\u003c/a\u003e\n      \u003cul\u003e\n        \u003cli\u003e\u003ca href=\"#limitations\"\u003eLimitations\u003c/a\u003e\u003c/li\u003e\n        \u003cli\u003e\u003ca href=\"#built-with\"\u003eBuilt With\u003c/a\u003e\u003c/li\u003e\n      \u003c/ul\u003e\n    \u003c/li\u003e\n    \u003cli\u003e\n      \u003ca href=\"#getting-started\"\u003eGetting Started\u003c/a\u003e\n      \u003cul\u003e\n        \u003cli\u003e\u003ca href=\"#struct-setup\"\u003eStruct Setup\u003c/a\u003e\u003c/li\u003e\n        \u003cli\u003e\u003ca href=\"#foreign-relations\"\u003eForeign Relations\u003c/a\u003e\u003c/li\u003e\n        \u003cli\u003e\u003ca href=\"#read-method\"\u003eRead Method\u003c/a\u003e\u003c/li\u003e\n        \u003cli\u003e\u003ca href=\"#creating-new-wrappers\"\u003eCreating New Wrappers\u003c/a\u003e\u003c/li\u003e\n        \u003cli\u003e\u003ca href=\"#wrapper-functions\"\u003eWrapper Functions\u003c/a\u003e\u003c/li\u003e\n        \u003cli\u003e\u003ca href=\"#examples\"\u003eExamples\u003c/a\u003e\u003c/li\u003e\n      \u003c/ul\u003e\n    \u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"#roadmap\"\u003eRoadmap\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"#contributing\"\u003eContributing\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"#license\"\u003eLicense\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"#contact\"\u003eContact\u003c/a\u003e\u003c/li\u003e\n  \u003c/ol\u003e\n\u003c/details\u003e\n\n\n\u003c!-- ABOUT --\u003e\n## About\n\nThis project is used to wrap golang structs in a schema that makes it easier to repeatedly save occurances of those structs to SQL. \n\nThe motivation of this project is to avoid the annoying task of updating SQL saving in golang projects. For example, if an API is saving `Record` structs to an SQL database, and then spec changes require the `Record` struct to change, it can be very cumbersome to go through all of the manually-written SQL code to change this.\n\nIn addition, wrapping SQL structs makes set up much, much easier. You only need to worry about the initialization of your structs and related SQL instead of every SQL action.\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n\n### Limitations\n\nThis project has a few key limitations:\n* You must define all of your SQL types in the struct using tags\n  * The wrapper attempts to match these automatically, but it trusts SQL to make decisions and throw errors. If you declare a field as an integer when it is actually a string, SQL will handle it\n* You must make your struct a part of the `Readable` interface\n  * This is required because I have no idea how to automate reading SQL tables with an indeterminate amount of interface pointers (if you know how to do this please consider contributing!)\n  * There are some \"template\" `Read` methods in the `examples` directory for different scenarios that you can check out\n* Not every struct attribute is supported\n  * So far, only primitive types (`string`, `int`, `enums`, etc), pointers (`*Object`), and lists of pointers (`[]*Object`) are supported\n  * Maps are **not** supported\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n### Built With\n\n* [Golang](https://go.dev/)\n* SQL\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n\n\u003c!-- GETTING STARTED --\u003e\n## Getting Started\n\nThis wrapper is relatively easy to set up for your projects. These steps will follow along the `examples/record` project found in the repository.\n\n#### Struct Setup\n\nFor each struct you want to wrap, define the struct and the SQL table it represents. At the moment this is a relatively manual process done with struct tags.\n* The `sql` tag tells the wrapper to include this field for SQL consideration. The value of this field is the name of the associated SQL column\n* The `def` tag tells the wrapper how to initialize this field as a column in SQL.\n\nYou do not need to create an ID field; one will be added automatically.\n\n```go\ntype Record struct {\n  Author string   `sql:\"Author\" def:\"VARCHAR(128)\"`\n\tLikes  int      `sql:\"Likes\" def:\"INT\"`\n\tType   PostType `sql:\"Type\" def:\"ENUM('Original', 'Comment', 'Repost')\"`\n}\n```\n\nIn this instance, a `Record` struct is created with three different fields (the `Type` field has an enum value defined outside of this snippet). Then, each field has related tags to fit with the definition.\n\nWhen the wrapper is initialized, an SQL table will be created that looks like the following:\n\n|id |Author|Likes|Type|\n|---|------|-----|----|\n|...|      |     |    |\n\nEach column has the following SQL type:\n* **id**: `INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY` (this is automatically generated for every struct)\n* **Author**: `VARCHAR(128)`\n* **Likes**: `INT`\n* **Type**: `ENUM('Original', 'Comment', 'Repost')`\n\n#### Foreign Relations\n\nForeign relations are supported for wrappers. Foreign relations work by relating a **source** (the struct you are defining) and a **target** (the struct you are referencing).\n\nThere are four different types stored as the `RelationType` enum:\n* `OneToOne`: Match one source object to one target object. Objects cannot be linked to more than one other object.\n  * Relations are done using a *pointer* to the other object\n  * This is equivalent to `ManyToOne` with a `UNIQUE` constraint on the target ID\n* `ManyToOne`: Match any amount of source objects to one target object. Source objects cannot be linked to more than one other object.\n  * Relations are done using a *pointer* to the other object\n* `OneToMany`: Match any amount of target objects to one source object. Target objects cannot be linked to more than one source object.\n  * Relations are done using an *array of pointers* to the other object(s)\n  * This is equivalent to `ManyToMany` with a `UNIQUE` constraint on the target ID\n* `ManyToMany`: Match any amount of target objects to any source object.\n  * Relations are done using an *array of pointers* to the other object(s)\n\nYou can define struct attributes to be a foreign relation by adding a `rel` field to the attribute tag. For example:\n\n```go\ntype ReferenceObject struct {\n\tOneToOne   *Object1   `sql:\"Object1ID\" rel:\"one-to-one\"`\n\tManyToOne  *Object2   `sql:\"Object2ID\" rel:\"many-to-one\"`\n\tOneToMany  []*Object3 `sql:\"Object3ID\" rel:\"one-to-many\"`\n\tManyToMany []*Object4 `sql:\"Object4ID\" rel:\"many-to-many\"`\n}\n```\n\nYou can see examples of foreign relations in the `examples` folder of this project. The examples that deal with foreign relations are:\n* `examples/user-post`: a one-to-many relationship between User and Post, where a User can have a list of Posts\n* `examples/item-identification`: a one-to-one relationship between Item and Identification. An Item has one and only one Identification struct created and linked to it\n\n#### Read Method\n\nNext, a `Read` function needs to be created that is attached to the struct.\n\nBecause SQL querires need specificity when reading in new values, this is done easiest through a user-defined function. \n\nIf you know of a way to make reading in SQL tables easier, please consider [contributing](#contributing)!\n\n```go\n// Read function reads in values from an SQL database\n// NOTE: there is no pointer receiver in this method to properly match the Readable interface\nfunc (r Record) Read(db *sql.DB) (map[int]sql_wrapper.Readable, error) {\n  // Create a list of Readable objects to populate\n\titems := map[int]sql_wrapper.Readable{}\n\n\t// Get the main elements\n  // NOTE: the name of the SQl table you want to use is the name of the struct\n  // NOTE: the commented line below should be equivalent:\n  // rows, err := db.Query(\"SELECT * FROM \" + reflect.TypeOf(s.template).Name())\n\trows, err := db.Query(\"SELECT * FROM Record\")\n\tif err != nil {\n\t\treturn items, err\n\t}\n\tdefer rows.Close()\n\n\t// Read each row in the query\n\tvar (\n\t\tid     int\n\t\tauthor string\n\t\tlikes  int\n\t\tt      PostType\n\t)\n\tfor rows.Next() {\n    // Scan the specific elements of this struct in to custom variables\n\t\tif err := rows.Scan(\u0026id, \u0026author, \u0026likes, \u0026t); err != nil {\n\t\t\treturn items, err\n\t\t}\n\n    // Create a new Record object and add it to our list\n    // NOTE: you **MUST** add the object as a pointer. If not, then the object will be stored and retreived as a copy, leading to undesired behavior\n\t\tobj := Record{Author: author, Likes: likes, Type: t}\n\t\titems[id] = \u0026obj\n\t}\n\n\treturn items, nil\n}\n```\n\nAs noted in the code snippet, there are many important notes to keep in mind:\n* The function is defined as `func (o Object) Read...`. It is **not** constructed with a pointer receiver (i.e. no `(o *Object)`). This is to make `Object` fit the `Readable` interface\n* The SQL table queried from is the same as the object name. You can replace this with `reflect` to automate the name-getting\n* You **MUST** add objects as pointers or else undesired behavior will enter your program and it will most likely not work\n\nIf you are creating a `Read` method with foreign relations, you need to perform more operations than a standard read.\n\nFor pointer relationships (**one-to-one** and **many-to-one**), you must get the referenced object by using the public method `GetObjectBySchema` and then cast it to the object you want. Here is an example from `examples/item-identification`:\n\n```go\n// Read reads in SQL values to the wrapper\nfunc (r Identification) Read(db *sql.DB) (map[int]sql_wrapper.Readable, error) {\n\titems := map[int]sql_wrapper.Readable{}\n\n\t// Get the main elements\n\trows, err := db.Query(\"SELECT * FROM Identification\")\n\tif err != nil {\n\t\treturn items, err\n\t}\n\tdefer rows.Close()\n\n\t// Read for each row\n\tvar (\n\t\tid     int\n\t\tnumber int\n\t\thash   string\n\t\titemID int\n\t)\n\tfor rows.Next() {\n\t\tif err := rows.Scan(\u0026id, \u0026number, \u0026hash, \u0026itemID); err != nil {\n\t\t\treturn items, err\n\t\t}\n\n\t\t// Get the referenced item. We use a public method together with the name of the database/wrapper we want to find the link to. This gives us an object of the Readable interface\n\t\treadable, err := sql_wrapper.GetObjectBySchema(\"Item\", itemID)\n\t\tif err != nil {\n\t\t\treturn items, err\n\t\t}\n\n    // Now we can cast our readable object to the pointer we want\n    // NOTE: this cast will fail if you didn't save your object as a pointer!\n\t\titem, ok := readable.(*Item)\n\t\tif !ok {\n\t\t\treturn items, fmt.Errorf(\"cannot cast object to *post\")\n\t\t}\n\n\t\t// Create and add the object\n    // NOTE: save the object as a pointer!\n\t\tobj := Identification{Number: number, Hash: hash, Item: item}\n\t\titems[id] = \u0026obj\n\t}\n\n\treturn items, nil\n}\n```\n\nFor slice relationships (**one-to-many** and **many-to-many**), you must get the rows of another SQL table that links the two wrappers together. This table is defined as the source struct's name concatenated with the target's struct name. Here is an example from `examples/user-post`:\n\n```go\n// Read reads in SQL values to the wrapper\nfunc (r User) Read(db *sql.DB) (map[int]sql_wrapper.Readable, error) {\n\titems := map[int]sql_wrapper.Readable{}\n\n\t// Get the main elements\n\trows, err := db.Query(\"SELECT * FROM User\")\n\tif err != nil {\n\t\treturn items, err\n\t}\n\tdefer rows.Close()\n\n\t// Read for each row\n\tvar (\n\t\tid   int\n\t\tname string\n\t)\n\tfor rows.Next() {\n\t\tif err := rows.Scan(\u0026id, \u0026name); err != nil {\n\t\t\treturn items, err\n\t\t}\n\n    // Add the object normally\n\t\tobj := User{Name: name}\n\t\titems[id] = \u0026obj\n\t}\n\n\t// Query the related elements\n  // NOTE: the source, or the struct we're writing Read for, is User. The target, or the struct being referenced by the source, is Post. So, the table name is UserPost, and ID columns follow the same order\n\trows, err = db.Query(\"SELECT * FROM UserPost\")\n\tif err != nil {\n\t\treturn items, err\n\t}\n\tdefer rows.Close()\n\n\t// Read for each row\n\tvar (\n\t\tuserID int\n\t\tpostID int\n\t)\n\tfor rows.Next() {\n\t\t// Scan in row elements\n\t\tif err := rows.Scan(\u0026userID, \u0026postID); err != nil {\n\t\t\treturn items, err\n\t\t}\n\n    // Get the reference object from the other wrapper\n\t\treadable, err := sql_wrapper.GetObjectBySchema(\"Post\", postID)\n\t\tif err != nil {\n\t\t\treturn items, err\n\t\t}\n\n    // Cast it to be the object we want\n\t\tobj, ok := readable.(*Post)\n\t\tif !ok {\n\t\t\treturn items, fmt.Errorf(\"cannot cast object to *Post\")\n\t\t}\n\n    // Get the user we want to link this item to\n\t\treadable, ok = items[userID]\n\t\tif !ok {\n\t\t\treturn items, fmt.Errorf(\"object was not saved from main table correctly (was it saved as a pointer?)\")\n\t\t}\n\t\tuser := readable.(*User)\n\n\t\t// Add the target object to the corresponding source object\n\t\tuser.Posts = append(user.Posts, obj)\n\t}\n\n\treturn items, nil\n}\n```\n\n#### Creating New Wrappers\n\nNow that your struct has been finished, a wrapper must be created to keep your struct objects synced to SQL. Creating a wrapper can be done as so with the required parameters.\n\n```go\nwrapper, err := sql_wrapper.NewWrapper[*Record](db, Record{})\n```\n\nIn order to create a schema, you must provide:\n* The SQL database needed to execute statements\n* An empty struct you are parametrizing the schema with\n* A generic type as a pointer, which lets the wrapper know what type to return\n\nYou can read in existing SQL entries using the `Read` function:\n\n```go\nif err := wrapper.Read(); err != nil {\n  // Handle error\n}\n```\n\nYou should call `Read` on wrappers without foreign references **first**. This allows other wrappers with foreign references to pull in relations after the other wrapper has loaded first.\n\n#### Wrapper Functions\n\nAfter your wrapper is created, you can then call functions associated with it. These are present in the examples and the [documentation][documentation-url].\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n### Examples\n\nThere are numerous examples present in the `examples` directory. You can check these out for help with your own project. You can do this by editing the SQL config struct present in the examples with your own values initialized on your own machine.\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n\n\u003c!-- ROADMAP --\u003e\n## Roadmap\n\n- [x] Foreign Key Constraints\n- [x] Find by ID Functions\n- [ ] Foreign Relations with Maps\n- [ ] Create GitHub Actions Workflow\n\nSee the [open issues][issues-url] for a full list of proposed features (and known issues).\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n\n\u003c!-- CONTRIBUTING --\u003e\n## Contributing\n\nFor issues and suggestions, please include as much useful information as possible.\nReview the [documentation][documentation-url] and make sure the issue is actually\npresent or the suggestion is not included. Please share issues/suggestions on the\n[issue tracker][issues-url].\n\nFor patches and feature additions, please submit them as [pull requests][pulls-url]. \nPlease adhere to the [conventional commits][conventional-commits-url]. standard for\ncommit messaging. In addition, please try to name your git branch according to your\nnew patch. [These standards][conventional-branches-url] are a great guide you can follow.\n\nYou can follow these steps below to create a pull request:\n\n1. Fork the Project\n2. Create your Feature Branch (`git checkout -b branch_name`)\n3. Commit your Changes (`git commit -m \"commit_message\"`)\n4. Push to the Branch (`git push origin branch_name`)\n5. Open a Pull Request\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n\n\u003c!-- LICENSE --\u003e\n## License\n\nThis project uses the Apache 2.0 License.\n\nYou can find more information in the [LICENSE][license-url] file.\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n\n\u003c!-- CONTACT --\u003e\n## Contact\n\nEthan Baker - contact@ethanbaker.dev - [LinkedIn][linkedin-url]\n\nProject Link: [https://github.com/ethanbaker/sql-wrapper][project-url]\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n\n\u003c!-- MARKDOWN LINKS \u0026 IMAGES --\u003e\n\u003c!-- https://www.markdownguide.org/basic-syntax/#reference-style-links --\u003e\n[contributors-shield]: https://img.shields.io/github/contributors/ethanbaker/sql-wrapper.svg\n[forks-shield]: https://img.shields.io/github/forks/ethanbaker/sql-wrapper.svg\n[stars-shield]: https://img.shields.io/github/stars/ethanbaker/sql-wrapper.svg\n[issues-shield]: https://img.shields.io/github/issues/ethanbaker/sql-wrapper.svg\n[license-shield]: https://img.shields.io/github/license/ethanbaker/sql-wrapper.svg\n[linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?logo=linkedin\u0026colorB=555\n\n[contributors-url]: \u003chttps://github.com/ethanbaker/sql-wrapper/graphs/contributors\u003e\n[forks-url]: \u003chttps://github.com/ethanbaker/sql-wrapper/network/members\u003e\n[stars-url]: \u003chttps://github.com/ethanbaker/sql-wrapper/stargazers\u003e\n[issues-url]: \u003chttps://github.com/ethanbaker/sql-wrapper/issues\u003e\n[pulls-url]: \u003chttps://github.com/ethanbaker/sql-wrapper/pulls\u003e\n[license-url]: \u003chttps://github.com/ethanbaker/sql-wrapper/blob/master/LICENSE\u003e\n[linkedin-url]: \u003chttps://linkedin.com/in/ethandbaker\u003e\n[project-url]: \u003chttps://github.com/ethanbaker/sql-wrapper\u003e\n\n[documentation-url]: \u003chttps://godoc.org/github.com/ethanbaker/sql-wrapper\u003e\n\n[conventional-commits-url]: \u003chttps://www.conventionalcommits.org/en/v1.0.0/#summary\u003e\n[conventional-branches-url]: \u003chttps://docs.microsoft.com/en-us/azure/devops/repos/git/git-branching-guidance?view=azure-devops\u003e","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fethanbaker%2Fsql-wrapper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fethanbaker%2Fsql-wrapper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fethanbaker%2Fsql-wrapper/lists"}