SlideShare ist ein Scribd-Unternehmen logo
1 von 36
Downloaden Sie, um offline zu lesen
© 2017
Using Go to build a REST API
© 2017
Who we are
Vincent BEHAR
twitter.com/vbehar
Mina GENDI
github.com/m-gendi
© 2017
Agenda
• Our project
• First challenges
• REST Endpoints
• API Documentation
• Testing
• API versioning
• Monitoring
© 2017
• Programmatic Advertising
• Complex & rich domain
• Full control over programmatic chain
• From scratch
• Link publishers with advertisers
OUR PROJECT
AdTech Platform
• REST API
• Used to generate all the rules for the ad-servers
• 239 endpoints
• 54 main entity types
• Could have been any language
• So what were the benefits of using Go ?
API
© 2017
FIRST CHALLENGES
Start easy
func getPublishers(w http.ResponseWriter, r *http.Request) {
publishers, _ := store.LoadPublishers(...)
json.NewEncoder(w).Encode(publishers)
}
• Write simple http.HandlerFunc
• Lots of copy/paste when it gets more complex:
• Error handling
• Pagination
• Filtering
• Sorting
• Logging
• Monitoring
• …
© 2017
FIRST CHALLENGES
Keep It DRY
• Identify common use-cases:
• GET Collection (54)
• GET Resource (49)
• POST Resource (30)
• PUT Resource (28)
• DELETE Resource (27)
• …
• Write a helper func per use-case
func getPublishers(w http.ResponseWriter, r *http.Request) {
return getCollection(w, r, "publisher", &models.Publisher{})
}
func getPublisher(w http.ResponseWriter, r *http.Request) {
return getResource(w, r, "publisher", &models.Publisher{})
}
© 2017
FIRST CHALLENGES
• Same use-case, but different behaviour
• Parameters hell
func getPublishers(w http.ResponseWriter, r *http.Request) {
return getCollection(w, r, "publisher",
&models.Publisher{}, true, "", false, nil, "", "", nil, true)
}
But…
© 2017
SOLUTIONS
Options Struct
func getPublishers(w http.ResponseWriter, r *http.Request) {
return getCollection(w, r, GetCollectionOptions{
Type: "publisher",
Model: &models.Publisher{},
...
}
}
• Extensible
• Readable
© 2017
SOLUTIONS
Functional Options
func getPublishers(w http.ResponseWriter, r *http.Request) {
return getCollection(w, r,
withType("publisher"),
withModel(&models.Publisher{}),
…)
}
• https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis
• Options are functions that operates on some internal objects used by getCollection
• Extensible
© 2017
OUR SOLUTION
Composite Literal
// GetPublishers is a REST endpoint that retrieves all publishers
var GetPublishers = Endpoint{
For: http.MethodGet,
At: "/v1/publisher",
HandledBy: GetCollection{
Of: "publisher",
Using: sliceOf(models.Publisher{}),
With: publishersLoader,
},
}
• All in 1 place: path, method, handler
• Declarative
• Easy to read
• Extensible
• It’s just Go
© 2017
REST ENDPOINTS
Implementation
type Endpoint struct {
For string // method
At string // path
HandledBy http.Handler
}
func (ep Endpoint) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ep.HandledBy.ServeHTTP(w, r)
}
• Endpoint is an http.Handler
• Just a wrapper around another http.Handler
© 2017
REST ENDPOINTS
Handlers
type GetCollection struct {
Of string
ParsedBy CollectionRequestParser
Using ModelFactory
With CollectionLoader
}
func (h GetCollection) ServeHTTP(w http.ResponseWriter, r *http.Request) {
model := h.Using()
if h.ParsedBy == nil {
h.ParsedBy = paginationParser.And(sortingParser).And(filterParser)
}
if h.With == nil {
h.With = defaultCollectionLoader
}
[...]
}
© 2017
REST ENDPOINTS
Handlers
type PostResource struct {
Of string
Using ModelFactory
ValidatedBy Validator
With ResourceStorer
}
func (h PostResource) ServeHTTP(w http.ResponseWriter, r *http.Request) {
model := h.Using()
err := json.NewDecoder(r.Body).Decode(model)
validationErr, err := h.ValidatedBy.validate(ctx, model)
h.With(ctx, StoreItemOptions{ TableName: h.Of }, model)
[...]
}
© 2017
REST ENDPOINTS
Example
var GetSitesByPublisher = Endpoint{
For: http.MethodGet,
At: "/v1/publisher/{pubid:[0-9]+}/site",
HandledBy: GetNestedCollection{
Of: "site",
Using: sliceOf(models.Site{}),
With: nestedSitesLoader,
From: Parent{
Type: "publisher",
MappedBy: "pubid",
Using: instanceOf(models.Publisher{}),
With: nonSoftDeletedResourceLoader,
},
},
}
© 2017
API DOCUMENTATION
• Manually written
• Generated using apidocjs.com
• Lots of duplicate information
• API Documentation is often outdated
• Everything is already written and accessible from our Endpoints
• Couldn’t it be re-used ?
Current status
© 2017
API DOCUMENTATION
Kubernetes API Documentation
https://github.com/kubernetes/api/blob/master/core/v1/types.go
https://kubernetes.io/docs/api-reference/v1.8/#pod-v1-core
© 2017
API DOCUMENTATION
• API Documentation Generation
• Use Go stdlib to parse the source code
• go/ast, go/doc, go/parser, go/token
• Use reflection to retrieve the « models »
• Use godoc for both internal doc and API doc
• Use request/response fields from internal models (Go Struct)
• Generate swagger, markdown, …
Future
© 2017
INTEGRATION TESTING
• Lots of copy/paste
• Un-friendly error messages:
• We can do better
Who cares about tests ?
--- FAIL: TestGetAdCategories (0.03s)
ad_category_test.go:44: Expected 8 Got 7
© 2017
INTEGRATION TESTING
BDD-style scenarios
Scenario{
Name: "Load all Ad Categories",
Given: IHave{
RESTCollection{
Of: "ad_category",
WithAPIPrefix: "/v1",
},
},
When: IDo{
GetCollection{
At: httpServer.URL,
},
},
Then: ItShould{
BeAnHTTPResponse{
WithStatusCode: http.StatusOK,
WithBody: MatchingJSON{
jsonValueAt("result.#").EqualToValue(26),
jsonValueAt("result.0.childId").EqualToValue("IAB1"),
},
},
},
}.test(t)
© 2017
INTEGRATION TESTING
BDD-style scenarios
Error:
Not equal: 27 (expected)
!= 26 (actual)
Messages: [TestGetAdCategories]
Given I have a REST collection of ad_category
When I do a GET collection request at http://127.0.0.1:
62526/v1/ad_category?sort=id
Then it should be an HTTP response
with body matching JSON value at result.#
• For integration tests
• Use the net/http/httptest pkg
• Custom scenario: nice output message:
© 2017
INTEGRATION TESTING
Implementation
type Scenario struct {
Name string
Given Given
When When
Then Then
}
type When interface {
run(context.Context, *testing.T) context.Context
}
// GetCollection is a When step
type GetCollection struct {
At string // At is the base URL of the HTTP server
}
func (c GetCollection) run(ctx context.Context, t *testing.T)
context.Context {
resp, _ := http.Get(...)
return context.WithValue(ctx, contextKeys.HTTPResponse, resp)
}
© 2017
INTEGRATION TESTING
Result
• Easier to read
• Re-usable blocks
• Clear separation of concerns
© 2017
API VERSIONING
• Product requirements are constantly changing
• Breaking changes
• API users need to adapt
Product change vs API users
© 2017
API VERSIONING
• Don’t break old clients !
• Use an HTTP header to pass the « API level » implemented by the client
• Adapt the request to the current API level implemented by the server
• Handle the request as usual
• Adapt the response to the API level implemented by the client
• Use HTTP middleware
• Just JSON transformation
API Levels
© 2017
API VERSIONING
Declaration
func init() {
Adapters{
{
ForLevel: 2,
AppliesTo: rest.Endpoints{
rest.GetTimezones,
},
AdaptResponseWith: jsonResponseBodyAdapter(adaptTimezones),
},
{
ForLevel: 2,
AppliesTo: rest.Endpoints{
rest.GetTimezone,
},
AdaptResponseWith: jsonResponseBodyAdapter(adaptTimezone),
},
}.mustRegister()
}
© 2017
API VERSIONING
Types
type Adapter struct {
ForLevel int
AppliesTo rest.Endpoints
AdaptRequestWith RequestAdapter
AdaptResponseWith ResponseAdapter
}
type RequestAdapter interface {
Adapt(*http.Request) (*rest.APIError, error)
}
type ResponseAdapter interface {
Adapt(r *http.Request, statusCode int, headers http.Header,
body *bytes.Buffer) error
}
© 2017
API VERSIONING
Implementation
func Adapters(inner http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
level := ParseRequestedAPILevel(r.Header)
adapters := restadapters.ForEndpointAndLevel(r.Method, path, level)
adapters.SortByLevelAsc()
for _, adapter := range adapters {
adapter.AdaptRequestWith.Adapt(r)
}
}
inner.ServeHTTP(rw, r)
adapters.SortByLevelDesc()
for _, adapter := range adapters {
adapter.AdaptResponseWith.Adapt(r, statusCode, w.Header(), buf)
}
}
})
}
© 2017
API VERSIONING
• Request
• You can only read it once!
• Use a bytes.Buffer
• Response
• Can’t re-read from it
• http.ResponseWriter is not easy to wrap
• Use github.com/felixge/httpsnoop
• Capture status code, headers, and body
Gotchas!
© 2017
MONITORING
• Endpoints performances
• SQL queries
• More insights in the requests
• Different behaviour based on the request body
What we need
© 2017
MONITORING
DataDog, APM and Context
• Using github.com/DataDog/dd-trace-go
• Tracing from HTTP handlers to database calls
• Use the context pkg
• Tracing information is stored in the context
• Each func has a ctx as its first parameter
• Easily create custom « span » (validation, store, …)
© 2017
MONITORING
API Endpoints
SQL Requests
© 2017
MONITORING
Down to the SQL level
© 2017
MONITORING
More insights on the requests
if span, ok := tracer.SpanFromContext(r.Context()); ok {
payload, _ := ioutil.ReadAll(r.Body)
span.SetMeta("http.payload", string(payload))
span.SetMeta("http.remote_ip", remoteip.FromRequest(r))
span.SetMeta("http.user_agent", r.UserAgent())
span.SetMeta("api.level", strconv.Itoa(apiLevel))
}
• Middleware
• Add request metadata
• Request body
• Some headers
• …
© 2017
MONITORING
Single request details
© 2017
CONCLUSION
• Easy to read/write
• Performance
• Easy to fit to our needs, using just Go and the stdlib
• Fun to work with!
Go for a REST API ?
© 2017
Thank you

