Если вы видите что-то необычное, просто сообщите мне. Skip to main content

Строим свой первый Rest API с помощью GO

ThereУ isнас threeесть partтри toнеобходимые thisчасти workshop.для построения.

  • API
  • Rest API
  • Rest API Rest API withна GO

API

IfЕсли youвы haveблизки beenк aroundкомпьютерам aдостаточно computerдавно, forвы longвозможно enoughслышали youоб probablyэтих heardвещах. ofЧто this thing. What is thisтакое API?

API standsзначит forпрограммный Applicationинтерфейс Programприложения. Interface.Как Likeи mostбольшинство thingвещей inв computerинформатике scienceабревиатура theне abbreviationсильно doesn't help much.помогает.

WhatНа itсамом actuallyделе meansэто isзначит, itчто exposesон functionalityвыставялет withoutфункциональность exposingбез internals.выставления Ifвнутренностй. youЕсли programваша inпрограмма aнаписана languageна thatязыке supportsкоторый writingподдерживает functionsфункции orили methodsметоды(большинство (prettyязыков) muchвы allдолжны programmingпонимать, languages)о youчём wouldидет totally understand what I am talking about.речь.

func addNumber(a, b int) int {
    // DO AMAZING MATH HERE
    // and return the result
}

EvenДаже ifесли youвы areсовсем superновичок new toв go, youвы canможете tellсказать, thisчто functionфункция isвозвращает aboutрезультат addingсложения twoдвху numbers and returning the result.

For the user of the function you just call the function and never worry about how the function is doing what it does (don't trust every function)чисел.

ThatsКак allпользователь anфункции вы просто вызываете функцию и не беспокоитесь о том, функция выполняет свою работу(не доверяйтся всем функциям подряд)

Это всё что такое API. API is.может Anбыть APIфункция couldкоторую beвы aнаписали, functionили youфункция wrote,из orбиблиотеки aили functionметод fromфреймворка, a library or method from a framework, or aили http endpoint.точка доступа.

Rest API

MostБольшинство APIsAPI writtenнаписаные thisв daysнаши areдни это web apis.api. Don'tТолько quoteне meцитируйте onменя, thatтак oneкак becauseя Iне didn'tделал doникаких anyисследований researchчтобы toполучить getнастоящее aчисло. properНо number.учитывая 😁 But given the number ofколичество web servicesсервисов andи webприлжений applicationя Iне don'tдумаю, thinkчто Iя amдалеко tooот far off.правды.

What is REST

REST is- acronymэто forакроним для REpresentational State Transfer.Transfer(передача Itрепрезентативного isсостояния). architecturalЭто styleархитекторный forстиль distributedраспределнной hypermediaгипермедия systemsсистемы andи wasбыла firstвпервые presentedпредставлена byРоем RoyФилдингом Fielding inв 2000 inгоду hisв famousего dissertation.известной диссертации.

LikeКак anyлюбые otherдругие architecturalархитектурные style,стили, REST alsoтак doesже haveимеет it’s ownсвои 6 guidingформирующих constraintsего whichограничений, mustкоторые beдолжны satisfiedбыть ifудовлетворены anи interfaceесли needsинтерфейс toхочет be referred asназываться RESTful. TheseЭти principlesпринципы areприведены listed below.ниже.

GuidingВедущие Principles ofпринципы REST

  1. Client–serverКлиент-сервер - ByРазделяя separatingпроблемы theпользовательских userинтерфейсов interfaceот concernsпроблем fromхранения theданных, dataвы storageулучшаем concerns,переносимость weпользовательского improveинтерфейса theна portabilityмногие ofплатформы theи userулучшаем interfaceмасштабируемость acrossупрощением multipleсервреных platforms and improve scalability by simplifying the server components.компонентов.
  2. StatelessНе хранит Eachсостояние request- fromкаждый clientзапрос toот serverклиента mustк containсерверу allдолжен ofсодержать theвсю informationнеобходимую necessaryинформацию toдля understandпонимания theзапросов, request,и andне cannotможет takeвоспользоваться advantageлюбых ofсохраненным anyсодеражением storedна contextсервере. onСостояние theсессии server.отсюда Sessionхранится stateполностью isна therefore kept entirely on the client.клиенте.
  3. CacheableКешируемость - CacheОграничения constraintsкэша requireтребуют. thatчтобы theданные dataв withinответе aна responseзапрос toбыли aявно requestили beне implicitlyявно orобозначены explicitlyкак labeledкэшируемые asили cacheableне orкешуруемые. non-cacheable.Если Ifответ aкэшируемый, responseтогда isправо cacheable,кэша thenклиента aдля clientпереиспользования cacheданные isответа givenв theдальнейшем, rightпохожем to reuse that response data for later, equivalent requests.запросе.
  4. UniformУниверсальный interfaceинтерфейс - ByПрименя applyingпринцыпи theпрограмной softwareинженерии engineeringв principleцелом ofк generalityкомпонентам toинтерфейса, theобщая componentархитектура interface,системы theупрощена overallи systemвидимость architectureвзаимодействий isулучшена. simplifiedДля andтого, theчтобы visibilityполучить ofуниверсальный interactionsинтерфейс, isмножество improved.универсальных Inограничений orderнужны toдля obtainуказания aповедения uniform interface, multiple architectural constraints are needed to guide the behavior of components.компонентов. REST isопределяется definedчетырьмя byограничивающими fourинтерфейсами: interface
      constraints:
    • Определение identification of resources; manipulation of resources through representations; self-descriptive messages; and, hypermedia as the engine of application state.ресурса
    • LayeredУпралвение systemресурсами через The layered system style allows an architecture to be composed of hierarchical layers by constraining component behavior such that each component cannot “see” beyond the immediate layer with which they are interacting.репрезентацию
    • CodeСамоописываемое onсообщение
    • demand
    • Гипермедия - как движок состояния приложения.
  5. Слоёная система - Стиль слоёной системы позволяет архитектуре быть составленной из иерархических слоёв ограниченными поведением компонентов, так, что каждый компоненто не может "смотреть сковозь" слой с которым происходит взаимодействие.
  6. Код по запросу(optional)по желанию) - REST allowsпозволяет clientрасширить functionalityфункциональность toклиенту beскачать extendedи byвыполнив downloadingкод andв executingформе codeаплетов inили theскриптов. formЭто ofурощает appletsклиентов orс scripts.помощью Thisсокращения simplifiesколичества clientsособенностей byтребуемых reducingдля theпогдготовки number of features required to be pre-implemented. To see an example of a REST API we can useреализации.

