├── .gitignore ├── NOTES.md ├── README.md ├── cmd └── main.go ├── go.mod ├── go.sum ├── internal ├── db │ └── db.go └── handlers │ └── products.go └── js ├── db └── index.js ├── main.js ├── package.json ├── products └── index.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | js/node_modules -------------------------------------------------------------------------------- /NOTES.md: -------------------------------------------------------------------------------- 1 | go mod init example.com/tomdoestech 2 | 3 | go get -u github.com/gofiber/fiber/v2 4 | 5 | go get go.mongodb.org/mongo-driver/mongo 6 | 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Golang for Node.js developers 2 | 3 | ## Who is this video for? 4 | * Familiar with Node.js and interested in Go 5 | * Familiar with Express.js 6 | * Know what Go is 7 | * Want to start using Go 8 | 9 | ## What will you learn? 10 | * Some basic Go syntax 11 | * The basics of how Go compares to Node.js 12 | * Some basic folder structure principals 13 | 14 | ## What technologies are we using? 15 | * Go 16 | * [Fiber](https://docs.gofiber.io/) - Express-inspired web framework 17 | * MongoDB 18 | * Postman 19 | 20 | -------------------------------------------------------------------------------- /cmd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | 7 | "example.com/tomdoestech/internal/handlers" 8 | "github.com/gofiber/fiber/v2" 9 | ) 10 | 11 | func healthcheck(c *fiber.Ctx) error { 12 | return c.SendString("OK") 13 | } 14 | 15 | func main() { 16 | 17 | app := fiber.New() 18 | 19 | app.Use("/api", func(c *fiber.Ctx) error { 20 | fmt.Println("Hello from middleware") 21 | return c.Next() 22 | }) 23 | 24 | app.Get("/healthcheck", healthcheck) 25 | 26 | app.Post("/api/products", handlers.CreateProduct) 27 | app.Get("/api/products", handlers.GetAllProducts) 28 | 29 | fmt.Println("Hello world") 30 | 31 | log.Fatal(app.Listen(":3000")) 32 | 33 | } 34 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module example.com/tomdoestech 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/andybalholm/brotli v1.0.4 // indirect 7 | github.com/go-playground/locales v0.14.0 // indirect 8 | github.com/go-playground/universal-translator v0.18.0 // indirect 9 | github.com/go-playground/validator/v10 v10.10.0 // indirect 10 | github.com/go-stack/stack v1.8.0 // indirect 11 | github.com/gofiber/fiber/v2 v2.24.0 // indirect 12 | github.com/golang/snappy v0.0.3 // indirect 13 | github.com/klauspost/compress v1.13.6 // indirect 14 | github.com/leodido/go-urn v1.2.1 // indirect 15 | github.com/pkg/errors v0.9.1 // indirect 16 | github.com/valyala/bytebufferpool v1.0.0 // indirect 17 | github.com/valyala/fasthttp v1.31.0 // indirect 18 | github.com/valyala/tcplisten v1.0.0 // indirect 19 | github.com/xdg-go/pbkdf2 v1.0.0 // indirect 20 | github.com/xdg-go/scram v1.0.2 // indirect 21 | github.com/xdg-go/stringprep v1.0.2 // indirect 22 | github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect 23 | go.mongodb.org/mongo-driver v1.8.1 // indirect 24 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect 25 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect 26 | golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect 27 | golang.org/x/text v0.3.6 // indirect 28 | ) 29 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= 2 | github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= 3 | github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= 4 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 5 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 7 | github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 8 | github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= 9 | github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= 10 | github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= 11 | github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= 12 | github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0= 13 | github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= 14 | github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= 15 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 16 | github.com/gofiber/fiber/v2 v2.24.0 h1:18rpLoQMJBVlLtX/PwgHj3hIxPSeWfN1YeDJ2lEnzjU= 17 | github.com/gofiber/fiber/v2 v2.24.0/go.mod h1:MR1usVH3JHYRyQwMe2eZXRSZHRX38fkV+A7CPB+DlDQ= 18 | github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 19 | github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= 20 | github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 21 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 22 | github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= 23 | github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= 24 | github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= 25 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 26 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 27 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 28 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 29 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 30 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 31 | github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= 32 | github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= 33 | github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= 34 | github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= 35 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 36 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 37 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 38 | github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= 39 | github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= 40 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 41 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 42 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 43 | github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= 44 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= 45 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 46 | github.com/valyala/fasthttp v1.31.0 h1:lrauRLII19afgCs2fnWRJ4M5IkV0lo2FqA61uGkNBfE= 47 | github.com/valyala/fasthttp v1.31.0/go.mod h1:2rsYD01CKFrjjsvFxx75KlEUNpWNBY9JWD3K/7o2Cus= 48 | github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= 49 | github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= 50 | github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= 51 | github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= 52 | github.com/xdg-go/scram v1.0.2 h1:akYIkZ28e6A96dkWNJQu3nmCzH3YfwMPQExUYDaRv7w= 53 | github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= 54 | github.com/xdg-go/stringprep v1.0.2 h1:6iq84/ryjjeRmMJwxutI51F2GIPlP5BfTvXHeYjyhBc= 55 | github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= 56 | github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= 57 | github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= 58 | go.mongodb.org/mongo-driver v1.8.1 h1:OZE4Wni/SJlrcmSIBRYNzunX5TKxjrTS4jKSnA99oKU= 59 | go.mongodb.org/mongo-driver v1.8.1/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= 60 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 61 | golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= 62 | golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc= 63 | golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= 64 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI= 65 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 66 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 67 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 68 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 69 | golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 70 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 71 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= 72 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 73 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 74 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 75 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 76 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 77 | golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 78 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 79 | golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 80 | golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= 81 | golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 82 | golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 83 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 84 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 85 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 86 | golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 87 | golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= 88 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 89 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 90 | golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 91 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 92 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 93 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 94 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 95 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 96 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 97 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 98 | -------------------------------------------------------------------------------- /internal/db/db.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "context" 5 | "sync" 6 | 7 | "go.mongodb.org/mongo-driver/mongo" 8 | "go.mongodb.org/mongo-driver/mongo/options" 9 | ) 10 | 11 | var clientInstance *mongo.Client 12 | 13 | var mongoOnce sync.Once 14 | 15 | var clientInstanceError error 16 | 17 | type Collection string 18 | 19 | const ( 20 | ProductsCollection Collection = "products" 21 | ) 22 | 23 | const ( 24 | url = "mongodb://localhost:27017" 25 | Database = "products-api" 26 | ) 27 | 28 | func GetMongoClient() (*mongo.Client, error) { 29 | mongoOnce.Do(func() { 30 | clientOptions := options.Client().ApplyURI(url) 31 | 32 | client, err := mongo.Connect(context.TODO(), clientOptions) 33 | 34 | clientInstance = client 35 | 36 | clientInstanceError = err 37 | }) 38 | 39 | return clientInstance, clientInstanceError 40 | } 41 | -------------------------------------------------------------------------------- /internal/handlers/products.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "example.com/tomdoestech/internal/db" 8 | "github.com/go-playground/validator/v10" 9 | "github.com/gofiber/fiber/v2" 10 | "go.mongodb.org/mongo-driver/bson" 11 | "go.mongodb.org/mongo-driver/bson/primitive" 12 | ) 13 | 14 | type Product struct { 15 | ID primitive.ObjectID `json:"_id" bson:"_id" validate:"required"` 16 | CreatedAt time.Time `json:"createdAt" bson:"created_at" validate:"required"` 17 | UpdatedAt time.Time `json:"updatedAt" bson:"updated_at" validate:"required"` 18 | Title string `json:"title" bson:"title" validate:"required,min=12"` 19 | } 20 | 21 | type ErrorResponse struct { 22 | FailedField string 23 | Tag string 24 | Value string 25 | } 26 | 27 | func ValidateProductStruct(p Product) []*ErrorResponse { 28 | var errors []*ErrorResponse 29 | validate := validator.New() 30 | err := validate.Struct(p) 31 | 32 | if err != nil { 33 | for _, err := range err.(validator.ValidationErrors) { 34 | var element ErrorResponse 35 | element.FailedField = err.StructNamespace() 36 | element.Tag = err.Tag() 37 | element.Value = err.Param() 38 | errors = append(errors, &element) 39 | 40 | } 41 | } 42 | return errors 43 | } 44 | 45 | func CreateProduct(c *fiber.Ctx) error { 46 | product := Product{ 47 | ID: primitive.NewObjectID(), 48 | CreatedAt: time.Now(), 49 | UpdatedAt: time.Now(), 50 | } 51 | 52 | if err := c.BodyParser(&product); err != nil { 53 | return err 54 | } 55 | 56 | // Could also be broken down to be 57 | // err := c.BodyParser(&product) 58 | 59 | // if err != nil { 60 | // return err 61 | // } 62 | 63 | errors := ValidateProductStruct(product) 64 | 65 | if errors != nil { 66 | return c.JSON(errors) 67 | } 68 | 69 | client, err := db.GetMongoClient() 70 | 71 | if err != nil { 72 | return err 73 | } 74 | 75 | collection := client.Database(db.Database).Collection(string(db.ProductsCollection)) 76 | 77 | _, err = collection.InsertOne(context.TODO(), product) 78 | 79 | if err != nil { 80 | return err 81 | } 82 | 83 | return c.JSON(product) 84 | 85 | } 86 | 87 | func GetAllProducts(c *fiber.Ctx) error { 88 | client, err := db.GetMongoClient() 89 | 90 | var products []*Product 91 | 92 | if err != nil { 93 | return err 94 | } 95 | 96 | collection := client.Database(db.Database).Collection(string(db.ProductsCollection)) 97 | 98 | cur, err := collection.Find(context.TODO(), bson.D{ 99 | primitive.E{}, 100 | }) 101 | 102 | if err != nil { 103 | return err 104 | } 105 | 106 | for cur.Next(context.TODO()) { 107 | var p Product 108 | err := cur.Decode(&p) 109 | 110 | if err != nil { 111 | return err 112 | } 113 | 114 | products = append(products, &p) 115 | 116 | } 117 | 118 | return c.JSON(products) 119 | 120 | } 121 | -------------------------------------------------------------------------------- /js/db/index.js: -------------------------------------------------------------------------------- 1 | const { MongoClient } = require("mongodb"); 2 | 3 | const URI = "mongodb://localhost:27017"; 4 | 5 | const databaseStr = "products-api"; 6 | 7 | const collections = { 8 | products: "products", 9 | }; 10 | 11 | const client = new MongoClient(`${URI}`); 12 | 13 | async function connectToDB() { 14 | try { 15 | await client.connect(); 16 | console.log("Database connected"); 17 | } catch { 18 | await client.close(); 19 | console.error("Could not connect to database"); 20 | process.exit(1); 21 | } 22 | } 23 | 24 | const database = client.db(databaseStr); 25 | 26 | module.exports = { 27 | database, 28 | collections, 29 | connectToDB, 30 | }; 31 | -------------------------------------------------------------------------------- /js/main.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const { connectToDB } = require("./db"); 3 | const { createProduct, getProducts } = require("./products"); 4 | 5 | const app = express(); 6 | 7 | app.use(express.json()); 8 | 9 | app.post("/api/products", createProduct); 10 | 11 | app.get("/api/products", getProducts); 12 | 13 | async function main() { 14 | app.listen(4000, () => { 15 | console.log("App listening at http://localhost:4000"); 16 | }); 17 | 18 | await connectToDB(); 19 | 20 | console.log("Ready for work"); 21 | } 22 | 23 | /* 24 | * In Go we won't need to execute main 25 | */ 26 | main(); 27 | -------------------------------------------------------------------------------- /js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "js", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "author": "Tom Nagle", 6 | "license": "MIT", 7 | "dependencies": { 8 | "express": "^4.17.2", 9 | "mongodb": "^4.2.2" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /js/products/index.js: -------------------------------------------------------------------------------- 1 | const { database, collections } = require("../db"); 2 | 3 | async function createProduct(req, res) { 4 | const collection = database.collection(collections.products); 5 | 6 | const doc = { 7 | title: req.body.title, 8 | createdAt: new Date(), 9 | updatedAt: new Date(), 10 | }; 11 | 12 | const result = await collection.insertOne(doc); 13 | 14 | return res.send(result); 15 | } 16 | 17 | async function getProducts(req, res) { 18 | const collection = database.collection(collections.products); 19 | 20 | const cur = collection.find({}); 21 | 22 | let products = []; 23 | 24 | await cur.forEach((p) => { 25 | products.push(p); 26 | }); 27 | 28 | return res.send(products); 29 | } 30 | 31 | module.exports = { 32 | createProduct, 33 | getProducts, 34 | }; 35 | -------------------------------------------------------------------------------- /js/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@types/node@*": 6 | version "17.0.6" 7 | resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.6.tgz#cc1589c9ee853b389e67e8fb4384e0f250a139b9" 8 | integrity sha512-+XBAjfZmmivILUzO0HwBJoYkAyyySSLg5KCGBDFLomJo0sV6szvVLAf4ANZZ0pfWzgEds5KmGLG9D5hfEqOhaA== 9 | 10 | "@types/webidl-conversions@*": 11 | version "6.1.1" 12 | resolved "https://registry.yarnpkg.com/@types/webidl-conversions/-/webidl-conversions-6.1.1.tgz#e33bc8ea812a01f63f90481c666334844b12a09e" 13 | integrity sha512-XAahCdThVuCFDQLT7R7Pk/vqeObFNL3YqRyFZg+AqAP/W1/w3xHaIxuW7WszQqTbIBOPRcItYJIou3i/mppu3Q== 14 | 15 | "@types/whatwg-url@^8.2.1": 16 | version "8.2.1" 17 | resolved "https://registry.yarnpkg.com/@types/whatwg-url/-/whatwg-url-8.2.1.tgz#f1aac222dab7c59e011663a0cb0a3117b2ef05d4" 18 | integrity sha512-2YubE1sjj5ifxievI5Ge1sckb9k/Er66HyR2c+3+I6VDUUg1TLPdYYTEbQ+DjRkS4nTxMJhgWfSfMRD2sl2EYQ== 19 | dependencies: 20 | "@types/node" "*" 21 | "@types/webidl-conversions" "*" 22 | 23 | accepts@~1.3.7: 24 | version "1.3.7" 25 | resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" 26 | integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== 27 | dependencies: 28 | mime-types "~2.1.24" 29 | negotiator "0.6.2" 30 | 31 | array-flatten@1.1.1: 32 | version "1.1.1" 33 | resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" 34 | integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= 35 | 36 | base64-js@^1.3.1: 37 | version "1.5.1" 38 | resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" 39 | integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== 40 | 41 | body-parser@1.19.1: 42 | version "1.19.1" 43 | resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.1.tgz#1499abbaa9274af3ecc9f6f10396c995943e31d4" 44 | integrity sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA== 45 | dependencies: 46 | bytes "3.1.1" 47 | content-type "~1.0.4" 48 | debug "2.6.9" 49 | depd "~1.1.2" 50 | http-errors "1.8.1" 51 | iconv-lite "0.4.24" 52 | on-finished "~2.3.0" 53 | qs "6.9.6" 54 | raw-body "2.4.2" 55 | type-is "~1.6.18" 56 | 57 | bson@^4.6.0: 58 | version "4.6.0" 59 | resolved "https://registry.yarnpkg.com/bson/-/bson-4.6.0.tgz#15c3b39ba3940c3d915a0c44d51459f4b4fbf1b2" 60 | integrity sha512-8jw1NU1hglS+Da1jDOUYuNcBJ4cNHCFIqzlwoFNnsTOg2R/ox0aTYcTiBN4dzRa9q7Cvy6XErh3L8ReTEb9AQQ== 61 | dependencies: 62 | buffer "^5.6.0" 63 | 64 | buffer@^5.6.0: 65 | version "5.7.1" 66 | resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" 67 | integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== 68 | dependencies: 69 | base64-js "^1.3.1" 70 | ieee754 "^1.1.13" 71 | 72 | bytes@3.1.1: 73 | version "3.1.1" 74 | resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.1.tgz#3f018291cb4cbad9accb6e6970bca9c8889e879a" 75 | integrity sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg== 76 | 77 | content-disposition@0.5.4: 78 | version "0.5.4" 79 | resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" 80 | integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== 81 | dependencies: 82 | safe-buffer "5.2.1" 83 | 84 | content-type@~1.0.4: 85 | version "1.0.4" 86 | resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" 87 | integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== 88 | 89 | cookie-signature@1.0.6: 90 | version "1.0.6" 91 | resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" 92 | integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= 93 | 94 | cookie@0.4.1: 95 | version "0.4.1" 96 | resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1" 97 | integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA== 98 | 99 | debug@2.6.9: 100 | version "2.6.9" 101 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" 102 | integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== 103 | dependencies: 104 | ms "2.0.0" 105 | 106 | denque@^2.0.1: 107 | version "2.0.1" 108 | resolved "https://registry.yarnpkg.com/denque/-/denque-2.0.1.tgz#bcef4c1b80dc32efe97515744f21a4229ab8934a" 109 | integrity sha512-tfiWc6BQLXNLpNiR5iGd0Ocu3P3VpxfzFiqubLgMfhfOw9WyvgJBd46CClNn9k3qfbjvT//0cf7AlYRX/OslMQ== 110 | 111 | depd@~1.1.2: 112 | version "1.1.2" 113 | resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" 114 | integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= 115 | 116 | destroy@~1.0.4: 117 | version "1.0.4" 118 | resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" 119 | integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= 120 | 121 | ee-first@1.1.1: 122 | version "1.1.1" 123 | resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" 124 | integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= 125 | 126 | encodeurl@~1.0.2: 127 | version "1.0.2" 128 | resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" 129 | integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= 130 | 131 | escape-html@~1.0.3: 132 | version "1.0.3" 133 | resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" 134 | integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= 135 | 136 | etag@~1.8.1: 137 | version "1.8.1" 138 | resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" 139 | integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= 140 | 141 | express@^4.17.2: 142 | version "4.17.2" 143 | resolved "https://registry.yarnpkg.com/express/-/express-4.17.2.tgz#c18369f265297319beed4e5558753cc8c1364cb3" 144 | integrity sha512-oxlxJxcQlYwqPWKVJJtvQiwHgosH/LrLSPA+H4UxpyvSS6jC5aH+5MoHFM+KABgTOt0APue4w66Ha8jCUo9QGg== 145 | dependencies: 146 | accepts "~1.3.7" 147 | array-flatten "1.1.1" 148 | body-parser "1.19.1" 149 | content-disposition "0.5.4" 150 | content-type "~1.0.4" 151 | cookie "0.4.1" 152 | cookie-signature "1.0.6" 153 | debug "2.6.9" 154 | depd "~1.1.2" 155 | encodeurl "~1.0.2" 156 | escape-html "~1.0.3" 157 | etag "~1.8.1" 158 | finalhandler "~1.1.2" 159 | fresh "0.5.2" 160 | merge-descriptors "1.0.1" 161 | methods "~1.1.2" 162 | on-finished "~2.3.0" 163 | parseurl "~1.3.3" 164 | path-to-regexp "0.1.7" 165 | proxy-addr "~2.0.7" 166 | qs "6.9.6" 167 | range-parser "~1.2.1" 168 | safe-buffer "5.2.1" 169 | send "0.17.2" 170 | serve-static "1.14.2" 171 | setprototypeof "1.2.0" 172 | statuses "~1.5.0" 173 | type-is "~1.6.18" 174 | utils-merge "1.0.1" 175 | vary "~1.1.2" 176 | 177 | finalhandler@~1.1.2: 178 | version "1.1.2" 179 | resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" 180 | integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== 181 | dependencies: 182 | debug "2.6.9" 183 | encodeurl "~1.0.2" 184 | escape-html "~1.0.3" 185 | on-finished "~2.3.0" 186 | parseurl "~1.3.3" 187 | statuses "~1.5.0" 188 | unpipe "~1.0.0" 189 | 190 | forwarded@0.2.0: 191 | version "0.2.0" 192 | resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" 193 | integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== 194 | 195 | fresh@0.5.2: 196 | version "0.5.2" 197 | resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" 198 | integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= 199 | 200 | http-errors@1.8.1: 201 | version "1.8.1" 202 | resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c" 203 | integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g== 204 | dependencies: 205 | depd "~1.1.2" 206 | inherits "2.0.4" 207 | setprototypeof "1.2.0" 208 | statuses ">= 1.5.0 < 2" 209 | toidentifier "1.0.1" 210 | 211 | iconv-lite@0.4.24: 212 | version "0.4.24" 213 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" 214 | integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== 215 | dependencies: 216 | safer-buffer ">= 2.1.2 < 3" 217 | 218 | ieee754@^1.1.13: 219 | version "1.2.1" 220 | resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" 221 | integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== 222 | 223 | inherits@2.0.4: 224 | version "2.0.4" 225 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 226 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 227 | 228 | ipaddr.js@1.9.1: 229 | version "1.9.1" 230 | resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" 231 | integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== 232 | 233 | media-typer@0.3.0: 234 | version "0.3.0" 235 | resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" 236 | integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= 237 | 238 | memory-pager@^1.0.2: 239 | version "1.5.0" 240 | resolved "https://registry.yarnpkg.com/memory-pager/-/memory-pager-1.5.0.tgz#d8751655d22d384682741c972f2c3d6dfa3e66b5" 241 | integrity sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg== 242 | 243 | merge-descriptors@1.0.1: 244 | version "1.0.1" 245 | resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" 246 | integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= 247 | 248 | methods@~1.1.2: 249 | version "1.1.2" 250 | resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" 251 | integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= 252 | 253 | mime-db@1.51.0: 254 | version "1.51.0" 255 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.51.0.tgz#d9ff62451859b18342d960850dc3cfb77e63fb0c" 256 | integrity sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g== 257 | 258 | mime-types@~2.1.24: 259 | version "2.1.34" 260 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.34.tgz#5a712f9ec1503511a945803640fafe09d3793c24" 261 | integrity sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A== 262 | dependencies: 263 | mime-db "1.51.0" 264 | 265 | mime@1.6.0: 266 | version "1.6.0" 267 | resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" 268 | integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== 269 | 270 | mongodb-connection-string-url@^2.3.2: 271 | version "2.4.1" 272 | resolved "https://registry.yarnpkg.com/mongodb-connection-string-url/-/mongodb-connection-string-url-2.4.1.tgz#6b3c6c40133a0ad059fe9a0abda64b2a1cb4e8b4" 273 | integrity sha512-d5Kd2bVsKcSA7YI/yo57fSTtMwRQdFkvc5IZwod1RRxJtECeWPPSo7zqcUGJELifRA//Igs4spVtYAmvFCatug== 274 | dependencies: 275 | "@types/whatwg-url" "^8.2.1" 276 | whatwg-url "^11.0.0" 277 | 278 | mongodb@^4.2.2: 279 | version "4.2.2" 280 | resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-4.2.2.tgz#cd70568bd96003877e35358ad17a0c5de35c6dfd" 281 | integrity sha512-zt8rCTnTKyMQppyt63qMnrLM5dbADgUk18ORPF1XbtHLIYCyc9hattaYHi0pqMvNxDpgGgUofSVzS+UQErgTug== 282 | dependencies: 283 | bson "^4.6.0" 284 | denque "^2.0.1" 285 | mongodb-connection-string-url "^2.3.2" 286 | optionalDependencies: 287 | saslprep "^1.0.3" 288 | 289 | ms@2.0.0: 290 | version "2.0.0" 291 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 292 | integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= 293 | 294 | ms@2.1.3: 295 | version "2.1.3" 296 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" 297 | integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== 298 | 299 | negotiator@0.6.2: 300 | version "0.6.2" 301 | resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" 302 | integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== 303 | 304 | on-finished@~2.3.0: 305 | version "2.3.0" 306 | resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" 307 | integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= 308 | dependencies: 309 | ee-first "1.1.1" 310 | 311 | parseurl@~1.3.3: 312 | version "1.3.3" 313 | resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" 314 | integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== 315 | 316 | path-to-regexp@0.1.7: 317 | version "0.1.7" 318 | resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" 319 | integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= 320 | 321 | proxy-addr@~2.0.7: 322 | version "2.0.7" 323 | resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" 324 | integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== 325 | dependencies: 326 | forwarded "0.2.0" 327 | ipaddr.js "1.9.1" 328 | 329 | punycode@^2.1.1: 330 | version "2.1.1" 331 | resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" 332 | integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== 333 | 334 | qs@6.9.6: 335 | version "6.9.6" 336 | resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.6.tgz#26ed3c8243a431b2924aca84cc90471f35d5a0ee" 337 | integrity sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ== 338 | 339 | range-parser@~1.2.1: 340 | version "1.2.1" 341 | resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" 342 | integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== 343 | 344 | raw-body@2.4.2: 345 | version "2.4.2" 346 | resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.2.tgz#baf3e9c21eebced59dd6533ac872b71f7b61cb32" 347 | integrity sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ== 348 | dependencies: 349 | bytes "3.1.1" 350 | http-errors "1.8.1" 351 | iconv-lite "0.4.24" 352 | unpipe "1.0.0" 353 | 354 | safe-buffer@5.2.1: 355 | version "5.2.1" 356 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" 357 | integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== 358 | 359 | "safer-buffer@>= 2.1.2 < 3": 360 | version "2.1.2" 361 | resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" 362 | integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== 363 | 364 | saslprep@^1.0.3: 365 | version "1.0.3" 366 | resolved "https://registry.yarnpkg.com/saslprep/-/saslprep-1.0.3.tgz#4c02f946b56cf54297e347ba1093e7acac4cf226" 367 | integrity sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag== 368 | dependencies: 369 | sparse-bitfield "^3.0.3" 370 | 371 | send@0.17.2: 372 | version "0.17.2" 373 | resolved "https://registry.yarnpkg.com/send/-/send-0.17.2.tgz#926622f76601c41808012c8bf1688fe3906f7820" 374 | integrity sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww== 375 | dependencies: 376 | debug "2.6.9" 377 | depd "~1.1.2" 378 | destroy "~1.0.4" 379 | encodeurl "~1.0.2" 380 | escape-html "~1.0.3" 381 | etag "~1.8.1" 382 | fresh "0.5.2" 383 | http-errors "1.8.1" 384 | mime "1.6.0" 385 | ms "2.1.3" 386 | on-finished "~2.3.0" 387 | range-parser "~1.2.1" 388 | statuses "~1.5.0" 389 | 390 | serve-static@1.14.2: 391 | version "1.14.2" 392 | resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.2.tgz#722d6294b1d62626d41b43a013ece4598d292bfa" 393 | integrity sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ== 394 | dependencies: 395 | encodeurl "~1.0.2" 396 | escape-html "~1.0.3" 397 | parseurl "~1.3.3" 398 | send "0.17.2" 399 | 400 | setprototypeof@1.2.0: 401 | version "1.2.0" 402 | resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" 403 | integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== 404 | 405 | sparse-bitfield@^3.0.3: 406 | version "3.0.3" 407 | resolved "https://registry.yarnpkg.com/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz#ff4ae6e68656056ba4b3e792ab3334d38273ca11" 408 | integrity sha1-/0rm5oZWBWuks+eSqzM004JzyhE= 409 | dependencies: 410 | memory-pager "^1.0.2" 411 | 412 | "statuses@>= 1.5.0 < 2", statuses@~1.5.0: 413 | version "1.5.0" 414 | resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" 415 | integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= 416 | 417 | toidentifier@1.0.1: 418 | version "1.0.1" 419 | resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" 420 | integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== 421 | 422 | tr46@^3.0.0: 423 | version "3.0.0" 424 | resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9" 425 | integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA== 426 | dependencies: 427 | punycode "^2.1.1" 428 | 429 | type-is@~1.6.18: 430 | version "1.6.18" 431 | resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" 432 | integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== 433 | dependencies: 434 | media-typer "0.3.0" 435 | mime-types "~2.1.24" 436 | 437 | unpipe@1.0.0, unpipe@~1.0.0: 438 | version "1.0.0" 439 | resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" 440 | integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= 441 | 442 | utils-merge@1.0.1: 443 | version "1.0.1" 444 | resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" 445 | integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= 446 | 447 | vary@~1.1.2: 448 | version "1.1.2" 449 | resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" 450 | integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= 451 | 452 | webidl-conversions@^7.0.0: 453 | version "7.0.0" 454 | resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" 455 | integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== 456 | 457 | whatwg-url@^11.0.0: 458 | version "11.0.0" 459 | resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-11.0.0.tgz#0a849eebb5faf2119b901bb76fd795c2848d4018" 460 | integrity sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ== 461 | dependencies: 462 | tr46 "^3.0.0" 463 | webidl-conversions "^7.0.0" 464 | --------------------------------------------------------------------------------