Weitere ähnliche Inhalte

Ähnlich wie Using Go to build a REST API: yes, it’s a good match! - Vincent BEHAR & Mina GENDI - Paris API meetup #30 @Dailymotion HQ

Building RESTful Services With Go and MongoDB
Building RESTful Services With Go and MongoDBBuilding RESTful Services With Go and MongoDB
Building RESTful Services With Go and MongoDBShiju Varghese
 
Driving containerd operations with gRPC
Driving containerd operations with gRPCDriving containerd operations with gRPC
Driving containerd operations with gRPCDocker, Inc.
 
EWD 3 Training Course Part 36: Accessing REST and Web Services from a QEWD ap...
EWD 3 Training Course Part 36: Accessing REST and Web Services from a QEWD ap...EWD 3 Training Course Part 36: Accessing REST and Web Services from a QEWD ap...
EWD 3 Training Course Part 36: Accessing REST and Web Services from a QEWD ap...Rob Tweed
 
RESTful Web Development with CakePHP
RESTful Web Development with CakePHPRESTful Web Development with CakePHP
RESTful Web Development with CakePHPAndru Weir
 
Integrating React.js Into a PHP Application: Dutch PHP 2019
Integrating React.js Into a PHP Application: Dutch PHP 2019Integrating React.js Into a PHP Application: Dutch PHP 2019
Integrating React.js Into a PHP Application: Dutch PHP 2019Andrew Rota
 