HTTP Verbsглаголы

TheseВот areнесколько someусловностей conventionsсоблюдаемы HTTP apisAPI. follow.Это Theseне areчасть actuallyспецификации notREST. partНо ofнам Restнужно specification.это Butпонять, weчто needбы toиспользовать understandREST theseAPI to fully utilize Rest API.по-понной.

HTTP definesопределяет aнабор setметодов-запросов ofчтобы requestуказать methodsжелаемое toдействие indicateдля theданного desiredресурса. actionТак toже beони performedмогут forбыть aсуществительными, givenэти resource.методы-запросы Althoughиногда they can also be nouns, these request methods are sometimes referred asназываются HTTP verbs.глаголы. EachКаждый ofиз themних implementsреализует aразличную differentсемантику, semantic,но butкоторые someобщие commonособенности featuresподелены areна sharedгруппы: byнапример aзапрос groupможет ofбыть them:безопасный, e.g.неизменяемый aили request method can be safe, idempotent, or cacheable.кэшируемый.

GET The- метод запрашивает представление указанного ресурка. Запросы используещие GET methodдолжны requestsтолько aполучать representation of the specified resource. Requests using GETshould only retrieve data.данные.

HEADThe HEAD- methodметод asksпросит forответ aидентичный responseGET identicalзапросу, toно thatопускает of a GET request, but without the response body.body.

POSTThe POST- methodметодв isиспользуется usedдля toотправки submitсущности anуказанному entityресурсу, toчасто theявляется specifiedпричиной resource,изменения oftenизменения causingсостояния aили changeимеет inпобочный stateэффект orна side effects on the server.сервер.

PUTThe PUT- methodметод replacesзаменяет allвсе currentтекущее representationsпредставление ofцелевого theресурса targetзагруженным resourceв with the request payload.запросе.

DELETEThe DELETE- methodметод deletesудаляет theуказанный specified resource.ресурс.

CONNECTThe CONNECT- methodметод establishesустанавливает aтоннель tunnelк toсерверу theуказанному serverкак identified by the target resource.целевой.

OPTIONSThe OPTIONS- methodметод isиспользуется usedдля toописания describeвозможностей theподключения communicationк optionsудаленному for the target resource.ресурсу.

