README.md
1# InfluxDB Client Go
2
3[![CircleCI](https://circleci.com/gh/influxdata/influxdb-client-go.svg?style=svg)](https://circleci.com/gh/influxdata/influxdb-client-go)
4[![codecov](https://codecov.io/gh/influxdata/influxdb-client-go/branch/master/graph/badge.svg)](https://codecov.io/gh/influxdata/influxdb-client-go)
5[![License](https://img.shields.io/github/license/influxdata/influxdb-client-go.svg)](https://github.com/influxdata/influxdb-client-go/blob/master/LICENSE)
6[![Slack Status](https://img.shields.io/badge/slack-join_chat-white.svg?logo=slack&style=social)](https://www.influxdata.com/slack)
7
8This repository contains the reference Go client for InfluxDB 2.
9
10#### Note: Use this client library with InfluxDB 2.x and InfluxDB 1.8+ ([see details](#influxdb-18-api-compatibility)). For connecting to InfluxDB 1.7 or earlier instances, use the [influxdb1-go](https://github.com/influxdata/influxdb1-client) client library.
11
12- [Features](#features)
13- [Documentation](#documentation)
14 - [Examples](#examples)
15- [How To Use](#how-to-use)
16 - [Basic Example](#basic-example)
17 - [Writes in Detail](#writes)
18 - [Queries in Detail](#queries)
19 - [Concurrency](#concurrency)
20 - [Proxy and redirects](#proxy-and-redirects)
21 - [Checking Server State](#checking-server-state)
22- [InfluxDB 1.8 API compatibility](#influxdb-18-api-compatibility)
23- [Contributing](#contributing)
24- [License](#license)
25
26## Features
27
28- InfluxDB 2 client
29 - Querying data
30 - using the Flux language
31 - into raw data, flux table representation
32 - [How to queries](#queries)
33 - Writing data using
34 - [Line Protocol](https://docs.influxdata.com/influxdb/v2.0/reference/syntax/line-protocol/)
35 - [Data Point](https://pkg.go.dev/github.com/influxdata/influxdb-client-go/v2/api/write#Point)
36 - Both [asynchronous](https://pkg.go.dev/github.com/influxdata/influxdb-client-go/v2/api#WriteAPI) or [synchronous](https://pkg.go.dev/github.com/influxdata/influxdb-client-go/v2/api#WriteAPIBlocking) ways
37 - [How to writes](#writes)
38 - InfluxDB 2 API
39 - setup, ready, health
40 - authotizations, users, organizations
41 - buckets, delete
42 - ...
43
44## Documentation
45
46This section contains links to the client library documentation.
47
48- [Product documentation](https://docs.influxdata.com/influxdb/v2.0/tools/client-libraries/), [Getting Started](#how-to-use)
49- [Examples](#examples)
50- [API Reference](https://pkg.go.dev/github.com/influxdata/influxdb-client-go/v2)
51- [Changelog](CHANGELOG.md)
52
53### Examples
54
55Examples for basic writing and querying data are shown below in this document
56
57There are also other examples in the API docs:
58 - [Client usage](https://pkg.go.dev/github.com/influxdata/influxdb-client-go/v2?tab=doc#pkg-examples)
59 - [Management APIs](https://pkg.go.dev/github.com/influxdata/influxdb-client-go/v2/api?tab=doc#pkg-examples)
60
61## How To Use
62
63### Installation
64**Go 1.13** or later is required.
65
661. Add the client package your to your project dependencies (go.mod).
67 ```sh
68 go get github.com/influxdata/influxdb-client-go/v2
69 ```
701. Add import `github.com/influxdata/influxdb-client-go/v2` to your source code.
71
72### Basic Example
73The following example demonstrates how to write data to InfluxDB 2 and read them back using the Flux language:
74```go
75package main
76
77import (
78 "context"
79 "fmt"
80 "time"
81
82 "github.com/influxdata/influxdb-client-go/v2"
83)
84
85func main() {
86 // Create a new client using an InfluxDB server base URL and an authentication token
87 client := influxdb2.NewClient("http://localhost:8086", "my-token")
88 // Use blocking write client for writes to desired bucket
89 writeAPI := client.WriteAPIBlocking("my-org", "my-bucket")
90 // Create point using full params constructor
91 p := influxdb2.NewPoint("stat",
92 map[string]string{"unit": "temperature"},
93 map[string]interface{}{"avg": 24.5, "max": 45.0},
94 time.Now())
95 // write point immediately
96 writeAPI.WritePoint(context.Background(), p)
97 // Create point using fluent style
98 p = influxdb2.NewPointWithMeasurement("stat").
99 AddTag("unit", "temperature").
100 AddField("avg", 23.2).
101 AddField("max", 45.0).
102 SetTime(time.Now())
103 writeAPI.WritePoint(context.Background(), p)
104
105 // Or write directly line protocol
106 line := fmt.Sprintf("stat,unit=temperature avg=%f,max=%f", 23.5, 45.0)
107 writeAPI.WriteRecord(context.Background(), line)
108
109 // Get query client
110 queryAPI := client.QueryAPI("my-org")
111 // Get parser flux query result
112 result, err := queryAPI.Query(context.Background(), `from(bucket:"my-bucket")|> range(start: -1h) |> filter(fn: (r) => r._measurement == "stat")`)
113 if err == nil {
114 // Use Next() to iterate over query result lines
115 for result.Next() {
116 // Observe when there is new grouping key producing new table
117 if result.TableChanged() {
118 fmt.Printf("table: %s\n", result.TableMetadata().String())
119 }
120 // read result
121 fmt.Printf("row: %s\n", result.Record().String())
122 }
123 if result.Err() != nil {
124 fmt.Printf("Query error: %s\n", result.Err().Error())
125 }
126 }
127 // Ensures background processes finishes
128 client.Close()
129}
130```
131### Options
132The InfluxDBClient uses set of options to configure behavior. These are available in the [Options](https://pkg.go.dev/github.com/influxdata/influxdb-client-go/v2#Options) object
133Creating a client instance using
134```go
135client := influxdb2.NewClient("http://localhost:8086", "my-token")
136```
137will use the default options.
138
139To set different configuration values, e.g. to set gzip compression and trust all server certificates, get default options
140and change what is needed:
141```go
142client := influxdb2.NewClientWithOptions("http://localhost:8086", "my-token",
143 influxdb2.DefaultOptions().
144 SetUseGZip(true).
145 SetTLSConfig(&tls.Config{
146 InsecureSkipVerify: true,
147 }))
148```
149### Writes
150
151Client offers two ways of writing, non-blocking and blocking.
152
153### Non-blocking write client
154Non-blocking write client uses implicit batching. Data are asynchronously
155written to the underlying buffer and they are automatically sent to a server when the size of the write buffer reaches the batch size, default 5000, or the flush interval, default 1s, times out.
156Writes are automatically retried on server back pressure.
157
158This write client also offers synchronous blocking method to ensure that write buffer is flushed and all pending writes are finished,
159see [Flush()](https://pkg.go.dev/github.com/influxdata/influxdb-client-go/v2/api#WriteAPI.Flush) method.
160Always use [Close()](https://pkg.go.dev/github.com/influxdata/influxdb-client-go/v2#Client.Close) method of the client to stop all background processes.
161
162Asynchronous write client is recommended for frequent periodic writes.
163
164```go
165package main
166
167import (
168 "fmt"
169 "math/rand"
170 "time"
171
172 "github.com/influxdata/influxdb-client-go/v2"
173)
174
175func main() {
176 // Create a new client using an InfluxDB server base URL and an authentication token
177 // and set batch size to 20
178 client := influxdb2.NewClientWithOptions("http://localhost:8086", "my-token",
179 influxdb2.DefaultOptions().SetBatchSize(20))
180 // Get non-blocking write client
181 writeAPI := client.WriteAPI("my-org","my-bucket")
182 // write some points
183 for i := 0; i <100; i++ {
184 // create point
185 p := influxdb2.NewPoint(
186 "system",
187 map[string]string{
188 "id": fmt.Sprintf("rack_%v", i%10),
189 "vendor": "AWS",
190 "hostname": fmt.Sprintf("host_%v", i%100),
191 },
192 map[string]interface{}{
193 "temperature": rand.Float64() * 80.0,
194 "disk_free": rand.Float64() * 1000.0,
195 "disk_total": (i/10 + 1) * 1000000,
196 "mem_total": (i/100 + 1) * 10000000,
197 "mem_free": rand.Uint64(),
198 },
199 time.Now())
200 // write asynchronously
201 writeAPI.WritePoint(p)
202 }
203 // Force all unwritten data to be sent
204 writeAPI.Flush()
205 // Ensures background processes finishes
206 client.Close()
207}
208```
209### Handling of failed async writes
210WriteAPI by default continues with retrying of failed writes.
211Retried are automatically writes that fail on a connection failure or when server returns response HTTP status code >= 429.
212
213Retrying algorithm uses random exponential strategy to set retry time.
214The delay for the next retry attempt is a random value in the interval _retryInterval * exponentialBase^(attempts)_ and _retryInterval * exponentialBase^(attempts+1)_.
215If writes of batch repeatedly fails, WriteAPI continues with retrying until _maxRetries_ is reached or the overall retry time of batch exceeds _maxRetryTime_.
216
217The defaults parameters (part of the WriteOptions) are:
218 - _retryInterval_=5,000ms
219 - _exponentialBase_=2
220 - _maxRetryDelay_=125,000ms
221 - _maxRetries_=5
222 - _maxRetryTime_=180,000ms
223
224Retry delays are by default randomly distributed within the ranges:
225 1. 5,000-10,000
226 1. 10,000-20,000
227 1. 20,000-40,000
228 1. 40,000-80,000
229 1. 80,000-125,000
230
231Setting _retryInterval_ to 0 disables retry strategy and any failed write will discard the batch.
232
233[WriteFailedCallback](https://pkg.go.dev/github.com/influxdata/influxdb-client-go/v2/api#WriteFailedCallback) allows advanced controlling of retrying.
234It is synchronously notified in case async write fails.
235It controls further batch handling by its return value. If it returns `true`, WriteAPI continues with retrying of writes of this batch. Returned `false` means the batch should be discarded.
236
237### Reading async errors
238[Errors()](https://pkg.go.dev/github.com/influxdata/influxdb-client-go/v2/api#WriteAPI.Errors) method returns a channel for reading errors which occurs during async writes. This channel is unbuffered and it
239must be read asynchronously otherwise will block write procedure:
240
241```go
242package main
243
244import (
245 "fmt"
246 "math/rand"
247 "time"
248
249 "github.com/influxdata/influxdb-client-go/v2"
250)
251
252func main() {
253 // Create a new client using an InfluxDB server base URL and an authentication token
254 client := influxdb2.NewClient("http://localhost:8086", "my-token")
255 // Get non-blocking write client
256 writeAPI := client.WriteAPI("my-org", "my-bucket")
257 // Get errors channel
258 errorsCh := writeAPI.Errors()
259 // Create go proc for reading and logging errors
260 go func() {
261 for err := range errorsCh {
262 fmt.Printf("write error: %s\n", err.Error())
263 }
264 }()
265 // write some points
266 for i := 0; i < 100; i++ {
267 // create point
268 p := influxdb2.NewPointWithMeasurement("stat").
269 AddTag("id", fmt.Sprintf("rack_%v", i%10)).
270 AddTag("vendor", "AWS").
271 AddTag("hostname", fmt.Sprintf("host_%v", i%100)).
272 AddField("temperature", rand.Float64()*80.0).
273 AddField("disk_free", rand.Float64()*1000.0).
274 AddField("disk_total", (i/10+1)*1000000).
275 AddField("mem_total", (i/100+1)*10000000).
276 AddField("mem_free", rand.Uint64()).
277 SetTime(time.Now())
278 // write asynchronously
279 writeAPI.WritePoint(p)
280 }
281 // Force all unwritten data to be sent
282 writeAPI.Flush()
283 // Ensures background processes finishes
284 client.Close()
285}
286```
287
288### Blocking write client
289Blocking write client writes given point(s) synchronously. It doesn't have implicit batching. Batch is created from given set of points.
290
291```go
292package main
293
294import (
295 "context"
296 "fmt"
297 "math/rand"
298 "time"
299
300 "github.com/influxdata/influxdb-client-go/v2"
301)
302
303func main() {
304 // Create a new client using an InfluxDB server base URL and an authentication token
305 client := influxdb2.NewClient("http://localhost:8086", "my-token")
306 // Get blocking write client
307 writeAPI := client.WriteAPIBlocking("my-org","my-bucket")
308 // write some points
309 for i := 0; i <100; i++ {
310 // create data point
311 p := influxdb2.NewPoint(
312 "system",
313 map[string]string{
314 "id": fmt.Sprintf("rack_%v", i%10),
315 "vendor": "AWS",
316 "hostname": fmt.Sprintf("host_%v", i%100),
317 },
318 map[string]interface{}{
319 "temperature": rand.Float64() * 80.0,
320 "disk_free": rand.Float64() * 1000.0,
321 "disk_total": (i/10 + 1) * 1000000,
322 "mem_total": (i/100 + 1) * 10000000,
323 "mem_free": rand.Uint64(),
324 },
325 time.Now())
326 // write synchronously
327 err := writeAPI.WritePoint(context.Background(), p)
328 if err != nil {
329 panic(err)
330 }
331 }
332 // Ensures background processes finishes
333 client.Close()
334}
335```
336
337### Queries
338Query client offers retrieving of query results to a parsed representation in a [QueryTableResult](https://pkg.go.dev/github.com/influxdata/influxdb-client-go/v2/api#QueryTableResult) or to a raw string.
339
340### QueryTableResult
341QueryTableResult offers comfortable way how to deal with flux query CSV response. It parses CSV stream into FluxTableMetaData, FluxColumn and FluxRecord objects
342for easy reading the result.
343
344```go
345package main
346
347import (
348 "context"
349 "fmt"
350
351 "github.com/influxdata/influxdb-client-go/v2"
352)
353
354func main() {
355 // Create a new client using an InfluxDB server base URL and an authentication token
356 client := influxdb2.NewClient("http://localhost:8086", "my-token")
357 // Get query client
358 queryAPI := client.QueryAPI("my-org")
359 // get QueryTableResult
360 result, err := queryAPI.Query(context.Background(), `from(bucket:"my-bucket")|> range(start: -1h) |> filter(fn: (r) => r._measurement == "stat")`)
361 if err == nil {
362 // Iterate over query response
363 for result.Next() {
364 // Notice when group key has changed
365 if result.TableChanged() {
366 fmt.Printf("table: %s\n", result.TableMetadata().String())
367 }
368 // Access data
369 fmt.Printf("value: %v\n", result.Record().Value())
370 }
371 // check for an error
372 if result.Err() != nil {
373 fmt.Printf("query parsing error: %s\n", result.Err().Error())
374 }
375 } else {
376 panic(err)
377 }
378 // Ensures background processes finishes
379 client.Close()
380}
381```
382
383### Raw
384[QueryRaw()](https://pkg.go.dev/github.com/influxdata/influxdb-client-go/v2/api#QueryAPI.QueryRaw) returns raw, unparsed, query result string and process it on your own. Returned csv format
385can be controlled by the third parameter, query dialect.
386
387```go
388package main
389
390import (
391 "context"
392 "fmt"
393
394 "github.com/influxdata/influxdb-client-go/v2"
395)
396
397func main() {
398 // Create a new client using an InfluxDB server base URL and an authentication token
399 client := influxdb2.NewClient("http://localhost:8086", "my-token")
400 // Get query client
401 queryAPI := client.QueryAPI("my-org")
402 // Query and get complete result as a string
403 // Use default dialect
404 result, err := queryAPI.QueryRaw(context.Background(), `from(bucket:"my-bucket")|> range(start: -1h) |> filter(fn: (r) => r._measurement == "stat")`, influxdb2.DefaultDialect())
405 if err == nil {
406 fmt.Println("QueryResult:")
407 fmt.Println(result)
408 } else {
409 panic(err)
410 }
411 // Ensures background processes finishes
412 client.Close()
413}
414```
415### Concurrency
416InfluxDB Go Client can be used in a concurrent environment. All its functions are thread-safe.
417
418The best practise is to use a single `Client` instance per server URL. This ensures optimized resources usage,
419most importantly reusing HTTP connections.
420
421For efficient reuse of HTTP resources among multiple clients, create an HTTP client and use `Options.SetHTTPClient()` for setting it to all clients:
422```go
423 // Create HTTP client
424 httpClient := &http.Client{
425 Timeout: time.Second * time.Duration(60),
426 Transport: &http.Transport{
427 DialContext: (&net.Dialer{
428 Timeout: 5 * time.Second,
429 }).DialContext,
430 TLSHandshakeTimeout: 5 * time.Second,
431 TLSClientConfig: &tls.Config{
432 InsecureSkipVerify: true,
433 },
434 MaxIdleConns: 100,
435 MaxIdleConnsPerHost: 100,
436 IdleConnTimeout: 90 * time.Second,
437 },
438 }
439 // Client for server 1
440 client1 := influxdb2.NewClientWithOptions("https://server:8086", "my-token", influxdb2.DefaultOptions().SetHTTPClient(httpClient))
441 // Client for server 2
442 client2 := influxdb2.NewClientWithOptions("https://server:9999", "my-token2", influxdb2.DefaultOptions().SetHTTPClient(httpClient))
443```
444
445Client ensures that there is a single instance of each server API sub-client for the specific area. E.g. a single `WriteAPI` instance for each org/bucket pair,
446a single `QueryAPI` for each org.
447
448Such a single API sub-client instance can be used concurrently:
449```go
450package main
451
452import (
453 "math/rand"
454 "sync"
455 "time"
456
457 influxdb2 "github.com/influxdata/influxdb-client-go"
458 "github.com/influxdata/influxdb-client-go/v2/api/write"
459)
460
461func main() {
462 // Create client
463 client := influxdb2.NewClient("http://localhost:8086", "my-token")
464 // Ensure closing the client
465 defer client.Close()
466
467 // Get write client
468 writeApi := client.WriteAPI("my-org", "my-bucket")
469
470 // Create channel for points feeding
471 pointsCh := make(chan *write.Point, 200)
472
473 threads := 5
474
475 var wg sync.WaitGroup
476 go func(points int) {
477 for i := 0; i < points; i++ {
478 p := influxdb2.NewPoint("meas",
479 map[string]string{"tag": "tagvalue"},
480 map[string]interface{}{"val1": rand.Int63n(1000), "val2": rand.Float64()*100.0 - 50.0},
481 time.Now())
482 pointsCh <- p
483 }
484 close(pointsCh)
485 }(1000000)
486
487 // Launch write routines
488 for t := 0; t < threads; t++ {
489 wg.Add(1)
490 go func() {
491 for p := range pointsCh {
492 writeApi.WritePoint(p)
493 }
494 wg.Done()
495 }()
496 }
497 // Wait for writes complete
498 wg.Wait()
499}
500```
501
502### Proxy and redirects
503You can configure InfluxDB Go client behind a proxy in two ways:
504 1. Using environment variable
505 Set environment variable `HTTP_PROXY` (or `HTTPS_PROXY` based on the scheme of your server url).
506 e.g. (linux) `export HTTP_PROXY=http://my-proxy:8080` or in Go code `os.Setenv("HTTP_PROXY","http://my-proxy:8080")`
507
508 1. Configure `http.Client` to use proxy<br>
509 Create a custom `http.Client` with a proxy configuration:
510 ```go
511 proxyUrl, err := url.Parse("http://my-proxy:8080")
512 httpClient := &http.Client{
513 Transport: &http.Transport{
514 Proxy: http.ProxyURL(proxyUrl)
515 }
516 }
517 client := influxdb2.NewClientWithOptions("http://localhost:8086", token, influxdb2.DefaultOptions().SetHTTPClient(httpClient))
518 ```
519
520 Client automatically follows HTTP redirects. The default redirect policy is to follow up to 10 consecutive requests.
521 Due to a security reason _Authorization_ header is not forwarded when redirect leads to a different domain.
522 To overcome this limitation you have to set a custom redirect handler:
523```go
524token := "my-token"
525
526httpClient := &http.Client{
527 CheckRedirect: func(req *http.Request, via []*http.Request) error {
528 req.Header.Add("Authorization","Token " + token)
529 return nil
530 },
531}
532client := influxdb2.NewClientWithOptions("http://localhost:8086", token, influxdb2.DefaultOptions().SetHTTPClient(httpClient))
533```
534
535### Checking Server State
536There are three functions for checking whether a server is up and ready for communication:
537
538| Function| Description | Availability |
539|:----------|:----------|:----------|
540| [Health()](https://pkg.go.dev/github.com/influxdata/influxdb-client-go/v2#Client.Health) | Detailed info about the server status, along with version string | OSS |
541| [Ready()](https://pkg.go.dev/github.com/influxdata/influxdb-client-go/v2#Client.Ready) | Server uptime info | OSS |
542| [Ping()](https://pkg.go.dev/github.com/influxdata/influxdb-client-go/v2#Client.Ping) | Whether a server is up | OSS, Cloud |
543
544Only the [Ping()](https://pkg.go.dev/github.com/influxdata/influxdb-client-go/v2#Client.Ping) function works in InfluxDB Cloud server.
545
546## InfluxDB 1.8 API compatibility
547
548 [InfluxDB 1.8.0 introduced forward compatibility APIs](https://docs.influxdata.com/influxdb/latest/tools/api/#influxdb-2-0-api-compatibility-endpoints) for InfluxDB 2.0. This allow you to easily move from InfluxDB 1.x to InfluxDB 2.0 Cloud or open source.
549
550 Client API usage differences summary:
551 1. Use the form `username:password` for an **authentication token**. Example: `my-user:my-password`. Use an empty string (`""`) if the server doesn't require authentication.
552 1. The organization parameter is not used. Use an empty string (`""`) where necessary.
553 1. Use the form `database/retention-policy` where a **bucket** is required. Skip retention policy if the default retention policy should be used. Examples: `telegraf/autogen`, `telegraf`.
554
555 The following forward compatible APIs are available:
556
557 | API | Endpoint | Description |
558 |:----------|:----------|:----------|
559 | [WriteAPI](https://pkg.go.dev/github.com/influxdata/influxdb-client-go/v2/api#WriteAPI) (also [WriteAPIBlocking](https://pkg.go.dev/github.com/influxdata/influxdb-client-go/v2/api#WriteAPIBlocking))| [/api/v2/write](https://docs.influxdata.com/influxdb/v2.0/write-data/developer-tools/api/) | Write data to InfluxDB 1.8.0+ using the InfluxDB 2.0 API |
560 | [QueryAPI](https://pkg.go.dev/github.com/influxdata/influxdb-client-go/v2/api#QueryAPI) | [/api/v2/query](https://docs.influxdata.com/influxdb/v2.0/query-data/execute-queries/influx-api/) | Query data in InfluxDB 1.8.0+ using the InfluxDB 2.0 API and [Flux](https://docs.influxdata.com/flux/latest/) endpoint should be enabled by the [`flux-enabled` option](https://docs.influxdata.com/influxdb/v1.8/administration/config/#flux-enabled-false)
561 | [Health()](https://pkg.go.dev/github.com/influxdata/influxdb-client-go/v2#Client.Health) | [/health](https://docs.influxdata.com/influxdb/v2.0/api/#tag/Health) | Check the health of your InfluxDB instance |
562
563
564### Example
565```go
566package main
567
568import (
569 "context"
570 "fmt"
571 "time"
572
573 "github.com/influxdata/influxdb-client-go/v2"
574)
575
576func main() {
577 userName := "my-user"
578 password := "my-password"
579 // Create a new client using an InfluxDB server base URL and an authentication token
580 // For authentication token supply a string in the form: "username:password" as a token. Set empty value for an unauthenticated server
581 client := influxdb2.NewClient("http://localhost:8086", fmt.Sprintf("%s:%s",userName, password))
582 // Get the blocking write client
583 // Supply a string in the form database/retention-policy as a bucket. Skip retention policy for the default one, use just a database name (without the slash character)
584 // Org name is not used
585 writeAPI := client.WriteAPIBlocking("", "test/autogen")
586 // create point using full params constructor
587 p := influxdb2.NewPoint("stat",
588 map[string]string{"unit": "temperature"},
589 map[string]interface{}{"avg": 24.5, "max": 45},
590 time.Now())
591 // Write data
592 err := writeAPI.WritePoint(context.Background(), p)
593 if err != nil {
594 fmt.Printf("Write error: %s\n", err.Error())
595 }
596
597 // Get query client. Org name is not used
598 queryAPI := client.QueryAPI("")
599 // Supply string in a form database/retention-policy as a bucket. Skip retention policy for the default one, use just a database name (without the slash character)
600 result, err := queryAPI.Query(context.Background(), `from(bucket:"test")|> range(start: -1h) |> filter(fn: (r) => r._measurement == "stat")`)
601 if err == nil {
602 for result.Next() {
603 if result.TableChanged() {
604 fmt.Printf("table: %s\n", result.TableMetadata().String())
605 }
606 fmt.Printf("row: %s\n", result.Record().String())
607 }
608 if result.Err() != nil {
609 fmt.Printf("Query error: %s\n", result.Err().Error())
610 }
611 } else {
612 fmt.Printf("Query error: %s\n", err.Error())
613 }
614 // Close client
615 client.Close()
616}
617```
618
619## Contributing
620
621If you would like to contribute code you can do through GitHub by forking the repository and sending a pull request into the `master` branch.
622
623## License
624
625The InfluxDB 2 Go Client is released under the [MIT License](https://opensource.org/licenses/MIT).
626