Serverless Framework Workshop - Tyler Hendrickson, Chicago/burbs
 Serverless Framework Workshop - Tyler Hendrickson, Chicago/burbs Serverless Framework Workshop - Tyler Hendrickson, Chicago/burbs
Serverless Framework Workshop - Tyler Hendrickson, Chicago/burbsAWS Chicago
 
EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 2
EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 2EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 2
EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 2Rob Tweed
 
REST API 20.2 - Appworks Gateway Integration.pptx
REST API 20.2 - Appworks Gateway Integration.pptxREST API 20.2 - Appworks Gateway Integration.pptx
REST API 20.2 - Appworks Gateway Integration.pptxJason452803
 
Resting with OroCRM Webinar
Resting with OroCRM WebinarResting with OroCRM Webinar
Resting with OroCRM WebinarOro Inc.
 
An approach to responsive, realtime with Backbone.js and WebSockets
An approach to responsive, realtime with Backbone.js and WebSocketsAn approach to responsive, realtime with Backbone.js and WebSockets
An approach to responsive, realtime with Backbone.js and WebSocketsAndrei Sebastian Cîmpean
 
Building Microservivces with Java EE 8 and Microprofile
Building Microservivces with Java EE 8 and MicroprofileBuilding Microservivces with Java EE 8 and Microprofile
Building Microservivces with Java EE 8 and MicroprofileQAware GmbH
 
Spring Web Services: SOAP vs. REST
Spring Web Services: SOAP vs. RESTSpring Web Services: SOAP vs. REST
Spring Web Services: SOAP vs. RESTSam Brannen
 
Building Serverless Backends with AWS Lambda and Amazon API Gateway
Building Serverless Backends with AWS Lambda and Amazon API GatewayBuilding Serverless Backends with AWS Lambda and Amazon API Gateway
Building Serverless Backends with AWS Lambda and Amazon API GatewayAmazon Web Services
 
Test-Driven Documentation for your REST(ful) service
Test-Driven Documentation for your REST(ful) serviceTest-Driven Documentation for your REST(ful) service
Test-Driven Documentation for your REST(ful) serviceJeroen Reijn
 
REST Development made Easy with ColdFusion Aether
REST Development made Easy with ColdFusion AetherREST Development made Easy with ColdFusion Aether
REST Development made Easy with ColdFusion AetherPavan Kumar
 