TRACEThe TRACE- methodметод performsпроизводит aсообщение messageпетлевого loop-backконтроля testна alongпути theк pathресурсу to the target resource.цели.

PATCHThe PATCH- methodметод isиспользуется usedдля toприменения applyчастичных partialизменений modifications to a resource.ресурса.

THESEЭТО AREВСЁ ALL LIES.ВРАНЬЕ.

StatusСтатус Codesкоды

1xx InformationИнформация

100 Continue 101 Switching Protocols 102 Processing 2xx Success

200 OK 201 Created 202 Accepted 203 Non-authoritative Information 204 No Content 205 Reset Content 206 Partial Content 207 Multi-Status 208 Already Reported 226 IM Used 3xx Redirects

300 Multiple Choices 301 Moved Permanently 302 Found 303 See Other 304 Not Modified 305 Use Proxy 307 Temporary Redirect 308 Permanent Redirect 4xx Client Error

400 Bad Request 401 Unauthorized 402 Payment Required 403 Forbidden 404 Not Found 405 Method Not Allowed 406 Not Acceptable 407 Proxy Authentication Required 408 Request Timeout 409 Conflict 410 Gone 411 Length Required 412 Precondition Failed 413 Payload Too Large 414 Request-URI Too Long 415 Unsupported Media Type 416 Requested Range Not Satisfiable 417 Expectation Failed 418 I'm a teapot 421 Misdirected Request 422 Unprocessable Entity 423 Locked 424 Failed Dependency 426 Upgrade Required 428 Precondition Required 429 Too Many Requests 431 Request Header Fields Too Large 444 Connection Closed Without Response 451 Unavailable For Legal Reasons 499 Client Closed Request 5xx Server Error

500 Internal Server Error 501 Not Implemented 502 Bad Gateway 503 Service Unavailable 504 Gateway Timeout 505 HTTP Version Not Supported 506 Variant Also Negotiates 507 Insufficient Storage 508 Loop Detected 510 Not Extended 511 Network Authentication Required 599 Network Connect Timeout Error This also has no actual meaning.

TerminologiesТерминология

TheНиже following- areсамые theважные mostпонятия importantсвязанные terms related toс REST APIsAPI.

  • ResourceРесурс is- anобъект objectили orпредставление representationчего-то, ofчто something,имеет whichнекоторую hasассоциацию someданных associatedс dataним withи itможет andиметь thereнабор canметодов beдля setобработки ofоных. methodsК toпримеру: operateЖивтоные, onшколы it.или E.g.работники Animals,ресурсы, schoolsа anddelete, employeesadd, areupdate resources- andоперации delete,для add,обработки updateэтих are the operations to be performed on these resources.данных.
  • CollectionsКоллекции are- setнаборы ofресурсов: resources,Компании e.gэто Companiesнаборы isресурсов the collection of Company resource.компаний.
  • URL (Uniform- Resourceэто Locator)путь isпо aкоторому pathресурс throughможет whichбыть aопределен resourceи canдействия beкоторые locatedнеобходимо andс someним actions can be performed on it.произвести.

API Endpoint

ThisВот isкак whatвыглдяит aпример APIтакой endpointточки:

looks like.
https://www.github.com/golang/go/search?q=http&type=Commits
This

Разделим URL canна be broken into these partsчасти:

protocol	subdomain	domain	path	Port	query
http/https	subdomain	base-url	resource/some-other-resource	some-port	key value pair
https	www	github.com	golang/go/search	80	?q=http&type=Commits

Протоколы

Как браузер или клиент должен взаимодействовать с сервером.

ProtocolПоддомен

HowПодраздел theглавного browser or client should communicate with the server.домена.

SubdomainПорт

SubПорт Divisionсервера ofна theкотором mainзапущено domainприложения. По умолчанию это 80. Но в большинстве случаем вы его не видим.

PortПеть

PortПусть on- theпараметры server the application is running on. By default its 80. So most cases we don't see it

Path

Path parameters in a RestREST API representsотражающие resources.ресурсы.

https://jsonplaceholder.typicode.com/posts/1/comments
posts/1/comments

ThisЭтот pathпусть isотражает representingкоментарии the1го 1stпоста posts resource'c commentsресурса.

BasicБазовой structureструктурой isявляется

top-level-resource/<some-identifier>/secondary-resource/<some-identifier>/...

QueryЗапрос

QueriesЗапросы are- keyпары valueключ-значение pairsинформации, ofиспользуемый information,в usedосновном mostlyдля forцелей filtering purposes.фильтрования.

https://jsonplaceholder.typicode.com/posts?userId=1

PartsЧасть afterпосле the? ?это isпараметры theзапроса. queryУ parameters.нас Weесть haveтолько onlyодин oneзапрос queryтут: here. userId=userID=1.

HeadersЗаголовки

ThisЭто wasне notчасть partсамого ofURL, theно URLзаголовки itselfэто butчасть headerсетевого isкомпонента aпосылаемые partклиентом ofили networkсервером. componentВ sentзависимости byот theтого, clientкто orпослал theего. server.Есть Based1 onтипа who sends it. There are two kinds of headerзаголовков.

  1. RequestЗаголовок Headerзапроса (client -> server)
  2. ResponseЗаголовок Headerответа (server -> client)

BodyТело

YouВы canможете addдобавить extraдополнительную informationинфомацию toв bothобза theзапроса requestк toсерверу theи serverк andответу toот the response from the server.сервера.

ResponseТип Typeответа

UsuallyОбычно JSON orили XML.

NowВ aнаши daysдни it'sэто mostlyобычно JSON.

Rest API withна GO

ThisЭто isто whyпочему youвы areтут. here.Ну Orили Iя, hopeпо thisкрайней, isмере whyя youнадеюсь areна here.это.

LanguageЕсли Designвы inпишите theREST ServiceAPI, ofпочему Softwareвы Engineeringдолжны выбрать Go?

This

    above
  • Он articleкомпилируем. cameВы outполучаете inмаленькие 2012.бинарники.
  • But
  • Он stillбыстр. prettyМедленнее relevant to learn the ideology behind go.

    If you are writing Rest API why should you choose go?

    It's compiled. So you get small binaries. It's fast. (slower thanчем c/c++ orили rust)rust, butно fasterбыстрее thanчем mostбольшинство otherдругих языков web programmingпрограммирования.

  • languages.
  • Он It'sлегок simpleв toизучении.
  • understand.
  • Он Itработает worksотлично reallyв wellмире inмикросервисов the- microservicesэто worldпричини for reason noномер 1.

net/http

TheСтандартная standardбиблиотека library inв go comesидущая withс thenet/http пакетом, что является отличной точкой отсчета для построения REST API. И большинство других библиотек добавлюят особенности тоже взаимодействуют с net/http пакетом, поэтому понимание пакется является критичным для использования golang в качестве REST API.

net/http
package,
which

Нам, isвозможно, anне excellentнужно startingзнать pointвсё forв buildingпакете RestAPIs. And most other libraries the adds some additional feature are also interoperable with the net/http. packageно soесть understandingнесколько theвещей, net/httpкоторый packageмы isдолжны crucialзнать toдля using golang for RestAPIs.

net/http

We probably don't need to know everything in the net/http package. But there are a few things we should know to get started.начала.

The Handler Interface

I am never a proposer of memorizing something but as Todd Mcleod in his course mentions over and over. We need to memorize the Handler interface. type Handler interface { ServeHTTP(ResponseWriter, *Request) } And here it is.

It has one method and one method only.

A struct or object will be Handler if it has one method ServeHTTP which takes ResponseWriter and pointer to Request.

With all our knowledge now we are ready to do some damage.

Let's Begin

I think now we are ready to get started.

That was a lot of theory. I promised you will build you first RestAPI.

Simple Rest API

So let's jump right into it.

In a folder where you want to write your go code go mod init api-test Create a new file, you can name it whatever you want.

I am calling mine main.go

package main

import (
    "log"
    "net/http"
)

type server struct{}

func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
        w.WriteHeader(http.StatusOK)
    w.Write([]byte(`{"message": "hello world"}`))
}