The future of server side JavaScript
The future of server side JavaScriptThe future of server side JavaScript
The future of server side JavaScriptOleg Podsechin
 

Ähnlich wie Using Go to build a REST API: yes, it’s a good match! - Vincent BEHAR & Mina GENDI - Paris API meetup #30 @Dailymotion HQ (20)

Building RESTful Services With Go and MongoDB
Building RESTful Services With Go and MongoDBBuilding RESTful Services With Go and MongoDB
Building RESTful Services With Go and MongoDB
 
Driving containerd operations with gRPC
Driving containerd operations with gRPCDriving containerd operations with gRPC
Driving containerd operations with gRPC
 
EWD 3 Training Course Part 36: Accessing REST and Web Services from a QEWD ap...
EWD 3 Training Course Part 36: Accessing REST and Web Services from a QEWD ap...EWD 3 Training Course Part 36: Accessing REST and Web Services from a QEWD ap...
EWD 3 Training Course Part 36: Accessing REST and Web Services from a QEWD ap...
 
RESTful Web Development with CakePHP
RESTful Web Development with CakePHPRESTful Web Development with CakePHP
RESTful Web Development with CakePHP
 
Integrating React.js Into a PHP Application: Dutch PHP 2019
Integrating React.js Into a PHP Application: Dutch PHP 2019Integrating React.js Into a PHP Application: Dutch PHP 2019
Integrating React.js Into a PHP Application: Dutch PHP 2019
 
Serverless Framework Workshop - Tyler Hendrickson, Chicago/burbs
 Serverless Framework Workshop - Tyler Hendrickson, Chicago/burbs Serverless Framework Workshop - Tyler Hendrickson, Chicago/burbs
Serverless Framework Workshop - Tyler Hendrickson, Chicago/burbs
 
EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 2
EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 2EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 2
EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 2
 
RxSwift to Combine
RxSwift to CombineRxSwift to Combine
RxSwift to Combine
 
REST API 20.2 - Appworks Gateway Integration.pptx
REST API 20.2 - Appworks Gateway Integration.pptxREST API 20.2 - Appworks Gateway Integration.pptx
REST API 20.2 - Appworks Gateway Integration.pptx
 
Resting with OroCRM Webinar
Resting with OroCRM WebinarResting with OroCRM Webinar
Resting with OroCRM Webinar
 
An approach to responsive, realtime with Backbone.js and WebSockets
An approach to responsive, realtime with Backbone.js and WebSocketsAn approach to responsive, realtime with Backbone.js and WebSockets
An approach to responsive, realtime with Backbone.js and WebSockets
 
Building Microservivces with Java EE 8 and Microprofile
Building Microservivces with Java EE 8 and MicroprofileBuilding Microservivces with Java EE 8 and Microprofile
Building Microservivces with Java EE 8 and Microprofile
 
Spring Web Services: SOAP vs. REST
Spring Web Services: SOAP vs. RESTSpring Web Services: SOAP vs. REST
Spring Web Services: SOAP vs. REST
 
RESTEasy
RESTEasyRESTEasy
RESTEasy
 
Building Serverless Backends with AWS Lambda and Amazon API Gateway
Building Serverless Backends with AWS Lambda and Amazon API GatewayBuilding Serverless Backends with AWS Lambda and Amazon API Gateway
Building Serverless Backends with AWS Lambda and Amazon API Gateway
 
C#on linux
C#on linuxC#on linux
C#on linux
 
RESTing with JAX-RS
RESTing with JAX-RSRESTing with JAX-RS
RESTing with JAX-RS
 
Test-Driven Documentation for your REST(ful) service
Test-Driven Documentation for your REST(ful) serviceTest-Driven Documentation for your REST(ful) service
Test-Driven Documentation for your REST(ful) service
 
REST Development made Easy with ColdFusion Aether
REST Development made Easy with ColdFusion AetherREST Development made Easy with ColdFusion Aether
REST Development made Easy with ColdFusion Aether
 
The future of server side JavaScript
The future of server side JavaScriptThe future of server side JavaScript
The future of server side JavaScript
 

Kürzlich hochgeladen

notes on Evolution Of Analytic Scalability.ppt
notes on Evolution Of Analytic Scalability.pptnotes on Evolution Of Analytic Scalability.ppt
notes on Evolution Of Analytic Scalability.pptMsecMca
 
DC MACHINE-Motoring and generation, Armature circuit equation
DC MACHINE-Motoring and generation, Armature circuit equationDC MACHINE-Motoring and generation, Armature circuit equation
DC MACHINE-Motoring and generation, Armature circuit equationBhangaleSonal
 
Hazard Identification (HAZID) vs. Hazard and Operability (HAZOP): A Comparati...
Hazard Identification (HAZID) vs. Hazard and Operability (HAZOP): A Comparati...Hazard Identification (HAZID) vs. Hazard and Operability (HAZOP): A Comparati...
Hazard Identification (HAZID) vs. Hazard and Operability (HAZOP): A Comparati...soginsider
 