func main() {
    s := &server{}
    http.Handle("/", s)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

Lets break down this code.

At the top we have our package main all go executable need a main package.

We have our imports. log for logging some error if it happens. net/http because we are writing a rest api.

Then we have a struct called server. It has no fields. We will add a method to this server ServeHTTP and that will satisfy the Handler interface. One thing you will notice in go we don't have to explicitly say the interface we are implementing. The compiler is smart enough to figure that out. In the ServeHTTP method we set httpStatus 200 to denote its the request was a success. We se the content type to application/json so the client understands when we send back json as payload. Finally we write

{"message": "hello world"}

To the response.

Lets run our server

go run main.go

If you had installed postman before, let's test our app with postman real quick.

Get returns us our message.

Great work!

But Wait.

Lets see what other HTTP verbs our application serves.

In postman we can change the Type of request we make. Click on the dropdown and select something else. Lets say we do post.

Now if we run the request, we get back the same result.

Well its not really a bug per se. But in most cases we probably want to do different things based on the request types.

Lets see how we can do that.

We will modify our ServeHTTP method with the following.

func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    switch r.Method {
    case "GET":
        w.WriteHeader(http.StatusOK)
        w.Write([]byte(`{"message": "get called"}`))
    case "POST":
        w.WriteHeader(http.StatusCreated)
        w.Write([]byte(`{"message": "post called"}`))
    case "PUT":
        w.WriteHeader(http.StatusAccepted)
        w.Write([]byte(`{"message": "put called"}`))
    case "DELETE":
        w.WriteHeader(http.StatusOK)
        w.Write([]byte(`{"message": "delete called"}`))
    default:
        w.WriteHeader(http.StatusNotFound)
        w.Write([]byte(`{"message": "not found"}`))
    }
}

If our server is already running lets stop it with ctrl-c

Run it again.

go run main.go

Test it with postman or curl again.

One thing you may have noticed is that we are using our server struct literally for attaching a method to.

The go team knew this was an inconvenience and gave us HandleFunc Its a method on the http package that allows us to pass a function that has the same signature as the ServeHTTP and can serve a route.

We can clean up our code a little bit with this

package main

import (
    "log"
    "net/http"
)

func home(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    switch r.Method {
    case "GET":
        w.WriteHeader(http.StatusOK)
        w.Write([]byte(`{"message": "get called"}`))
    case "POST":
        w.WriteHeader(http.StatusCreated)
        w.Write([]byte(`{"message": "post called"}`))
    case "PUT":
        w.WriteHeader(http.StatusAccepted)
        w.Write([]byte(`{"message": "put called"}`))
    case "DELETE":
        w.WriteHeader(http.StatusOK)
        w.Write([]byte(`{"message": "delete called"}`))
    default:
        w.WriteHeader(http.StatusNotFound)
        w.Write([]byte(`{"message": "not found"}`))
    }
}

func main() {
    http.HandleFunc("/", home)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

Functionality should be exactly the same.

Gorilla Mux

net/http built in methods are great. We can write a server with no external libraries. But net/http has its limitations. There is no direct way to handle path parameters. Just like request methods we have to handle path and query parameters manually.

Gorilla Mux is a very popular library that works really well to net/http package and helps us do a few things that makes api building a breeze.

Using Gorilla Mux

To install a module we can use go get

Go get uses git under the hood.

In the same folder you have your go.mod and main.go file run

go get github.com/gorilla/mux

We change our code to this

package main

import (
    "log"
    "net/http"

    "github.com/gorilla/mux"
)

func home(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    switch r.Method {
    case "GET":
        w.WriteHeader(http.StatusOK)
        w.Write([]byte(`{"message": "get called"}`))
    case "POST":
        w.WriteHeader(http.StatusCreated)
        w.Write([]byte(`{"message": "post called"}`))
    case "PUT":
        w.WriteHeader(http.StatusAccepted)
        w.Write([]byte(`{"message": "put called"}`))
    case "DELETE":
        w.WriteHeader(http.StatusOK)
        w.Write([]byte(`{"message": "delete called"}`))
    default:
        w.WriteHeader(http.StatusNotFound)
        w.Write([]byte(`{"message": "not found"}`))
    }
}

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/", home)
    log.Fatal(http.ListenAndServe(":8080", r))
}

Looks like nothing really changed except for a new import and line 32.

HandleFunc HTTP Methods

But now we can do a little bit more with our HandleFunc Like making each function handle a specific HTTP Method.

It looks something like this

package main

import (
    "log"
    "net/http"

    "github.com/gorilla/mux"
)

func get(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusOK)
    w.Write([]byte(`{"message": "get called"}`))
}

func post(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusCreated)
    w.Write([]byte(`{"message": "post called"}`))
}

func put(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusAccepted)
    w.Write([]byte(`{"message": "put called"}`))
}

func delete(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusOK)
    w.Write([]byte(`{"message": "delete called"}`))
}