Unit 1 - Soil Classification and Compaction.pdf
Unit 1 - Soil Classification and Compaction.pdfUnit 1 - Soil Classification and Compaction.pdf
Unit 1 - Soil Classification and Compaction.pdfRagavanV2
 
Thermal Engineering Unit - I & II . ppt
Thermal Engineering  Unit - I & II . pptThermal Engineering  Unit - I & II . ppt
Thermal Engineering Unit - I & II . pptDineshKumar4165
 
data_management_and _data_science_cheat_sheet.pdf
data_management_and _data_science_cheat_sheet.pdfdata_management_and _data_science_cheat_sheet.pdf
data_management_and _data_science_cheat_sheet.pdfJiananWang21
 
COST-EFFETIVE and Energy Efficient BUILDINGS ptx
COST-EFFETIVE  and Energy Efficient BUILDINGS ptxCOST-EFFETIVE  and Energy Efficient BUILDINGS ptx
COST-EFFETIVE and Energy Efficient BUILDINGS ptxJIT KUMAR GUPTA
 
Navigating Complexity: The Role of Trusted Partners and VIAS3D in Dassault Sy...
Navigating Complexity: The Role of Trusted Partners and VIAS3D in Dassault Sy...Navigating Complexity: The Role of Trusted Partners and VIAS3D in Dassault Sy...
Navigating Complexity: The Role of Trusted Partners and VIAS3D in Dassault Sy...Arindam Chakraborty, Ph.D., P.E. (CA, TX)
 
Call Girls Pimpri Chinchwad Call Me 7737669865 Budget Friendly No Advance Boo...
Call Girls Pimpri Chinchwad Call Me 7737669865 Budget Friendly No Advance Boo...Call Girls Pimpri Chinchwad Call Me 7737669865 Budget Friendly No Advance Boo...
Call Girls Pimpri Chinchwad Call Me 7737669865 Budget Friendly No Advance Boo...roncy bisnoi
 
University management System project report..pdf
University management System project report..pdfUniversity management System project report..pdf
University management System project report..pdfKamal Acharya
 
UNIT - IV - Air Compressors and its Performance
UNIT - IV - Air Compressors and its PerformanceUNIT - IV - Air Compressors and its Performance
UNIT - IV - Air Compressors and its Performancesivaprakash250
 
Minimum and Maximum Modes of microprocessor 8086
Minimum and Maximum Modes of microprocessor 8086Minimum and Maximum Modes of microprocessor 8086
Minimum and Maximum Modes of microprocessor 8086anil_gaur
 
VIP Call Girls Palanpur 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Palanpur 7001035870 Whatsapp Number, 24/07 BookingVIP Call Girls Palanpur 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Palanpur 7001035870 Whatsapp Number, 24/07 Bookingdharasingh5698
 
Unit 2- Effective stress & Permeability.pdf
Unit 2- Effective stress & Permeability.pdfUnit 2- Effective stress & Permeability.pdf
Unit 2- Effective stress & Permeability.pdfRagavanV2
 

Kürzlich hochgeladen (20)

(INDIRA) Call Girl Bhosari Call Now 8617697112 Bhosari Escorts 24x7
(INDIRA) Call Girl Bhosari Call Now 8617697112 Bhosari Escorts 24x7(INDIRA) Call Girl Bhosari Call Now 8617697112 Bhosari Escorts 24x7
(INDIRA) Call Girl Bhosari Call Now 8617697112 Bhosari Escorts 24x7
 
Integrated Test Rig For HTFE-25 - Neometrix
Integrated Test Rig For HTFE-25 - NeometrixIntegrated Test Rig For HTFE-25 - Neometrix
Integrated Test Rig For HTFE-25 - Neometrix
 
notes on Evolution Of Analytic Scalability.ppt
notes on Evolution Of Analytic Scalability.pptnotes on Evolution Of Analytic Scalability.ppt
notes on Evolution Of Analytic Scalability.ppt
 
DC MACHINE-Motoring and generation, Armature circuit equation
DC MACHINE-Motoring and generation, Armature circuit equationDC MACHINE-Motoring and generation, Armature circuit equation
DC MACHINE-Motoring and generation, Armature circuit equation
 
(INDIRA) Call Girl Meerut Call Now 8617697112 Meerut Escorts 24x7
(INDIRA) Call Girl Meerut Call Now 8617697112 Meerut Escorts 24x7(INDIRA) Call Girl Meerut Call Now 8617697112 Meerut Escorts 24x7
(INDIRA) Call Girl Meerut Call Now 8617697112 Meerut Escorts 24x7
 
Hazard Identification (HAZID) vs. Hazard and Operability (HAZOP): A Comparati...
Hazard Identification (HAZID) vs. Hazard and Operability (HAZOP): A Comparati...Hazard Identification (HAZID) vs. Hazard and Operability (HAZOP): A Comparati...
Hazard Identification (HAZID) vs. Hazard and Operability (HAZOP): A Comparati...
 
Unit 1 - Soil Classification and Compaction.pdf
Unit 1 - Soil Classification and Compaction.pdfUnit 1 - Soil Classification and Compaction.pdf
Unit 1 - Soil Classification and Compaction.pdf
 
Thermal Engineering Unit - I & II . ppt
Thermal Engineering  Unit - I & II . pptThermal Engineering  Unit - I & II . ppt
Thermal Engineering Unit - I & II . ppt
 
(INDIRA) Call Girl Aurangabad Call Now 8617697112 Aurangabad Escorts 24x7
(INDIRA) Call Girl Aurangabad Call Now 8617697112 Aurangabad Escorts 24x7(INDIRA) Call Girl Aurangabad Call Now 8617697112 Aurangabad Escorts 24x7
(INDIRA) Call Girl Aurangabad Call Now 8617697112 Aurangabad Escorts 24x7
 
data_management_and _data_science_cheat_sheet.pdf
data_management_and _data_science_cheat_sheet.pdfdata_management_and _data_science_cheat_sheet.pdf
data_management_and _data_science_cheat_sheet.pdf
 
COST-EFFETIVE and Energy Efficient BUILDINGS ptx
COST-EFFETIVE  and Energy Efficient BUILDINGS ptxCOST-EFFETIVE  and Energy Efficient BUILDINGS ptx
COST-EFFETIVE and Energy Efficient BUILDINGS ptx
 
Navigating Complexity: The Role of Trusted Partners and VIAS3D in Dassault Sy...
Navigating Complexity: The Role of Trusted Partners and VIAS3D in Dassault Sy...Navigating Complexity: The Role of Trusted Partners and VIAS3D in Dassault Sy...
Navigating Complexity: The Role of Trusted Partners and VIAS3D in Dassault Sy...
 
Call Girls Pimpri Chinchwad Call Me 7737669865 Budget Friendly No Advance Boo...
Call Girls Pimpri Chinchwad Call Me 7737669865 Budget Friendly No Advance Boo...Call Girls Pimpri Chinchwad Call Me 7737669865 Budget Friendly No Advance Boo...
Call Girls Pimpri Chinchwad Call Me 7737669865 Budget Friendly No Advance Boo...
 
University management System project report..pdf
University management System project report..pdfUniversity management System project report..pdf
University management System project report..pdf
 
UNIT - IV - Air Compressors and its Performance
UNIT - IV - Air Compressors and its PerformanceUNIT - IV - Air Compressors and its Performance
UNIT - IV - Air Compressors and its Performance
 
Minimum and Maximum Modes of microprocessor 8086
Minimum and Maximum Modes of microprocessor 8086Minimum and Maximum Modes of microprocessor 8086
Minimum and Maximum Modes of microprocessor 8086
 
VIP Call Girls Palanpur 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Palanpur 7001035870 Whatsapp Number, 24/07 BookingVIP Call Girls Palanpur 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Palanpur 7001035870 Whatsapp Number, 24/07 Booking
 
Unit 2- Effective stress & Permeability.pdf
Unit 2- Effective stress & Permeability.pdfUnit 2- Effective stress & Permeability.pdf
Unit 2- Effective stress & Permeability.pdf
 
Call Girls in Netaji Nagar, Delhi 💯 Call Us 🔝9953056974 🔝 Escort Service
Call Girls in Netaji Nagar, Delhi 💯 Call Us 🔝9953056974 🔝 Escort ServiceCall Girls in Netaji Nagar, Delhi 💯 Call Us 🔝9953056974 🔝 Escort Service
Call Girls in Netaji Nagar, Delhi 💯 Call Us 🔝9953056974 🔝 Escort Service
 
FEA Based Level 3 Assessment of Deformed Tanks with Fluid Induced Loads
FEA Based Level 3 Assessment of Deformed Tanks with Fluid Induced LoadsFEA Based Level 3 Assessment of Deformed Tanks with Fluid Induced Loads
FEA Based Level 3 Assessment of Deformed Tanks with Fluid Induced Loads
 