func notFound(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusNotFound)
    w.Write([]byte(`{"message": "not found"}`))
}

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/", get).Methods(http.MethodGet)
    r.HandleFunc("/", post).Methods(http.MethodPost)
    r.HandleFunc("/", put).Methods(http.MethodPut)
    r.HandleFunc("/", delete).Methods(http.MethodDelete)
    r.HandleFunc("/", notFound)
    log.Fatal(http.ListenAndServe(":8080", r))
}

If you run this it should still do the exact same thing.

At this point you might be wondering how is doing the same thing with more lines of code a good thing?

But think of it this way. Our code became much cleaner and much more readable.

Clear is Better than Clever Rob Pike

Subrouter

func main() {
    r := mux.NewRouter()
    api := r.PathPrefix("/api/v1").Subrouter()
    api.HandleFunc("", get).Methods(http.MethodGet)
    api.HandleFunc("", post).Methods(http.MethodPost)
    api.HandleFunc("", put).Methods(http.MethodPut)
    api.HandleFunc("", delete).Methods(http.MethodDelete)
    api.HandleFunc("", notFound)
    log.Fatal(http.ListenAndServe(":8080", r))
}

Everything else stays the same except we are creating something called a sub-router. Sub-router are really useful when we want to support multiple resources. Helps us group the content as well as save us from retyping the same path prefix.

We move our api to api/v1 . This way we can create v2 of our api if need be..

Path and Query Parameter

package main

import (
    "fmt"
    "log"
    "net/http"
    "strconv"

    "github.com/gorilla/mux"
)

func get(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusOK)
    w.Write([]byte(`{"message": "get called"}`))
}

func post(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusCreated)
    w.Write([]byte(`{"message": "post called"}`))
}

func put(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusAccepted)
    w.Write([]byte(`{"message": "put called"}`))
}

func delete(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusOK)
    w.Write([]byte(`{"message": "delete called"}`))
}

func params(w http.ResponseWriter, r *http.Request) {
    pathParams := mux.Vars(r)
    w.Header().Set("Content-Type", "application/json")

    userID := -1
    var err error
    if val, ok := pathParams["userID"]; ok {
        userID, err = strconv.Atoi(val)
        if err != nil {
            w.WriteHeader(http.StatusInternalServerError)
            w.Write([]byte(`{"message": "need a number"}`))
            return
        }
    }

    commentID := -1
    if val, ok := pathParams["commentID"]; ok {
        commentID, err = strconv.Atoi(val)
        if err != nil {
            w.WriteHeader(http.StatusInternalServerError)
            w.Write([]byte(`{"message": "need a number"}`))
            return
        }
    }

    query := r.URL.Query()
    location := query.Get("location")

    w.Write([]byte(fmt.Sprintf(`{"userID": %d, "commentID": %d, "location": "%s" }`, userID, commentID, location)))
}

func main() {
    r := mux.NewRouter()

    api := r.PathPrefix("/api/v1").Subrouter()
    api.HandleFunc("", get).Methods(http.MethodGet)
    api.HandleFunc("", post).Methods(http.MethodPost)
    api.HandleFunc("", put).Methods(http.MethodPut)
    api.HandleFunc("", delete).Methods(http.MethodDelete)

    api.HandleFunc("/user/{userID}/comment/{commentID}", params).Methods(http.MethodGet)

    log.Fatal(http.ListenAndServe(":8080", r))
}

Lets look at the params functions on line 36. We handle both path param and query params.

With this you now know enough to be dangerous.

Bookdata API

In Kaggle there is a dataset for bookdata. Its a csv file with about 13000 books. We will use it to make our own bookdata api.

books.csv

You can look at the file ☝🏼 there.

Clone Repo

Let's get started.

In a separate folder

git clone https://github.com/moficodes/bookdata-api.git

Tour of the Code

There are two packages inside the code. One is called datastore, one is called loader.

Loader deals with converting the csv data into an array of bookdata objects.

Datastore deals with how we access the data. It's mainly an interface that has some methods.

R### un app From the root of the repo

run

go run .

EndPoints

The App has a few Endpoints

All api endpoints are prefixed with /api/v1

To reach any endpoint use baseurl:8080/api/v1/{endpoint}

Get Books by Author
"/books/authors/{author}" 
Optional query parameter for ratingAbove ratingBelow limit and skip

Get Books by BookName
"/books/book-name/{bookName}"
Optional query parameter for ratingAbove ratingBelow limit and skip


Get Book by ISBN
"/book/isbn/{isbn}"

Delete Book by ISBN
"/book/isbn/{isbn}"

Create New Book
"/book"