Using Go to build a REST API: yes, it’s a good match! - Vincent BEHAR & Mina GENDI - Paris API meetup #30 @Dailymotion HQ

  • 1. © 2017 Using Go to build a REST API
  • 2. © 2017 Who we are Vincent BEHAR twitter.com/vbehar Mina GENDI github.com/m-gendi
  • 3. © 2017 Agenda • Our project • First challenges • REST Endpoints • API Documentation • Testing • API versioning • Monitoring
  • 4. © 2017 • Programmatic Advertising • Complex & rich domain • Full control over programmatic chain • From scratch • Link publishers with advertisers OUR PROJECT AdTech Platform • REST API • Used to generate all the rules for the ad-servers • 239 endpoints • 54 main entity types • Could have been any language • So what were the benefits of using Go ? API
  • 5. © 2017 FIRST CHALLENGES Start easy func getPublishers(w http.ResponseWriter, r *http.Request) { publishers, _ := store.LoadPublishers(...) json.NewEncoder(w).Encode(publishers) } • Write simple http.HandlerFunc • Lots of copy/paste when it gets more complex: • Error handling • Pagination • Filtering • Sorting • Logging • Monitoring • …
  • 6. © 2017 FIRST CHALLENGES Keep It DRY • Identify common use-cases: • GET Collection (54) • GET Resource (49) • POST Resource (30) • PUT Resource (28) • DELETE Resource (27) • … • Write a helper func per use-case func getPublishers(w http.ResponseWriter, r *http.Request) { return getCollection(w, r, "publisher", &models.Publisher{}) } func getPublisher(w http.ResponseWriter, r *http.Request) { return getResource(w, r, "publisher", &models.Publisher{}) }
  • 7. © 2017 FIRST CHALLENGES • Same use-case, but different behaviour • Parameters hell func getPublishers(w http.ResponseWriter, r *http.Request) { return getCollection(w, r, "publisher", &models.Publisher{}, true, "", false, nil, "", "", nil, true) } But…
  • 8. © 2017 SOLUTIONS Options Struct func getPublishers(w http.ResponseWriter, r *http.Request) { return getCollection(w, r, GetCollectionOptions{ Type: "publisher", Model: &models.Publisher{}, ... } } • Extensible • Readable
  • 9. © 2017 SOLUTIONS Functional Options func getPublishers(w http.ResponseWriter, r *http.Request) { return getCollection(w, r, withType("publisher"), withModel(&models.Publisher{}), …) } • https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis • Options are functions that operates on some internal objects used by getCollection • Extensible
  • 10. © 2017 OUR SOLUTION Composite Literal // GetPublishers is a REST endpoint that retrieves all publishers var GetPublishers = Endpoint{ For: http.MethodGet, At: "/v1/publisher", HandledBy: GetCollection{ Of: "publisher", Using: sliceOf(models.Publisher{}), With: publishersLoader, }, } • All in 1 place: path, method, handler • Declarative • Easy to read • Extensible • It’s just Go
  • 11. © 2017 REST ENDPOINTS Implementation type Endpoint struct { For string // method At string // path HandledBy http.Handler } func (ep Endpoint) ServeHTTP(w http.ResponseWriter, r *http.Request) { ep.HandledBy.ServeHTTP(w, r) } • Endpoint is an http.Handler • Just a wrapper around another http.Handler
  • 12. © 2017 REST ENDPOINTS Handlers type GetCollection struct { Of string ParsedBy CollectionRequestParser Using ModelFactory With CollectionLoader } func (h GetCollection) ServeHTTP(w http.ResponseWriter, r *http.Request) { model := h.Using() if h.ParsedBy == nil { h.ParsedBy = paginationParser.And(sortingParser).And(filterParser) } if h.With == nil { h.With = defaultCollectionLoader } [...] }
  • 13. © 2017 REST ENDPOINTS Handlers type PostResource struct { Of string Using ModelFactory ValidatedBy Validator With ResourceStorer } func (h PostResource) ServeHTTP(w http.ResponseWriter, r *http.Request) { model := h.Using() err := json.NewDecoder(r.Body).Decode(model) validationErr, err := h.ValidatedBy.validate(ctx, model) h.With(ctx, StoreItemOptions{ TableName: h.Of }, model) [...] }
  • 14. © 2017 REST ENDPOINTS Example var GetSitesByPublisher = Endpoint{ For: http.MethodGet, At: "/v1/publisher/{pubid:[0-9]+}/site", HandledBy: GetNestedCollection{ Of: "site", Using: sliceOf(models.Site{}), With: nestedSitesLoader, From: Parent{ Type: "publisher", MappedBy: "pubid", Using: instanceOf(models.Publisher{}), With: nonSoftDeletedResourceLoader, }, }, }
  • 15. © 2017 API DOCUMENTATION • Manually written • Generated using apidocjs.com • Lots of duplicate information • API Documentation is often outdated • Everything is already written and accessible from our Endpoints • Couldn’t it be re-used ? Current status
  • 16. © 2017 API DOCUMENTATION Kubernetes API Documentation https://github.com/kubernetes/api/blob/master/core/v1/types.go https://kubernetes.io/docs/api-reference/v1.8/#pod-v1-core
  • 17. © 2017 API DOCUMENTATION • API Documentation Generation • Use Go stdlib to parse the source code • go/ast, go/doc, go/parser, go/token • Use reflection to retrieve the « models » • Use godoc for both internal doc and API doc • Use request/response fields from internal models (Go Struct) • Generate swagger, markdown, … Future
  • 18. © 2017 INTEGRATION TESTING • Lots of copy/paste • Un-friendly error messages: • We can do better Who cares about tests ? --- FAIL: TestGetAdCategories (0.03s) ad_category_test.go:44: Expected 8 Got 7
  • 19. © 2017 INTEGRATION TESTING BDD-style scenarios Scenario{ Name: "Load all Ad Categories", Given: IHave{ RESTCollection{ Of: "ad_category", WithAPIPrefix: "/v1", }, }, When: IDo{ GetCollection{ At: httpServer.URL, }, }, Then: ItShould{ BeAnHTTPResponse{ WithStatusCode: http.StatusOK, WithBody: MatchingJSON{ jsonValueAt("result.#").EqualToValue(26), jsonValueAt("result.0.childId").EqualToValue("IAB1"), }, }, }, }.test(t)
  • 20. © 2017 INTEGRATION TESTING BDD-style scenarios Error: Not equal: 27 (expected) != 26 (actual) Messages: [TestGetAdCategories] Given I have a REST collection of ad_category When I do a GET collection request at http://127.0.0.1: 62526/v1/ad_category?sort=id Then it should be an HTTP response with body matching JSON value at result.# • For integration tests • Use the net/http/httptest pkg • Custom scenario: nice output message:
  • 21. © 2017 INTEGRATION TESTING Implementation type Scenario struct { Name string Given Given When When Then Then } type When interface { run(context.Context, *testing.T) context.Context } // GetCollection is a When step type GetCollection struct { At string // At is the base URL of the HTTP server } func (c GetCollection) run(ctx context.Context, t *testing.T) context.Context { resp, _ := http.Get(...) return context.WithValue(ctx, contextKeys.HTTPResponse, resp) }
  • 22. © 2017 INTEGRATION TESTING Result • Easier to read • Re-usable blocks • Clear separation of concerns
  • 23. © 2017 API VERSIONING • Product requirements are constantly changing • Breaking changes • API users need to adapt Product change vs API users
  • 24. © 2017 API VERSIONING • Don’t break old clients ! • Use an HTTP header to pass the « API level » implemented by the client • Adapt the request to the current API level implemented by the server • Handle the request as usual • Adapt the response to the API level implemented by the client • Use HTTP middleware • Just JSON transformation API Levels
  • 25. © 2017 API VERSIONING Declaration func init() { Adapters{ { ForLevel: 2, AppliesTo: rest.Endpoints{ rest.GetTimezones, }, AdaptResponseWith: jsonResponseBodyAdapter(adaptTimezones), }, { ForLevel: 2, AppliesTo: rest.Endpoints{ rest.GetTimezone, }, AdaptResponseWith: jsonResponseBodyAdapter(adaptTimezone), }, }.mustRegister() }
  • 26. © 2017 API VERSIONING Types type Adapter struct { ForLevel int AppliesTo rest.Endpoints AdaptRequestWith RequestAdapter AdaptResponseWith ResponseAdapter } type RequestAdapter interface { Adapt(*http.Request) (*rest.APIError, error) } type ResponseAdapter interface { Adapt(r *http.Request, statusCode int, headers http.Header, body *bytes.Buffer) error }
  • 27. © 2017 API VERSIONING Implementation func Adapters(inner http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { level := ParseRequestedAPILevel(r.Header) adapters := restadapters.ForEndpointAndLevel(r.Method, path, level) adapters.SortByLevelAsc() for _, adapter := range adapters { adapter.AdaptRequestWith.Adapt(r) } } inner.ServeHTTP(rw, r) adapters.SortByLevelDesc() for _, adapter := range adapters { adapter.AdaptResponseWith.Adapt(r, statusCode, w.Header(), buf) } } }) }
  • 28. © 2017 API VERSIONING • Request • You can only read it once! • Use a bytes.Buffer • Response • Can’t re-read from it • http.ResponseWriter is not easy to wrap • Use github.com/felixge/httpsnoop • Capture status code, headers, and body Gotchas!
  • 29. © 2017 MONITORING • Endpoints performances • SQL queries • More insights in the requests • Different behaviour based on the request body What we need
  • 30. © 2017 MONITORING DataDog, APM and Context • Using github.com/DataDog/dd-trace-go • Tracing from HTTP handlers to database calls • Use the context pkg • Tracing information is stored in the context • Each func has a ctx as its first parameter • Easily create custom « span » (validation, store, …)
  • 32. © 2017 MONITORING Down to the SQL level
  • 33. © 2017 MONITORING More insights on the requests if span, ok := tracer.SpanFromContext(r.Context()); ok { payload, _ := ioutil.ReadAll(r.Body) span.SetMeta("http.payload", string(payload)) span.SetMeta("http.remote_ip", remoteip.FromRequest(r)) span.SetMeta("http.user_agent", r.UserAgent()) span.SetMeta("api.level", strconv.Itoa(apiLevel)) } • Middleware • Add request metadata • Request body • Some headers • …
  • 35. © 2017 CONCLUSION • Easy to read/write • Performance • Easy to fit to our needs, using just Go and the stdlib • Fun to work with! Go for a REST API ?